From 2f8b2840073b38ccd8e21a3fbaad73225ad92e8b Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: [PATCH 0001/1408] Initial Contribution --- .../android/bluetooth/AtCommandHandler.java | 93 +++ .../android/bluetooth/AtCommandResult.java | 117 +++ .../java/android/bluetooth/AtParser.java | 370 ++++++++++ .../bluetooth/BluetoothAudioGateway.java | 190 +++++ .../android/bluetooth/BluetoothDevice.java | 601 ++++++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 198 +++++ .../android/bluetooth/BluetoothIntent.java | 118 +++ .../java/android/bluetooth/Database.java | 200 ++++++ .../java/android/bluetooth/DeviceClass.java | 131 ++++ .../java/android/bluetooth/HeadsetBase.java | 310 ++++++++ .../android/bluetooth/IBluetoothDevice.aidl | 85 +++ .../bluetooth/IBluetoothDeviceCallback.aidl | 28 + .../android/bluetooth/IBluetoothHeadset.aidl | 42 ++ .../bluetooth/IBluetoothHeadsetCallback.aidl | 25 + .../java/android/bluetooth/RfcommSocket.java | 674 ++++++++++++++++++ .../java/android/bluetooth/ScoSocket.java | 201 ++++++ framework/java/android/bluetooth/package.html | 14 + 17 files changed, 3397 insertions(+) create mode 100644 framework/java/android/bluetooth/AtCommandHandler.java create mode 100644 framework/java/android/bluetooth/AtCommandResult.java create mode 100644 framework/java/android/bluetooth/AtParser.java create mode 100644 framework/java/android/bluetooth/BluetoothAudioGateway.java create mode 100644 framework/java/android/bluetooth/BluetoothDevice.java create mode 100644 framework/java/android/bluetooth/BluetoothHeadset.java create mode 100644 framework/java/android/bluetooth/BluetoothIntent.java create mode 100644 framework/java/android/bluetooth/Database.java create mode 100644 framework/java/android/bluetooth/DeviceClass.java create mode 100644 framework/java/android/bluetooth/HeadsetBase.java create mode 100644 framework/java/android/bluetooth/IBluetoothDevice.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothHeadset.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl create mode 100644 framework/java/android/bluetooth/RfcommSocket.java create mode 100644 framework/java/android/bluetooth/ScoSocket.java create mode 100644 framework/java/android/bluetooth/package.html diff --git a/framework/java/android/bluetooth/AtCommandHandler.java b/framework/java/android/bluetooth/AtCommandHandler.java new file mode 100644 index 00000000000..8de2133e494 --- /dev/null +++ b/framework/java/android/bluetooth/AtCommandHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 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.bluetooth.AtCommandResult; + +/** + * Handler Interface for {@link AtParser}.

+ * @hide + */ +public abstract class AtCommandHandler { + + /** + * Handle Basic commands "ATA".

+ * These are single letter commands such as ATA and ATD. Anything following + * the single letter command ('A' and 'D' respectively) will be passed as + * 'arg'.

+ * For example, "ATDT1234" would result in the call + * handleBasicCommand("T1234").

+ * @param arg Everything following the basic command character. + * @return The result of this command. + */ + public AtCommandResult handleBasicCommand(String arg) { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Actions command "AT+FOO".

+ * Action commands are part of the Extended command syntax, and are + * typically used to signal an action on "FOO".

+ * @return The result of this command. + */ + public AtCommandResult handleActionCommand() { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Read command "AT+FOO?".

+ * Read commands are part of the Extended command syntax, and are + * typically used to read the value of "FOO".

+ * @return The result of this command. + */ + public AtCommandResult handleReadCommand() { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Set command "AT+FOO=...".

+ * Set commands are part of the Extended command syntax, and are + * typically used to set the value of "FOO". Multiple arguments can be + * sent.

+ * AT+FOO=[[,[,...]]]

+ * Each argument will be either numeric (Integer) or String. + * handleSetCommand is passed a generic Object[] array in which each + * element will be an Integer (if it can be parsed with parseInt()) or + * String.

+ * Missing arguments ",," are set to empty Strings.

+ * @param args Array of String and/or Integer's. There will always be at + * least one element in this array. + * @return The result of this command. + */ + // Typically used to set this paramter + public AtCommandResult handleSetCommand(Object[] args) { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Test command "AT+FOO=?".

+ * Test commands are part of the Extended command syntax, and are typically + * used to request an indication of the range of legal values that "FOO" + * can take.

+ * By defualt we return an OK result, to indicate that this command is at + * least recognized.

+ * @return The result of this command. + */ + public AtCommandResult handleTestCommand() { + return new AtCommandResult(AtCommandResult.OK); + } +} diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java new file mode 100644 index 00000000000..638be2d2e99 --- /dev/null +++ b/framework/java/android/bluetooth/AtCommandResult.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 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 java.util.*; + +/** + * The result of execution of an single AT command.

+ * + * + * This class can represent the final response to an AT command line, and also + * intermediate responses to a single command within a chained AT command + * line.

+ * + * The actual responses that are intended to be send in reply to the AT command + * line are stored in a string array. The final response is stored as an + * int enum, converted to a string when toString() is called. Only a single + * final response is sent from multiple commands chained into a single command + * line.

+ * @hide + */ +public class AtCommandResult { + // Result code enumerations + public static final int OK = 0; + public static final int ERROR = 1; + public static final int UNSOLICITED = 2; + + private static final String OK_STRING = "OK"; + private static final String ERROR_STRING = "ERROR"; + + private int mResultCode; // Result code + private StringBuilder mResponse; // Response with CRLF line breaks + + /** + * Construct a new AtCommandResult with given result code, and an empty + * response array. + * @param resultCode One of OK, ERROR or UNSOLICITED. + */ + public AtCommandResult(int resultCode) { + mResultCode = resultCode; + mResponse = new StringBuilder(); + } + + /** + * Construct a new AtCommandResult with result code OK, and the specified + * single line response. + * @param response The single line response. + */ + public AtCommandResult(String response) { + this(OK); + addResponse(response); + } + + public int getResultCode() { + return mResultCode; + } + + /** + * Add another line to the response. + */ + public void addResponse(String response) { + appendWithCrlf(mResponse, response); + } + + /** + * Add the given result into this AtCommandResult object.

+ * Used to combine results from multiple commands in a single command line + * (command chaining). + * @param result The AtCommandResult to add to this result. + */ + public void addResult(AtCommandResult result) { + if (result != null) { + appendWithCrlf(mResponse, result.mResponse.toString()); + mResultCode = result.mResultCode; + } + } + + /** + * Generate the string response ready to send + */ + public String toString() { + StringBuilder result = new StringBuilder(mResponse.toString()); + switch (mResultCode) { + case OK: + appendWithCrlf(result, OK_STRING); + break; + case ERROR: + appendWithCrlf(result, ERROR_STRING); + break; + } + return result.toString(); + } + + /** Append a string to a string builder, joining with a double + * CRLF. Used to create multi-line AT command replies + */ + public static void appendWithCrlf(StringBuilder str1, String str2) { + if (str1.length() > 0 && str2.length() > 0) { + str1.append("\r\n\r\n"); + } + str1.append(str2); + } +}; diff --git a/framework/java/android/bluetooth/AtParser.java b/framework/java/android/bluetooth/AtParser.java new file mode 100644 index 00000000000..1ea31503bb7 --- /dev/null +++ b/framework/java/android/bluetooth/AtParser.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2007 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.bluetooth.AtCommandHandler; +import android.bluetooth.AtCommandResult; + +import java.util.*; + +/** + * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard. + *

+ * + * Conforment with the subset of V.250 required for implementation of the + * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP + * specifications. Also implements some V.250 features not required by + * Bluetooth - such as chained commands.

+ * + * Command handlers are registered with an AtParser object. These handlers are + * invoked when command lines are processed by AtParser's process() method.

+ * + * The AtParser object accepts a new command line to parse via its process() + * method. It breaks each command line into one or more commands. Each command + * is parsed for name, type, and (optional) arguments, and an appropriate + * external handler method is called through the AtCommandHandler interface. + * + * The command types are

+ * + * In V.250 the last four command types are known as Extended Commands, and + * they are used heavily in Bluetooth.

+ * + * Basic commands cannot be chained in this implementation. For Bluetooth + * headset/handsfree use this is acceptable, because they only use the basic + * commands ATA and ATD, which are not allowed to be chained. For general V.250 + * use we would need to improve this class to allow Basic command chaining - + * however its tricky to get right becuase there is no deliminator for Basic + * command chaining.

+ * + * Extended commands can be chained. For example:

+ * AT+VGM?;+VGM=14;+CIMI

+ * This is equivalent to:

+ * AT+VGM? + * AT+VGM=14 + * AT+CIMI + * Except that only one final result code is return (although several + * intermediate responses may be returned), and as soon as one command in the + * chain fails the rest are abandonded.

+ * + * Handlers are registered by there command name via register(Char c, ...) or + * register(String s, ...). Handlers for Basic command should be registered by + * the basic command character, and handlers for Extended commands should be + * registered by String.

+ * + * Refer to:

+ * @hide + */ +public class AtParser { + + // Extended command type enumeration, only used internally + private static final int TYPE_ACTION = 0; // AT+FOO + private static final int TYPE_READ = 1; // AT+FOO? + private static final int TYPE_SET = 2; // AT+FOO= + private static final int TYPE_TEST = 3; // AT+FOO=? + + private HashMap mExtHandlers; + private HashMap mBasicHandlers; + + private String mLastInput; // for "A/" (repeat last command) support + + /** + * Create a new AtParser.

+ * No handlers are registered. + */ + public AtParser() { + mBasicHandlers = new HashMap(); + mExtHandlers = new HashMap(); + mLastInput = ""; + } + + /** + * Register a Basic command handler.

+ * Basic command handlers are later called via their + * handleBasicCommand(String args) method. + * @param command Command name - a single character + * @param handler Handler to register + */ + public void register(Character command, AtCommandHandler handler) { + mBasicHandlers.put(command, handler); + } + + /** + * Register an Extended command handler.

+ * Extended command handlers are later called via:

    + *
  • handleActionCommand() + *
  • handleGetCommand() + *
  • handleSetCommand() + *
  • handleTestCommand() + *
+ * Only one method will be called for each command processed. + * @param command Command name - can be multiple characters + * @param handler Handler to register + */ + public void register(String command, AtCommandHandler handler) { + mExtHandlers.put(command, handler); + } + + + /** + * Strip input of whitespace and force Uppercase - except sections inside + * quotes. Also fixes unmatched quotes (by appending a quote). Double + * quotes " are the only quotes allowed by V.250 + */ + static private String clean(String input) { + StringBuilder out = new StringBuilder(input.length()); + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '"') { + int j = input.indexOf('"', i + 1 ); // search for closing " + if (j == -1) { // unmatched ", insert one. + out.append(input.substring(i, input.length())); + out.append('"'); + break; + } + out.append(input.substring(i, j + 1)); + i = j; + } else if (c != ' ') { + out.append(Character.toUpperCase(c)); + } + } + + return out.toString(); + } + + static private boolean isAtoZ(char c) { + return (c >= 'A' && c <= 'Z'); + } + + /** + * Find a character ch, ignoring quoted sections. + * Return input.length() if not found. + */ + static private int findChar(char ch, String input, int fromIndex) { + for (int i = fromIndex; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '"') { + i = input.indexOf('"', i + 1); + if (i == -1) { + return input.length(); + } + } else if (c == ch) { + return i; + } + } + return input.length(); + } + + /** + * Break an argument string into individual arguments (comma deliminated). + * Integer arguments are turned into Integer objects. Otherwise a String + * object is used. + */ + static private Object[] generateArgs(String input) { + int i = 0; + int j; + ArrayList out = new ArrayList(); + while (i <= input.length()) { + j = findChar(',', input, i); + + String arg = input.substring(i, j); + try { + out.add(new Integer(arg)); + } catch (NumberFormatException e) { + out.add(arg); + } + + i = j + 1; // move past comma + } + return out.toArray(); + } + + /** + * Return the index of the end of character after the last characeter in + * the extended command name. Uses the V.250 spec for allowed command + * names. + */ + static private int findEndExtendedName(String input, int index) { + for (int i = index; i < input.length(); i++) { + char c = input.charAt(i); + + // V.250 defines the following chars as legal extended command + // names + if (isAtoZ(c)) continue; + if (c >= '0' && c <= '9') continue; + switch (c) { + case '!': + case '%': + case '-': + case '.': + case '/': + case ':': + case '_': + continue; + default: + return i; + } + } + return input.length(); + } + + /** + * Processes an incoming AT command line.

+ * This method will invoke zero or one command handler methods for each + * command in the command line.

+ * @param raw_input The AT input, without EOL deliminator (e.g. ). + * @return Result object for this command line. This can be + * converted to a String[] response with toStrings(). + */ + public AtCommandResult process(String raw_input) { + String input = clean(raw_input); + + // Handle "A/" (repeat previous line) + if (input.regionMatches(0, "A/", 0, 2)) { + input = new String(mLastInput); + } else { + mLastInput = new String(input); + } + + // Handle empty line - no response necessary + if (input.equals("")) { + // Return [] + return new AtCommandResult(AtCommandResult.UNSOLICITED); + } + + // Anything else deserves an error + if (!input.regionMatches(0, "AT", 0, 2)) { + // Return ["ERROR"] + return new AtCommandResult(AtCommandResult.ERROR); + } + + // Ok we have a command that starts with AT. Process it + int index = 2; + AtCommandResult result = + new AtCommandResult(AtCommandResult.UNSOLICITED); + while (index < input.length()) { + char c = input.charAt(index); + + if (isAtoZ(c)) { + // Option 1: Basic Command + // Pass the rest of the line as is to the handler. Do not + // look for any more commands on this line. + String args = input.substring(index + 1); + if (mBasicHandlers.containsKey((Character)c)) { + result.addResult(mBasicHandlers.get( + (Character)c).handleBasicCommand(args)); + return result; + } else { + // no handler + result.addResult( + new AtCommandResult(AtCommandResult.ERROR)); + return result; + } + // control never reaches here + } + + if (c == '+') { + // Option 2: Extended Command + // Search for first non-name character. Shortcircuit if we dont + // handle this command name. + int i = findEndExtendedName(input, index + 1); + String commandName = input.substring(index, i); + if (!mExtHandlers.containsKey(commandName)) { + // no handler + result.addResult( + new AtCommandResult(AtCommandResult.ERROR)); + return result; + } + AtCommandHandler handler = mExtHandlers.get(commandName); + + // Search for end of this command - this is usually the end of + // line + int endIndex = findChar(';', input, index); + + // Determine what type of command this is. + // Default to TYPE_ACTION if we can't find anything else + // obvious. + int type; + + if (i >= endIndex) { + type = TYPE_ACTION; + } else if (input.charAt(i) == '?') { + type = TYPE_READ; + } else if (input.charAt(i) == '=') { + if (i + 1 < endIndex) { + if (input.charAt(i + 1) == '?') { + type = TYPE_TEST; + } else { + type = TYPE_SET; + } + } else { + type = TYPE_SET; + } + } else { + type = TYPE_ACTION; + } + + // Call this command. Short-circuit as soon as a command fails + switch (type) { + case TYPE_ACTION: + result.addResult(handler.handleActionCommand()); + break; + case TYPE_READ: + result.addResult(handler.handleReadCommand()); + break; + case TYPE_TEST: + result.addResult(handler.handleTestCommand()); + break; + case TYPE_SET: + Object[] args = + generateArgs(input.substring(i + 1, endIndex)); + result.addResult(handler.handleSetCommand(args)); + break; + } + if (result.getResultCode() != AtCommandResult.OK) { + return result; // short-circuit + } + + index = endIndex; + } else { + // Can't tell if this is a basic or extended command. + // Push forwards and hope we hit something. + index++; + } + } + // Finished processing (and all results were ok) + return result; + } +} diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java new file mode 100644 index 00000000000..f3afd2a4a0b --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -0,0 +1,190 @@ +package android.bluetooth; + +import java.lang.Thread; + +import android.os.Message; +import android.os.Handler; +import android.util.Log; + +/** + * Listen's for incoming RFCOMM connection for the headset / handsfree service. + * + * This class is planned for deletion, in favor of a generic Rfcomm class. + * + * @hide + */ +public class BluetoothAudioGateway { + private static final String TAG = "BT Audio Gateway"; + private static final boolean DBG = false; + + private int mNativeData; + static { classInitNative(); } + + private BluetoothDevice mBluetooth; + + /* in */ + private int mHandsfreeAgRfcommChannel = -1; + private int mHeadsetAgRfcommChannel = -1; + + /* out */ + private String mConnectingHeadsetAddress; + private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ + private int mConnectingHeadsetSocketFd; + private String mConnectingHandsfreeAddress; + private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ + private int mConnectingHandsfreeSocketFd; + private int mTimeoutRemainingMs; /* in/out */ + + public static final int DEFAULT_HF_AG_CHANNEL = 10; + public static final int DEFAULT_HS_AG_CHANNEL = 11; + + public BluetoothAudioGateway(BluetoothDevice bluetooth) { + this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); + } + + public BluetoothAudioGateway(BluetoothDevice bluetooth, + int handsfreeAgRfcommChannel, + int headsetAgRfcommChannel) { + mBluetooth = bluetooth; + mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; + mHeadsetAgRfcommChannel = headsetAgRfcommChannel; + initializeNativeDataNative(); + } + + private Thread mConnectThead; + private volatile boolean mInterrupted; + private static final int SELECT_WAIT_TIMEOUT = 1000; + + private Handler mCallback; + + public class IncomingConnectionInfo { + IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd, + int rfcommChan) { + mBluetooth = bluetooth; + mAddress = address; + mSocketFd = socketFd; + mRfcommChan = rfcommChan; + } + + public BluetoothDevice mBluetooth; + public String mAddress; + public int mSocketFd; + public int mRfcommChan; + } + + public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; + public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101; + + public synchronized boolean start(Handler callback) { + + if (mConnectThead == null) { + mCallback = callback; + mConnectThead = new Thread(TAG) { + public void run() { + if (DBG) log("Connect Thread starting"); + while (!mInterrupted) { + //Log.i(TAG, "waiting for connect"); + mConnectingHeadsetRfcommChannel = -1; + mConnectingHandsfreeRfcommChannel = -1; + if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) { + if (mTimeoutRemainingMs > 0) { + try { + Log.i(TAG, "select thread timed out, but " + + mTimeoutRemainingMs + "ms of waiting remain."); + Thread.sleep(mTimeoutRemainingMs); + } catch (InterruptedException e) { + Log.i(TAG, "select thread was interrupted (2), exiting"); + mInterrupted = true; + } + } + } + else { + Log.i(TAG, "connect notification!"); + /* A device connected (most likely just one, but + it is possible for two separate devices, one + a headset and one a handsfree, to connect + simultaneously. + */ + if (mConnectingHeadsetRfcommChannel >= 0) { + Log.i(TAG, "Incoming connection from headset " + + mConnectingHeadsetAddress + " on channel " + + mConnectingHeadsetRfcommChannel); + Message msg = Message.obtain(mCallback); + msg.what = MSG_INCOMING_HEADSET_CONNECTION; + msg.obj = + new IncomingConnectionInfo( + mBluetooth, + mConnectingHeadsetAddress, + mConnectingHeadsetSocketFd, + mConnectingHeadsetRfcommChannel); + msg.sendToTarget(); + } + if (mConnectingHandsfreeRfcommChannel >= 0) { + Log.i(TAG, "Incoming connection from handsfree " + + mConnectingHandsfreeAddress + " on channel " + + mConnectingHandsfreeRfcommChannel); + Message msg = Message.obtain(); + msg.setTarget(mCallback); + msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; + msg.obj = + new IncomingConnectionInfo( + mBluetooth, + mConnectingHandsfreeAddress, + mConnectingHandsfreeSocketFd, + mConnectingHandsfreeRfcommChannel); + msg.sendToTarget(); + } + } + } + if (DBG) log("Connect Thread finished"); + } + }; + + if (setUpListeningSocketsNative() == false) { + Log.e(TAG, "Could not set up listening socket, exiting"); + return false; + } + + mInterrupted = false; + mConnectThead.start(); + } + + return true; + } + + public synchronized void stop() { + if (mConnectThead != null) { + if (DBG) log("stopping Connect Thread"); + mInterrupted = true; + try { + mConnectThead.interrupt(); + if (DBG) log("waiting for thread to terminate"); + mConnectThead.join(); + mConnectThead = null; + mCallback = null; + tearDownListeningSocketsNative(); + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted waiting for Connect Thread to join"); + } + } + } + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + + private static native void classInitNative(); + private native void initializeNativeDataNative(); + private native void cleanupNativeDataNative(); + private native boolean waitForHandsfreeConnectNative(int timeoutMs); + private native boolean setUpListeningSocketsNative(); + private native void tearDownListeningSocketsNative(); + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java new file mode 100644 index 00000000000..0b24db662ca --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2008 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.os.RemoteException; +import android.util.Log; + +import java.io.UnsupportedEncodingException; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Manages the local Bluetooth device. Scan for devices, create bondings, + * power up and down the adapter. + * + * @hide + */ +public class BluetoothDevice { + public static final int MODE_UNKNOWN = -1; + public static final int MODE_OFF = 0; + public static final int MODE_CONNECTABLE = 1; + public static final int MODE_DISCOVERABLE = 2; + + public static final int RESULT_FAILURE = -1; + public static final int RESULT_SUCCESS = 0; + + private static final String TAG = "BluetoothDevice"; + + private final IBluetoothDevice mService; + /** + * @hide - hide this because it takes a parameter of type + * IBluetoothDevice, which is a System private class. + * Also note that Context.getSystemService is a factory that + * returns a BlueToothDevice. That is the right way to get + * a BluetoothDevice. + */ + public BluetoothDevice(IBluetoothDevice service) { + mService = service; + } + + /** + * Get the current status of Bluetooth hardware. + * + * @return true if Bluetooth enabled, false otherwise. + */ + public boolean isEnabled() { + try { + return mService.isEnabled(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Enable the Bluetooth device. + * Turn on the underlying hardware. + * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be + * sent if and when the device is successfully enabled. + * @return false if we cannot enable the Bluetooth device. True does not + * imply the device was enabled, it only implies that so far there were no + * problems. + */ + public boolean enable() { + return enable(null); + } + + /** + * Enable the Bluetooth device. + * Turns on the underlying hardware. + * This is an asynchronous call. onEnableResult() of your callback will be + * called when the call is complete, with either RESULT_SUCCESS or + * RESULT_FAILURE. + * + * Your callback will be called from a binder thread, not the main thread. + * + * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be + * broadcast if the device is successfully enabled. + * + * @param callback Your callback, null is ok. + * @return true if your callback was successfully registered, or false if + * there was an error, implying your callback will never be called. + */ + public boolean enable(IBluetoothDeviceCallback callback) { + try { + return mService.enable(callback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Disable the Bluetooth device. + * This turns off the underlying hardware. + * + * @return true if successful, false otherwise. + */ + public boolean disable() { + try { + return mService.disable(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getAddress() { + try { + return mService.getAddress(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. Currently it is only + * possible to retrieve the Bluetooth name when Bluetooth is enabled. + * + * @return the Bluetooth name, or null if there was a problem. + */ + public String getName() { + try { + return mService.getName(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Set the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. The Bluetooth Service + * is responsible for persisting this name. + * + * @param name the name to set + * @return true, if the name was successfully set. False otherwise. + */ + public boolean setName(String name) { + try { + return mService.setName(name); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getMajorClass() { + try { + return mService.getMajorClass(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getMinorClass() { + try { + return mService.getMinorClass(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getVersion() { + try { + return mService.getVersion(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRevision() { + try { + return mService.getRevision(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getManufacturer() { + try { + return mService.getManufacturer(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getCompany() { + try { + return mService.getCompany(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + public int getMode() { + try { + return mService.getMode(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return MODE_UNKNOWN; + } + public void setMode(int mode) { + try { + mService.setMode(mode); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public int getDiscoverableTimeout() { + try { + return mService.getDiscoverableTimeout(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return -1; + } + public void setDiscoverableTimeout(int timeout) { + try { + mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean startDiscovery() { + return startDiscovery(true); + } + public boolean startDiscovery(boolean resolveNames) { + try { + return mService.startDiscovery(resolveNames); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public void cancelDiscovery() { + try { + mService.cancelDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean isDiscovering() { + try { + return mService.isDiscovering(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean startPeriodicDiscovery() { + try { + return mService.startPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean stopPeriodicDiscovery() { + try { + return mService.stopPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean isPeriodicDiscovery() { + try { + return mService.isPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String[] listRemoteDevices() { + try { + return mService.listRemoteDevices(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * List remote devices that have a low level (ACL) connection. + * + * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have + * an ACL connection even when not paired - this is common for SDP queries + * or for in-progress pairing requests. + * + * In most cases you probably want to test if a higher level protocol is + * connected, rather than testing ACL connections. + * + * @return bluetooth hardware addresses of remote devices with a current + * ACL connection. Array size is 0 if no devices have a + * connection. Null on error. + */ + public String[] listAclConnections() { + try { + return mService.listAclConnections(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Check if a specified remote device has a low level (ACL) connection. + * + * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have + * an ACL connection even when not paired - this is common for SDP queries + * or for in-progress pairing requests. + * + * In most cases you probably want to test if a higher level protocol is + * connected, rather than testing ACL connections. + * + * @param address the Bluetooth hardware address you want to check. + * @return true if there is an ACL connection, false otherwise and on + * error. + */ + public boolean isAclConnected(String address) { + try { + return mService.isAclConnected(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Perform a low level (ACL) disconnection of a remote device. + * + * This forcably disconnects the ACL layer connection to a remote device, + * which will cause all RFCOMM, SDP and L2CAP connections to this remote + * device to close. + * + * @param address the Bluetooth hardware address you want to disconnect. + * @return true if the device was disconnected, false otherwise and on + * error. + */ + public boolean disconnectRemoteDeviceAcl(String address) { + try { + return mService.disconnectRemoteDeviceAcl(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Create a bonding with a remote bluetooth device. + * + * This is an asynchronous call. BluetoothIntent.BONDING_CREATED_ACTION + * will be broadcast if and when the remote device is successfully bonded. + * + * @param address the remote device Bluetooth address. + * @return false if we cannot create a bonding to that device, true if + * there were no problems beginning the bonding process. + */ + public boolean createBonding(String address) { + return createBonding(address, null); + } + + /** + * Create a bonding with a remote bluetooth device. + * + * This is an asynchronous call. onCreateBondingResult() of your callback + * will be called when the call is complete, with either RESULT_SUCCESS or + * RESULT_FAILURE. + * + * In addition to the callback, BluetoothIntent.BONDING_CREATED_ACTION will + * be broadcast if the remote device is successfully bonded. + * + * @param address The remote device Bluetooth address. + * @param callback Your callback, null is ok. + * @return true if your callback was successfully registered, or false if + * there was an error, implying your callback will never be called. + */ + public boolean createBonding(String address, IBluetoothDeviceCallback callback) { + try { + return mService.createBonding(address, callback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean cancelBondingProcess(String address) { + try { + return mService.cancelBondingProcess(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * List remote devices that are bonded (paired) to the local device. + * + * Bonding (pairing) is the process by which the user enters a pin code for + * the device, which generates a shared link key, allowing for + * authentication and encryption of future connections. In Android we + * require bonding before RFCOMM or SCO connections can be made to a remote + * device. + * + * This function lists which remote devices we have a link key for. It does + * not cause any RF transmission, and does not check if the remote device + * still has it's link key with us. If the other side no longer has its + * link key then the RFCOMM or SCO connection attempt will result in an + * error. + * + * This function does not check if the remote device is in range. + * + * @return bluetooth hardware addresses of remote devices that are + * bonded. Array size is 0 if no devices are bonded. Null on error. + */ + public String[] listBondings() { + try { + return mService.listBondings(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Check if a remote device is bonded (paired) to the local device. + * + * Bonding (pairing) is the process by which the user enters a pin code for + * the device, which generates a shared link key, allowing for + * authentication and encryption of future connections. In Android we + * require bonding before RFCOMM or SCO connections can be made to a remote + * device. + * + * This function checks if we have a link key with the remote device. It + * does not cause any RF transmission, and does not check if the remote + * device still has it's link key with us. If the other side no longer has + * a link key then the RFCOMM or SCO connection attempt will result in an + * error. + * + * This function does not check if the remote device is in range. + * + * @param address Bluetooth hardware address of the remote device to check. + * @return true if bonded, false otherwise and on error. + */ + public boolean hasBonding(String address) { + try { + return mService.hasBonding(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean removeBonding(String address) { + try { + return mService.removeBonding(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getRemoteName(String address) { + try { + return mService.getRemoteName(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + public String getRemoteAlias(String address) { + try { + return mService.getRemoteAlias(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public boolean setRemoteAlias(String address, String alias) { + try { + return mService.setRemoteAlias(address, alias); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean clearRemoteAlias(String address) { + try { + return mService.clearRemoteAlias(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public String getRemoteVersion(String address) { + try { + return mService.getRemoteVersion(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteRevision(String address) { + try { + return mService.getRemoteRevision(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteManufacturer(String address) { + try { + return mService.getRemoteManufacturer(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteCompany(String address) { + try { + return mService.getRemoteCompany(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteMajorClass(String address) { + try { + return mService.getRemoteMajorClass(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteMinorClass(String address) { + try { + return mService.getRemoteMinorClass(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String[] getRemoteServiceClasses(String address) { + try { + return mService.getRemoteServiceClasses(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Returns the RFCOMM channel associated with the 16-byte UUID on + * the remote Bluetooth address. + * + * Performs a SDP ServiceSearchAttributeRequest transaction. The provided + * uuid is verified in the returned record. If there was a problem, or the + * specified uuid does not exist, -1 is returned. + */ + public boolean getRemoteServiceChannel(String address, short uuid16, + IBluetoothDeviceCallback callback) { + try { + return mService.getRemoteServiceChannel(address, uuid16, callback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public int getRemoteClass(String address) { + try { + return mService.getRemoteClass(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return DeviceClass.CLASS_UNKNOWN; + } + public byte[] getRemoteFeatures(String address) { + try { + return mService.getRemoteFeatures(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String lastSeen(String address) { + try { + return mService.lastSeen(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String lastUsed(String address) { + try { + return mService.lastUsed(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + public boolean setPin(String address, byte[] pin) { + try { + return mService.setPin(address, pin); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean cancelPin(String address) { + try { + return mService.cancelPin(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Check that a pin is valid and convert to byte array. + * + * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. + * @param pin pin as java String + * @return the pin code as a UTF8 byte array, or null if it is an invalid + * Bluetooth pin. + */ + public static byte[] convertPinToBytes(String pin) { + if (pin == null) { + return null; + } + byte[] pinBytes; + try { + pinBytes = pin.getBytes("UTF8"); + } catch (UnsupportedEncodingException uee) { + Log.e(TAG, "UTF8 not supported?!?"); // this should not happen + return null; + } + if (pinBytes.length <= 0 || pinBytes.length > 16) { + return null; + } + return pinBytes; + } + + + /* Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ + private static final int ADDRESS_LENGTH = 17; + public static boolean checkBluetoothAddress(String address) { + if (address == null || address.length() != ADDRESS_LENGTH) { + return false; + } + for (int i = 0; i < ADDRESS_LENGTH; i++) { + char c = address.charAt(i); + switch (i % 3) { + case 0: + case 1: + if (Character.digit(c, 16) != -1) { + break; // hex character, OK + } + return false; + case 2: + if (c == ':') { + break; // OK + } + return false; + } + } + return true; + } +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java new file mode 100644 index 00000000000..90db39bc547 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Public API for controlling the Bluetooth Headset Service. + * + * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset + * Service. + * + * Creating a BluetoothHeadset object will create a binding with the + * BluetoothHeadset service. Users of this object should call close() when they + * are finished with the BluetoothHeadset, so that this proxy object can unbind + * from the service. + * + * BlueoothHeadset objects are not guarenteed to be connected to the + * BluetoothHeadsetService at all times. Calls on this object while not + * connected to the service will result in default error return values. Even + * after object construction, there is a short delay (~10ms) before this proxy + * object is actually connected to the Service. + * + * Android only supports one connected Bluetooth Headset at a time. + * + * Note that in this context, Headset includes both Bluetooth Headset's and + * Handsfree devices. + * + * @hide + */ +public class BluetoothHeadset { + + private final static String TAG = "BluetoothHeadset"; + + private final Context mContext; + private IBluetoothHeadset mService; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + /** No headset currently connected */ + public static final int STATE_DISCONNECTED = 0; + /** Connection attempt in progress */ + public static final int STATE_CONNECTING = 1; + /** A headset is currently connected */ + public static final int STATE_CONNECTED = 2; + + public static final int RESULT_FAILURE = 0; + public static final int RESULT_SUCCESS = 1; + /** Connection cancelled before completetion. */ + public static final int RESULT_CANCELLED = 2; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + mService = IBluetoothHeadset.Stub.asInterface(service); + Log.i(TAG, "Proxy object is now connected to Bluetooth Headset Service"); + } + public void onServiceDisconnected(ComponentName className) { + mService = null; + } + }; + + /** + * Create a BluetoothHeadset proxy object. + * Remeber to call close() when you are done with this object, so that it + * can unbind from the BluetoothHeadsetService. + */ + public BluetoothHeadset(Context context) { + mContext = context; + if (!context.bindService( + new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service"); + } + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothHeadset will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized void close() { + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } + } + + /** + * Get the current state of the Bluetooth Headset service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Headset service. + */ + public int getState() { + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } + return BluetoothHeadset.STATE_ERROR; + } + + /** + * Get the Bluetooth address of the current headset. + * @return The Bluetooth address, or null if not in connected or connecting + * state, or if this proxy object is not connected to the Headset + * service. + */ + public String getHeadsetAddress() { + if (mService != null) { + try { + return mService.getHeadsetAddress(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } + return null; + } + + /** + * Request to initiate a connection to a headset. + * This call does not block. Fails if a headset is already connecting + * or connected. + * Will connect to the last connected headset if address is null. + * @param address The Bluetooth Address to connect to, or null to connect + * to the last connected headset. + * @param callback A callback with onCreateBondingResult() defined, or + * null. + * @return False if there was a problem initiating the connection + * procedure, and your callback will not be used. True if + * the connection procedure was initiated, in which case + * your callback is guarenteed to be called. + */ + public boolean connectHeadset(String address, IBluetoothHeadsetCallback callback) { + if (mService != null) { + try { + return mService.connectHeadset(address, callback); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } + return false; + } + + /** + * Returns true if the specified headset is connected (does not include + * connecting). Returns false if not connected, or if this proxy object + * if not currently connected to the headset service. + */ + public boolean isConnected(String address) { + if (mService != null) { + try { + return mService.isConnected(address); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } + return false; + } + + /** + * Disconnects the current headset. Currently this call blocks, it may soon + * be made asynchornous. Returns false if this proxy object is + * not currently connected to the Headset service. + */ + public boolean disconnectHeadset() { + if (mService != null) { + try { + mService.disconnectHeadset(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } + return false; + } +} diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java new file mode 100644 index 00000000000..8e227918903 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Manages the local Bluetooth device. Scan for devices, create bondings, + * power up and down the adapter. + * + * @hide + */ +public interface BluetoothIntent { + public static final String MODE = + "android.bluetooth.intent.MODE"; + public static final String ADDRESS = + "android.bluetooth.intent.ADDRESS"; + public static final String NAME = + "android.bluetooth.intent.NAME"; + public static final String ALIAS = + "android.bluetooth.intent.ALIAS"; + public static final String RSSI = + "android.bluetooth.intent.RSSI"; + public static final String CLASS = + "android.bluetooth.intent.CLASS"; + public static final String HEADSET_STATE = + "android.bluetooth.intent.HEADSET_STATE"; + public static final String HEADSET_PREVIOUS_STATE = + "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ENABLED_ACTION = + "android.bluetooth.intent.action.ENABLED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISABLED_ACTION = + "android.bluetooth.intent.action.DISABLED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String NAME_CHANGED_ACTION = + "android.bluetooth.intent.action.NAME_CHANGED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String MODE_CHANGED_ACTION = + "android.bluetooth.intent.action.MODE_CHANGED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISCOVERY_STARTED_ACTION = + "android.bluetooth.intent.action.DISCOVERY_STARTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISCOVERY_COMPLETED_ACTION = + "android.bluetooth.intent.action.DISCOVERY_COMPLETED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String PAIRING_REQUEST_ACTION = + "android.bluetooth.intent.action.PAIRING_REQUEST"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String PAIRING_CANCEL_ACTION = + "android.bluetooth.intent.action.PAIRING_CANCEL"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_FOUND_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISAPPEARED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_CONNECTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISCONNECTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_NAME_UPDATED_ACTION = + "android.bluetooth.intent.action.REMOTE_NAME_UPDATED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_NAME_FAILED_ACTION = + "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_ALIAS_CHANGED_ACTION = + "android.bluetooth.intent.action.REMOTE_ALIAS_CHANGED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_ALIAS_CLEARED_ACTION = + "android.bluetooth.intent.action.REMOTE_ALIAS_CLEARED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String BONDING_CREATED_ACTION = + "android.bluetooth.intent.action.BONDING_CREATED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String BONDING_REMOVED_ACTION = + "android.bluetooth.intent.action.BONDING_REMOVED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String HEADSET_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; +} diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java new file mode 100644 index 00000000000..fef641a5f7c --- /dev/null +++ b/framework/java/android/bluetooth/Database.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2007 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.bluetooth.RfcommSocket; + +import android.util.Log; + +import java.io.*; +import java.util.*; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * A low-level API to the Service Discovery Protocol (SDP) Database. + * + * Allows service records to be added to the local SDP database. Once added, + * these services will be advertised to remote devices when they make SDP + * queries on this device. + * + * Currently this API is a thin wrapper to the bluez SDP Database API. See: + * http://wiki.bluez.org/wiki/Database + * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords + * @hide + */ +public final class Database { + private static Database mInstance; + + private static final String sLogName = "android.bluetooth.Database"; + + /** + * Class load time initialization + */ + static { + classInitNative(); + } + private native static void classInitNative(); + + /** + * Private to enforce singleton property + */ + private Database() { + initializeNativeDataNative(); + } + private native void initializeNativeDataNative(); + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + private native void cleanupNativeDataNative(); + + /** + * Singelton accessor + * @return The singleton instance of Database + */ + public static synchronized Database getInstance() { + if (mInstance == null) { + mInstance = new Database(); + } + return mInstance; + } + + /** + * Advertise a service with an RfcommSocket. + * + * This adds the service the SDP Database with the following attributes + * set: Service Name, Protocol Descriptor List, Service Class ID List + * TODO: Construct a byte[] record directly, rather than via XML. + * @param socket The rfcomm socket to advertise (by channel). + * @param serviceName A short name for this service + * @param uuid + * Unique identifier for this service, by which clients + * can search for your service + * @return Handle to the new service record + */ + public int advertiseRfcommService(RfcommSocket socket, + String serviceName, + UUID uuid) throws IOException { + String xmlRecord = + "\n" + + "\n" + + " \n" + // ServiceClassIDList + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // ProtocolDescriptorList + " \n" + + " \n" + + " \n" + // L2CAP + " \n" + + " \n" + + " \n" + // RFCOMM + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // ServiceName + " \n" + + " \n" + + "\n"; + Log.i(sLogName, xmlRecord); + return addServiceRecordFromXml(xmlRecord); + } + + + /** + * Add a new service record. + * @param record The byte[] record + * @return A handle to the new record + */ + public synchronized int addServiceRecord(byte[] record) throws IOException { + int handle = addServiceRecordNative(record); + Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); + return handle; + } + private native int addServiceRecordNative(byte[] record) + throws IOException; + + /** + * Add a new service record, using XML. + * @param record The record as an XML string + * @return A handle to the new record + */ + public synchronized int addServiceRecordFromXml(String record) throws IOException { + int handle = addServiceRecordFromXmlNative(record); + Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); + return handle; + } + private native int addServiceRecordFromXmlNative(String record) + throws IOException; + + /** + * Update an exisiting service record. + * @param handle Handle to exisiting record + * @param record The updated byte[] record + */ + public synchronized void updateServiceRecord(int handle, byte[] record) { + try { + updateServiceRecordNative(handle, record); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void updateServiceRecordNative(int handle, byte[] record) + throws IOException; + + /** + * Update an exisiting record, using XML. + * @param handle Handle to exisiting record + * @param record The record as an XML string. + */ + public synchronized void updateServiceRecordFromXml(int handle, String record) { + try { + updateServiceRecordFromXmlNative(handle, record); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void updateServiceRecordFromXmlNative(int handle, String record) + throws IOException; + + /** + * Remove a service record. + * It is only possible to remove service records that were added by the + * current connection. + * @param handle Handle to exisiting record to be removed + */ + public synchronized void removeServiceRecord(int handle) { + try { + removeServiceRecordNative(handle); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void removeServiceRecordNative(int handle) throws IOException; +} diff --git a/framework/java/android/bluetooth/DeviceClass.java b/framework/java/android/bluetooth/DeviceClass.java new file mode 100644 index 00000000000..36035ca4db0 --- /dev/null +++ b/framework/java/android/bluetooth/DeviceClass.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 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; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Static helper methods and constants to decode the device class bit vector + * returned by the Bluetooth API. + * + * The Android Bluetooth API returns a 32-bit integer to represent the device + * class. This is actually a bit vector, the format defined at + * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + * (login required). This class provides static helper methods and constants to + * determine what Service Class(es), Major Class, and Minor Class are encoded + * in a 32-bit device class. + * + * Each of the helper methods takes the 32-bit integer device class as an + * argument. + * + * @hide + */ +public class DeviceClass { + + // Baseband class information + // See http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + + public static final int SERVICE_CLASS_BITMASK = 0xFFE000; + public static final int SERVICE_CLASS_LIMITED_DISCOVERABILITY = 0x002000; + public static final int SERVICE_CLASS_POSITIONING = 0x010000; + public static final int SERVICE_CLASS_NETWORKING = 0x020000; + public static final int SERVICE_CLASS_RENDER = 0x040000; + public static final int SERVICE_CLASS_CAPTURE = 0x080000; + public static final int SERVICE_CLASS_OBJECT_TRANSFER = 0x100000; + public static final int SERVICE_CLASS_AUDIO = 0x200000; + public static final int SERVICE_CLASS_TELEPHONY = 0x400000; + public static final int SERVICE_CLASS_INFORMATION = 0x800000; + + public static final int MAJOR_CLASS_BITMASK = 0x001F00; + public static final int MAJOR_CLASS_MISC = 0x000000; + public static final int MAJOR_CLASS_COMPUTER = 0x000100; + public static final int MAJOR_CLASS_PHONE = 0x000200; + public static final int MAJOR_CLASS_NETWORKING = 0x000300; + public static final int MAJOR_CLASS_AUDIO_VIDEO = 0x000400; + public static final int MAJOR_CLASS_PERIPHERAL = 0x000500; + public static final int MAJOR_CLASS_IMAGING = 0x000600; + public static final int MAJOR_CLASS_WEARABLE = 0x000700; + public static final int MAJOR_CLASS_TOY = 0x000800; + public static final int MAJOR_CLASS_MEDICAL = 0x000900; + public static final int MAJOR_CLASS_UNCATEGORIZED = 0x001F00; + + // Minor classes for the AUDIO_VIDEO major class + public static final int MINOR_CLASS_AUDIO_VIDEO_BITMASK = 0x0000FC; + public static final int MINOR_CLASS_AUDIO_VIDEO_UNCATEGORIZED = 0x000000; + public static final int MINOR_CLASS_AUDIO_VIDEO_HEADSET = 0x000004; + public static final int MINOR_CLASS_AUDIO_VIDEO_HANDSFREE = 0x000008; + public static final int MINOR_CLASS_AUDIO_VIDEO_MICROPHONE = 0x000010; + public static final int MINOR_CLASS_AUDIO_VIDEO_LOUDSPEAKER = 0x000014; + public static final int MINOR_CLASS_AUDIO_VIDEO_HEADPHONES = 0x000018; + public static final int MINOR_CLASS_AUDIO_VIDEO_PORTABLE_AUDIO = 0x00001C; + public static final int MINOR_CLASS_AUDIO_VIDEO_CAR_AUDIO = 0x000020; + public static final int MINOR_CLASS_AUDIO_VIDEO_SET_TOP_BOX = 0x000024; + public static final int MINOR_CLASS_AUDIO_VIDEO_HIFI_AUDIO = 0x000028; + public static final int MINOR_CLASS_AUDIO_VIDEO_VCR = 0x00002C; + public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_CAMERA = 0x000030; + public static final int MINOR_CLASS_AUDIO_VIDEO_CAMCORDER = 0x000034; + public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_MONITOR = 0x000038; + public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x00003C; + public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_CONFERENCING = 0x000040; + public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x000048; + + // Indicates the Bluetooth API could not retrieve the class + public static final int CLASS_UNKNOWN = 0xFF000000; + + /** Returns true if the given device class supports the given Service Class. + * A bluetooth device can claim to support zero or more service classes. + * @param deviceClass The bluetooth device class. + * @param serviceClassType The service class constant to test for. For + * example, DeviceClass.SERVICE_CLASS_AUDIO. This + * must be one of the SERVICE_CLASS_xxx constants, + * results of this function are undefined + * otherwise. + * @return If the deviceClass claims to support the serviceClassType. + */ + public static boolean hasServiceClass(int deviceClass, int serviceClassType) { + if (deviceClass == CLASS_UNKNOWN) { + return false; + } + return ((deviceClass & SERVICE_CLASS_BITMASK & serviceClassType) != 0); + } + + /** Returns the Major Class of a bluetooth device class. + * Values returned from this function can be compared with the constants + * MAJOR_CLASS_xxx. A bluetooth device can only be associated + * with one major class. + */ + public static int getMajorClass(int deviceClass) { + if (deviceClass == CLASS_UNKNOWN) { + return CLASS_UNKNOWN; + } + return (deviceClass & MAJOR_CLASS_BITMASK); + } + + /** Returns the Minor Class of a bluetooth device class. + * Values returned from this function can be compared with the constants + * MINOR_CLASS_xxx_yyy, where xxx is the Major Class. A bluetooth + * device can only be associated with one minor class within its major + * class. + */ + public static int getMinorClass(int deviceClass) { + if (deviceClass == CLASS_UNKNOWN) { + return CLASS_UNKNOWN; + } + return (deviceClass & MINOR_CLASS_AUDIO_VIDEO_BITMASK); + } +} diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java new file mode 100644 index 00000000000..bce3388b6e9 --- /dev/null +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2007 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.os.Handler; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +import java.io.IOException; +import java.lang.Thread; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * The base RFCOMM (service) connection for a headset or handsfree device. + * + * In the future this class will be removed. + * + * @hide + */ +public class HeadsetBase { + private static final String TAG = "Bluetooth HeadsetBase"; + private static final boolean DBG = false; + + public static final int RFCOMM_DISCONNECTED = 1; + + public static final int DIRECTION_INCOMING = 1; + public static final int DIRECTION_OUTGOING = 2; + + private final BluetoothDevice mBluetooth; + private final String mAddress; + private final int mRfcommChannel; + private int mNativeData; + private Thread mEventThread; + private volatile boolean mEventThreadInterrupted; + private Handler mEventThreadHandler; + private int mTimeoutRemainingMs; + private final int mDirection; + private final long mConnectTimestamp; + + protected AtParser mAtParser; + + private WakeLock mWakeLock; // held while processing an AT command + + private native static void classInitNative(); + static { + classInitNative(); + } + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + releaseWakeLock(); + } finally { + super.finalize(); + } + } + + private native void cleanupNativeDataNative(); + + public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, + int rfcommChannel) { + mDirection = DIRECTION_OUTGOING; + mConnectTimestamp = System.currentTimeMillis(); + mBluetooth = bluetooth; + mAddress = address; + mRfcommChannel = rfcommChannel; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); + mWakeLock.setReferenceCounted(false); + initializeAtParser(); + // Must be called after this.mAddress is set. + initializeNativeDataNative(-1); + } + + /* Create from an already exisiting rfcomm connection */ + public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, + int rfcommChannel, Handler handler) { + mDirection = DIRECTION_INCOMING; + mConnectTimestamp = System.currentTimeMillis(); + mBluetooth = bluetooth; + mAddress = address; + mRfcommChannel = rfcommChannel; + mEventThreadHandler = handler; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); + mWakeLock.setReferenceCounted(false); + initializeAtParser(); + // Must be called after this.mAddress is set. + initializeNativeDataNative(socketFd); + } + + private native void initializeNativeDataNative(int socketFd); + + /* Process an incoming AT command line + */ + protected synchronized void handleInput(String input) { + acquireWakeLock(); + long timestamp; + + if (DBG) timestamp = System.currentTimeMillis(); + AtCommandResult result = mAtParser.process(input); + if (DBG) Log.d(TAG, "Processing " + input + " took " + + (System.currentTimeMillis() - timestamp) + " ms"); + + if (result.getResultCode() == AtCommandResult.ERROR) { + Log.i(TAG, "Error pocessing <" + input + ">"); + } + + sendURC(result.toString()); + + releaseWakeLock(); + } + + /** + * Register AT commands that are common to all Headset / Handsets. This + * function is called by the HeadsetBase constructor. + */ + protected void initializeAtParser() { + mAtParser = new AtParser(); + + // Microphone Gain + mAtParser.register("+VGM", new AtCommandHandler() { + @Override + public AtCommandResult handleSetCommand(Object[] args) { + // AT+VGM= in range [0,15] + // Headset/Handsfree is reporting its current gain setting + //TODO: sync to android UI + //TODO: Send unsolicited +VGM when volume changed on AG + return new AtCommandResult(AtCommandResult.OK); + } + }); + + // Speaker Gain + mAtParser.register("+VGS", new AtCommandHandler() { + @Override + public AtCommandResult handleSetCommand(Object[] args) { + // AT+VGS= in range [0,15] + // Headset/Handsfree is reporting its current gain to Android + //TODO: sync to AG UI + //TODO: Send unsolicited +VGS when volume changed on AG + return new AtCommandResult(AtCommandResult.OK); + } + }); + } + + public AtParser getAtParser() { + return mAtParser; + } + + public void startEventThread() { + mEventThread = + new Thread("HeadsetBase Event Thread") { + public void run() { + int last_read_error; + while (!mEventThreadInterrupted) { + String input = readNative(500); + if (input != null) { + handleInput(input); + } + else { + last_read_error = getLastReadStatusNative(); + if (last_read_error != 0) { + Log.i(TAG, "headset read error " + last_read_error); + if (mEventThreadHandler != null) { + mEventThreadHandler.obtainMessage(RFCOMM_DISCONNECTED) + .sendToTarget(); + } + disconnectNative(); + break; + } + } + } + } + }; + mEventThreadInterrupted = false; + mEventThread.start(); + } + + + + private native String readNative(int timeout_ms); + private native int getLastReadStatusNative(); + + private void stopEventThread() { + mEventThreadInterrupted = true; + mEventThread.interrupt(); + try { + mEventThread.join(); + } catch (java.lang.InterruptedException e) { + // FIXME: handle this, + } + mEventThread = null; + } + + public boolean connect(Handler handler) { + if (mEventThread == null) { + if (!connectNative()) return false; + mEventThreadHandler = handler; + } + return true; + } + private native boolean connectNative(); + + /* + * Returns true when either the asynchronous connect is in progress, or + * the connect is complete. Call waitForAsyncConnect() to find out whether + * the connect is actually complete, or disconnect() to cancel. + */ + + public boolean connectAsync() { + return connectAsyncNative(); + } + private native boolean connectAsyncNative(); + + public int getRemainingAsyncConnectWaitingTimeMs() { + return mTimeoutRemainingMs; + } + + /* + * Returns 1 when an async connect is complete, 0 on timeout, and -1 on + * error. On error, handler will be called, and you need to re-initiate + * the async connect. + */ + public int waitForAsyncConnect(int timeout_ms, Handler handler) { + int res = waitForAsyncConnectNative(timeout_ms); + if (res > 0) { + mEventThreadHandler = handler; + } + return res; + } + private native int waitForAsyncConnectNative(int timeout_ms); + + public void disconnect() { + if (mEventThread != null) { + stopEventThread(); + } + disconnectNative(); + } + private native void disconnectNative(); + + + /* + * Note that if a remote side disconnects, this method will still return + * true until disconnect() is called. You know when a remote side + * disconnects because you will receive the intent + * IBluetoothService.REMOTE_DEVICE_DISCONNECTED_ACTION. If, when you get + * this intent, method isConnected() returns true, you know that the + * disconnect was initiated by the remote device. + */ + + public boolean isConnected() { + return mEventThread != null; + } + + public String getAddress() { + return mAddress; + } + + public String getName() { + return mBluetooth.getRemoteName(mAddress); + } + + public int getDirection() { + return mDirection; + } + + public long getConnectTimestamp() { + return mConnectTimestamp; + } + + public synchronized boolean sendURC(String urc) { + if (urc.length() > 0) { + boolean ret = sendURCNative(urc); + return ret; + } + return true; + } + private native boolean sendURCNative(String urc); + + private void acquireWakeLock() { + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } + + private void releaseWakeLock() { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + + private void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl new file mode 100644 index 00000000000..e7cc8edc9c6 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008, 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.bluetooth.IBluetoothDeviceCallback; + +/** + * System private API for talking with the Bluetooth service. + * + * {@hide} + */ +interface IBluetoothDevice +{ + boolean isEnabled(); + boolean enable(in IBluetoothDeviceCallback callback); // async + boolean disable(); + + String getAddress(); + String getName(); + boolean setName(in String name); + String getMajorClass(); + String getMinorClass(); + String getVersion(); + String getRevision(); + String getManufacturer(); + String getCompany(); + + int getMode(); + boolean setMode(int mode); + + int getDiscoverableTimeout(); + boolean setDiscoverableTimeout(int timeout); + + boolean startDiscovery(boolean resolveNames); + boolean cancelDiscovery(); + boolean isDiscovering(); + boolean startPeriodicDiscovery(); + boolean stopPeriodicDiscovery(); + boolean isPeriodicDiscovery(); + String[] listRemoteDevices(); + + String[] listAclConnections(); + boolean isAclConnected(in String address); + boolean disconnectRemoteDeviceAcl(in String address); + + boolean createBonding(in String address, in IBluetoothDeviceCallback callback); + boolean cancelBondingProcess(in String address); + String[] listBondings(); + boolean hasBonding(in String address); + boolean removeBonding(in String address); + + String getRemoteName(in String address); + String getRemoteAlias(in String address); + boolean setRemoteAlias(in String address, in String alias); + boolean clearRemoteAlias(in String address); + String getRemoteVersion(in String address); + String getRemoteRevision(in String address); + int getRemoteClass(in String address); + String getRemoteManufacturer(in String address); + String getRemoteCompany(in String address); + String getRemoteMajorClass(in String address); + String getRemoteMinorClass(in String address); + String[] getRemoteServiceClasses(in String address); + boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); + byte[] getRemoteFeatures(in String adddress); + String lastSeen(in String address); + String lastUsed(in String address); + + boolean setPin(in String address, in byte[] pin); + boolean cancelPin(in String address); +} diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl new file mode 100644 index 00000000000..86f44dde9ac --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008, 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; + +/** + * {@hide} + */ +oneway interface IBluetoothDeviceCallback +{ + void onCreateBondingResult(in String address, int result); + void onGetRemoteServiceChannelResult(in String address, int channel); + + void onEnableResult(int result); +} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl new file mode 100644 index 00000000000..7b6030b1cf6 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 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.bluetooth.IBluetoothHeadsetCallback; + +/** + * System private API for Bluetooth Headset service + * + * {@hide} + */ +interface IBluetoothHeadset { + int getState(); + + String getHeadsetAddress(); + + // Request that the given headset be connected + // Assumes the given headset is already bonded + // Will disconnect any currently connected headset + // returns false if cannot start a connection (for example, there is + // already a pending connect). callback will always be called iff this + // returns true + boolean connectHeadset(in String address, in IBluetoothHeadsetCallback callback); + + boolean isConnected(in String address); + + void disconnectHeadset(); +} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl new file mode 100644 index 00000000000..03e884b341c --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2008, 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; + +/** + * {@hide} + */ +oneway interface IBluetoothHeadsetCallback +{ + void onConnectHeadsetResult(in String address, int resultCode); +} diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java new file mode 100644 index 00000000000..a33263f5261 --- /dev/null +++ b/framework/java/android/bluetooth/RfcommSocket.java @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2007 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 java.io.IOException; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.FileDescriptor; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket + * is similar to a normal socket in that it takes an address and a port number. + * The difference is of course that the address is a Bluetooth-device address, + * and the port number is an RFCOMM channel. The API allows for the + * establishment of listening sockets via methods + * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and + * {@link #accept(RfcommSocket, int) accept}, as well as for the making of + * outgoing connections with {@link #connect(String, int) connect}, + * {@link #connectAsync(String, int) connectAsync}, and + * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. + * + * After constructing a socket, you need to {@link #create() create} it and then + * {@link #destroy() destroy} it when you are done using it. Both + * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return + * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. + * Alternatively, you may call {@link #getInputStream() getInputStream} and + * {@link #getOutputStream() getOutputStream} to retrieve the respective streams + * without going through the FileDescriptor. + * + * @hide + */ +public class RfcommSocket { + + /** + * Used by the native implementation of the class. + */ + private int mNativeData; + + /** + * Used by the native implementation of the class. + */ + private int mPort; + + /** + * Used by the native implementation of the class. + */ + private String mAddress; + + /** + * We save the return value of {@link #create() create} and + * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to + * retrieve the I/O streams. + */ + private FileDescriptor mFd; + + /** + * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, + * if the return value is zero, then, the the remaining time left to wait is + * written into this variable (by the native implementation). It is possible + * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before + * the user-specified timeout expires, which is why we save the remaining + * time in this member variable for the user to retrieve by calling method + * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. + */ + private int mTimeoutRemainingMs; + + /** + * Set to true when an asynchronous (nonblocking) connect is in progress. + * {@see #connectAsync(String,int)}. + */ + private boolean mIsConnecting; + + /** + * Set to true after a successful call to {@link #bind(String,int) bind} and + * used for error checking in {@link #listen(int) listen}. Reset to false + * on {@link #destroy() destroy}. + */ + private boolean mIsBound = false; + + /** + * Set to true after a successful call to {@link #listen(int) listen} and + * used for error checking in {@link #accept(RfcommSocket,int) accept}. + * Reset to false on {@link #destroy() destroy}. + */ + private boolean mIsListening = false; + + /** + * Used to store the remaining time after an accept with a non-negative + * timeout returns unsuccessfully. It is possible that a blocking + * {@link #accept(int) accept} may wait for less than the time specified by + * the user, which is why we store the remainder in this member variable for + * it to be retrieved with method + * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. + */ + private int mAcceptTimeoutRemainingMs; + + /** + * Maintained by {@link #getInputStream() getInputStream}. + */ + protected FileInputStream mInputStream; + + /** + * Maintained by {@link #getOutputStream() getOutputStream}. + */ + protected FileOutputStream mOutputStream; + + private native void initializeNativeDataNative(); + + /** + * Constructor. + */ + public RfcommSocket() { + initializeNativeDataNative(); + } + + private native void cleanupNativeDataNative(); + + /** + * Called by the GC to clean up the native data that we set up when we + * construct the object. + */ + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + + private native static void classInitNative(); + + static { + classInitNative(); + } + + /** + * Creates a socket. You need to call this method before performing any + * other operation on a socket. + * + * @return FileDescriptor for the data stream. + * @throws IOException + * @see #destroy() + */ + public FileDescriptor create() throws IOException { + if (mFd == null) { + mFd = createNative(); + } + if (mFd == null) { + throw new IOException("socket not created"); + } + return mFd; + } + + private native FileDescriptor createNative(); + + /** + * Destroys a socket created by {@link #create() create}. Call this + * function when you no longer use the socket in order to release the + * underlying OS resources. + * + * @see #create() + */ + public void destroy() { + synchronized (this) { + destroyNative(); + mFd = null; + mIsBound = false; + mIsListening = false; + } + } + + private native void destroyNative(); + + /** + * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. + * + * @return the FileDescriptor + * @throws IOException + * when the socket has not been {@link #create() created}. + */ + public FileDescriptor getFileDescriptor() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + return mFd; + } + + /** + * Retrieves the input stream from the socket. Alternatively, you can do + * that from the FileDescriptor returned by {@link #create() create} or + * {@link #accept(RfcommSocket, int) accept}. + * + * @return InputStream + * @throws IOException + * if you have not called {@link #create() create} on the + * socket. + */ + public InputStream getInputStream() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + + synchronized (this) { + if (mInputStream == null) { + mInputStream = new FileInputStream(mFd); + } + + return mInputStream; + } + } + + /** + * Retrieves the output stream from the socket. Alternatively, you can do + * that from the FileDescriptor returned by {@link #create() create} or + * {@link #accept(RfcommSocket, int) accept}. + * + * @return OutputStream + * @throws IOException + * if you have not called {@link #create() create} on the + * socket. + */ + public OutputStream getOutputStream() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + + synchronized (this) { + if (mOutputStream == null) { + mOutputStream = new FileOutputStream(mFd); + } + + return mOutputStream; + } + } + + /** + * Starts a blocking connect to a remote RFCOMM socket. It takes the address + * of a device and the RFCOMM channel (port) to which to connect. + * + * @param address + * is the Bluetooth address of the remote device. + * @param port + * is the RFCOMM channel + * @return true on success, false on failure + * @throws IOException + * if {@link #create() create} has not been called. + * @see #connectAsync(String, int) + */ + public boolean connect(String address, int port) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return connectNative(address, port); + } + } + + private native boolean connectNative(String address, int port); + + /** + * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. + * It takes the address of the device to connect to, as well as the RFCOMM + * channel (port). On successful return (return value is true), you need to + * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to + * block for up to a specified number of milliseconds while waiting for the + * asyncronous connect to complete. + * + * @param address + * of remote device + * @param port + * the RFCOMM channel + * @return true when the asynchronous connect has successfully started, + * false if there was an error. + * @throws IOException + * is you have not called {@link #create() create} + * @see #waitForAsyncConnect(int) + * @see #getRemainingAsyncConnectWaitingTimeMs() + * @see #connect(String, int) + */ + public boolean connectAsync(String address, int port) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + mIsConnecting = connectAsyncNative(address, port); + return mIsConnecting; + } + } + + private native boolean connectAsyncNative(String address, int port); + + /** + * Interrupts an asynchronous connect in progress. This method does nothing + * when there is no asynchronous connect in progress. + * + * @throws IOException + * if you have not called {@link #create() create}. + * @see #connectAsync(String, int) + */ + public void interruptAsyncConnect() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (mIsConnecting) { + mIsConnecting = !interruptAsyncConnectNative(); + } + } + } + + private native boolean interruptAsyncConnectNative(); + + /** + * Tells you whether there is an asynchronous connect in progress. This + * method returns an undefined value when there is a synchronous connect in + * progress. + * + * @return true if there is an asyc connect in progress, false otherwise + * @see #connectAsync(String, int) + */ + public boolean isConnecting() { + return mIsConnecting; + } + + /** + * Blocks for a specified amount of milliseconds while waiting for an + * asynchronous connect to complete. Returns an integer value to indicate + * one of the following: the connect succeeded, the connect is still in + * progress, or the connect failed. It is possible for this method to block + * for less than the time specified by the user, and still return zero + * (i.e., async connect is still in progress.) For this reason, if the + * return value is zero, you need to call method + * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} + * to retrieve the remaining time. + * + * @param timeoutMs + * the time to block while waiting for the async connect to + * complete. + * @return a positive value if the connect succeeds; zero, if the connect is + * still in progress, and a negative value if the connect failed. + * + * @throws IOException + * @see #getRemainingAsyncConnectWaitingTimeMs() + * @see #connectAsync(String, int) + */ + public int waitForAsyncConnect(int timeoutMs) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + int ret = waitForAsyncConnectNative(timeoutMs); + if (ret != 0) { + mIsConnecting = false; + } + return ret; + } + } + + private native int waitForAsyncConnectNative(int timeoutMs); + + /** + * Returns the number of milliseconds left to wait after the last call to + * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. + * + * It is possible that waitForAsyncConnect() waits for less than the time + * specified by the user, and still returns zero (i.e., async connect is + * still in progress.) For this reason, if the return value is zero, you + * need to call this method to retrieve the remaining time before you call + * waitForAsyncConnect again. + * + * @return the remaining timeout in milliseconds. + * @see #waitForAsyncConnect(int) + * @see #connectAsync(String, int) + */ + public int getRemainingAsyncConnectWaitingTimeMs() { + return mTimeoutRemainingMs; + } + + /** + * Shuts down both directions on a socket. + * + * @return true on success, false on failure; if the return value is false, + * the socket might be left in a patially shut-down state (i.e. one + * direction is shut down, but the other is still open.) In this + * case, you should {@link #destroy() destroy} and then + * {@link #create() create} the socket again. + * @throws IOException + * is you have not caled {@link #create() create}. + * @see #shutdownInput() + * @see #shutdownOutput() + */ + public boolean shutdown() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (shutdownNative(true)) { + return shutdownNative(false); + } + + return false; + } + } + + /** + * Shuts down the input stream of the socket, but leaves the output stream + * in its current state. + * + * @return true on success, false on failure + * @throws IOException + * is you have not called {@link #create() create} + * @see #shutdown() + * @see #shutdownOutput() + */ + public boolean shutdownInput() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return shutdownNative(true); + } + } + + /** + * Shut down the output stream of the socket, but leaves the input stream in + * its current state. + * + * @return true on success, false on failure + * @throws IOException + * is you have not called {@link #create() create} + * @see #shutdown() + * @see #shutdownInput() + */ + public boolean shutdownOutput() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return shutdownNative(false); + } + } + + private native boolean shutdownNative(boolean shutdownInput); + + /** + * Tells you whether a socket is connected to another socket. This could be + * for input or output or both. + * + * @return true if connected, false otherwise. + * @see #isInputConnected() + * @see #isOutputConnected() + */ + public boolean isConnected() { + return isConnectedNative() > 0; + } + + /** + * Determines whether input is connected (i.e., whether you can receive data + * on this socket.) + * + * @return true if input is connected, false otherwise. + * @see #isConnected() + * @see #isOutputConnected() + */ + public boolean isInputConnected() { + return (isConnectedNative() & 1) != 0; + } + + /** + * Determines whether output is connected (i.e., whether you can send data + * on this socket.) + * + * @return true if output is connected, false otherwise. + * @see #isConnected() + * @see #isInputConnected() + */ + public boolean isOutputConnected() { + return (isConnectedNative() & 2) != 0; + } + + private native int isConnectedNative(); + + /** + * Binds a listening socket to the local device, or a non-listening socket + * to a remote device. The port is automatically selected as the first + * available port in the range 12 to 30. + * + * NOTE: Currently we ignore the device parameter and always bind the socket + * to the local device, assuming that it is a listening socket. + * + * TODO: Use bind(0) in native code to have the kernel select an unused + * port. + * + * @param device + * Bluetooth address of device to bind to (currently ignored). + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} + * @see #listen(int) + * @see #accept(RfcommSocket,int) + */ + public boolean bind(String device) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + for (int port = 12; port <= 30; port++) { + if (bindNative(device, port)) { + mIsBound = true; + return true; + } + } + mIsBound = false; + return false; + } + + /** + * Binds a listening socket to the local device, or a non-listening socket + * to a remote device. + * + * NOTE: Currently we ignore the device parameter and always bind the socket + * to the local device, assuming that it is a listening socket. + * + * @param device + * Bluetooth address of device to bind to (currently ignored). + * @param port + * RFCOMM channel to bind socket to. + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} + * @see #listen(int) + * @see #accept(RfcommSocket,int) + */ + public boolean bind(String device, int port) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + mIsBound = bindNative(device, port); + return mIsBound; + } + + private native boolean bindNative(String device, int port); + + /** + * Starts listening for incoming connections on this socket, after it has + * been bound to an address and RFCOMM channel with + * {@link #bind(String,int) bind}. + * + * @param backlog + * the number of pending incoming connections to queue for + * {@link #accept(RfcommSocket, int) accept}. + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} or if the + * socket has not been bound to a device and RFCOMM channel. + */ + public boolean listen(int backlog) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (!mIsBound) { + throw new IOException("socket not bound"); + } + mIsListening = listenNative(backlog); + return mIsListening; + } + + private native boolean listenNative(int backlog); + + /** + * Accepts incoming-connection requests for a listening socket bound to an + * RFCOMM channel. The user may provide a time to wait for an incoming + * connection. + * + * Note that this method may return null (i.e., no incoming connection) + * before the user-specified timeout expires. For this reason, on a null + * return value, you need to call + * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} + * in order to see how much time is left to wait, before you call this + * method again. + * + * @param newSock + * is set to the new socket that is created as a result of a + * successful accept. + * @param timeoutMs + * time (in milliseconds) to block while waiting to an + * incoming-connection request. A negative value is an infinite + * wait. + * @return FileDescriptor of newSock on success, null on failure. Failure + * occurs if the timeout expires without a successful connect. + * @throws IOException + * if the socket has not been {@link #create() create}ed, is + * not bound, or is not a listening socket. + * @see #bind(String, int) + * @see #listen(int) + * @see #getRemainingAcceptWaitingTimeMs() + */ + public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) + throws IOException { + synchronized (newSock) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (mIsListening == false) { + throw new IOException("not listening on socket"); + } + newSock.mFd = acceptNative(newSock, timeoutMs); + return newSock.mFd; + } + } + + /** + * Returns the number of milliseconds left to wait after the last call to + * {@link #accept(RfcommSocket, int) accept}. + * + * Since accept() may return null (i.e., no incoming connection) before the + * user-specified timeout expires, you need to call this method in order to + * see how much time is left to wait, and wait for that amount of time + * before you call accept again. + * + * @return the remaining time, in milliseconds. + */ + public int getRemainingAcceptWaitingTimeMs() { + return mAcceptTimeoutRemainingMs; + } + + private native FileDescriptor acceptNative(RfcommSocket newSock, + int timeoutMs); + + /** + * Get the port (rfcomm channel) associated with this socket. + * + * This is only valid if the port has been set via a successful call to + * {@link #bind(String, int)}, {@link #connect(String, int)} + * or {@link #connectAsync(String, int)}. This can be checked + * with {@link #isListening()} and {@link #isConnected()}. + * @return Port (rfcomm channel) + */ + public int getPort() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (!mIsListening && !isConnected()) { + throw new IOException("not listening or connected on socket"); + } + return mPort; + } + + /** + * Return true if this socket is listening ({@link #listen(int)} + * has been called successfully). + */ + public boolean isListening() { + return mIsListening; + } +} diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java new file mode 100644 index 00000000000..75b33296dd1 --- /dev/null +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2008 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.content.Context; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +import java.io.IOException; +import java.lang.Thread; + + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Simple SCO Socket. + * Currently in Android, there is no support for sending data over a SCO + * socket - this is managed by the hardware link to the Bluetooth Chip. This + * class is instead intended for management of the SCO socket lifetime, + * and is tailored for use with the headset / handsfree profiles. + * @hide + */ +public class ScoSocket { + private static final String TAG = "ScoSocket"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // even more logging + + public static final int STATE_READY = 1; // Ready for use. No threads or sockets + public static final int STATE_ACCEPT = 2; // accept() thread running + public static final int STATE_CONNECTING = 3; // connect() thread running + public static final int STATE_CONNECTED = 4; // connected, waiting for close() + public static final int STATE_CLOSED = 5; // was connected, now closed. + + private int mState; + private int mNativeData; + private Handler mHandler; + private int mAcceptedCode; + private int mConnectedCode; + private int mClosedCode; + + private WakeLock mWakeLock; // held while STATE_CONNECTING or STATE_CONNECTED + + static { + classInitNative(); + } + private native static void classInitNative(); + + public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode, + int closedCode) { + initNative(); + mState = STATE_READY; + mHandler = handler; + mAcceptedCode = acceptedCode; + mConnectedCode = connectedCode; + mClosedCode = closedCode; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket"); + mWakeLock.setReferenceCounted(false); + if (VDBG) log(this + " SCO OBJECT CTOR"); + } + private native void initNative(); + + protected void finalize() throws Throwable { + try { + if (VDBG) log(this + " SCO OBJECT DTOR"); + destroyNative(); + releaseWakeLock(); + } finally { + super.finalize(); + } + } + private native void destroyNative(); + + /** Connect this SCO socket to the given BT address. + * Does not block. + */ + public synchronized boolean connect(String address) { + if (VDBG) log("connect() " + this); + if (mState != STATE_READY) { + if (DBG) log("connect(): Bad state"); + return false; + } + acquireWakeLock(); + if (connectNative(address)) { + mState = STATE_CONNECTING; + return true; + } else { + mState = STATE_CLOSED; + releaseWakeLock(); + return false; + } + } + private native boolean connectNative(String address); + + /** Accept incoming SCO connections. + * Does not block. + */ + public synchronized boolean accept() { + if (VDBG) log("accept() " + this); + if (mState != STATE_READY) { + if (DBG) log("Bad state"); + return false; + } + if (acceptNative()) { + mState = STATE_ACCEPT; + return true; + } else { + mState = STATE_CLOSED; + return false; + } + } + private native boolean acceptNative(); + + public synchronized void close() { + if (DBG) log(this + " SCO OBJECT close() mState = " + mState); + mState = STATE_CLOSED; + closeNative(); + releaseWakeLock(); + } + private native void closeNative(); + + public synchronized int getState() { + return mState; + } + + private synchronized void onConnected(int result) { + if (VDBG) log(this + " onConnected() mState = " + mState + " " + this); + if (mState != STATE_CONNECTING) { + if (DBG) log("Strange state, closing " + mState + " " + this); + return; + } + if (result >= 0) { + mState = STATE_CONNECTED; + } else { + mState = STATE_CLOSED; + } + mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); + if (result < 0) { + releaseWakeLock(); + } + } + + private synchronized void onAccepted(int result) { + if (VDBG) log("onAccepted() " + this); + if (mState != STATE_ACCEPT) { + if (DBG) log("Strange state" + this); + return; + } + if (result >= 0) { + acquireWakeLock(); + mState = STATE_CONNECTED; + } else { + mState = STATE_CLOSED; + } + mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget(); + } + + private synchronized void onClosed() { + if (DBG) log("onClosed() " + this); + if (mState != STATE_CLOSED) { + mState = STATE_CLOSED; + mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget(); + releaseWakeLock(); + } + } + + private void acquireWakeLock() { + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + if (VDBG) log("mWakeLock.acquire()" + this); + } + } + + private void releaseWakeLock() { + if (mWakeLock.isHeld()) { + if (VDBG) log("mWakeLock.release()" + this); + mWakeLock.release(); + } + } + + private void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html new file mode 100644 index 00000000000..ccd8fecaf82 --- /dev/null +++ b/framework/java/android/bluetooth/package.html @@ -0,0 +1,14 @@ + + +Provides classes that manage Bluetooth functionality on the device. +

+The Bluetooth APIs allow applications can connect and disconnect headsets, or scan +for other kinds of Bluetooth devices and pair them. Further control includes the +ability to write and modify the local Service Discovery Protocol (SDP) database, +query the SDP database of other Bluetooth devices, establish RFCOMM +channels/sockets on Android, and connect to specified sockets on other devices. +

+

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

+{@hide} + + -- GitLab From 9b9a933e8411c52725a2946ad6c71fc351c09052 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:43 -0800 Subject: [PATCH 0002/1408] Code drop from //branches/cupcake/...@124589 --- .../java/android/bluetooth/BluetoothA2dp.java | 181 +++++++++++++++++ .../android/bluetooth/BluetoothClass.java | 191 ++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 2 +- .../android/bluetooth/BluetoothError.java | 42 ++++ .../android/bluetooth/BluetoothHeadset.java | 162 ++++++++++++--- .../java/android/bluetooth/DeviceClass.java | 131 ------------ .../android/bluetooth/IBluetoothA2dp.aidl | 29 +++ .../android/bluetooth/IBluetoothHeadset.aidl | 6 +- framework/java/android/bluetooth/package.html | 1 - 9 files changed, 581 insertions(+), 164 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothA2dp.java create mode 100644 framework/java/android/bluetooth/BluetoothClass.java create mode 100644 framework/java/android/bluetooth/BluetoothError.java delete mode 100644 framework/java/android/bluetooth/DeviceClass.java create mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java new file mode 100644 index 00000000000..d6ea889e8c9 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.server.BluetoothA2dpService; +import android.content.Context; +import android.os.ServiceManager; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +import java.util.List; + +/** + * Public API for controlling the Bluetooth A2DP Profile Service. + * + * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP + * Service via IPC. + * + * Creating a BluetoothA2dp object will initiate a binding with the + * BluetoothHeadset service. Users of this object should call close() when they + * are finished, so that this proxy object can unbind from the service. + * + * Currently the BluetoothA2dp service runs in the system server and this + * proxy object will be immediately bound to the service on construction. + * However this may change in future releases, and error codes such as + * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the + * proxy object is not yet attached. + * + * Currently this class provides methods to connect to A2DP audio sinks. + * + * @hide + */ +public class BluetoothA2dp { + private static final String TAG = "BluetoothA2dp"; + + /** int extra for SINK_STATE_CHANGED_ACTION */ + public static final String SINK_STATE = + "android.bluetooth.a2dp.intent.SINK_STATE"; + /** int extra for SINK_STATE_CHANGED_ACTION */ + public static final String SINK_PREVIOUS_STATE = + "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; + + /** Indicates the state of an A2DP audio sink has changed. + * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SINK_STATE_CHANGED_ACTION = + "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; + + public static final int STATE_DISCONNECTED = 0; + public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTED = 2; + public static final int STATE_DISCONNECTING = 3; + /** Playing implies connected */ + public static final int STATE_PLAYING = 4; + + private final IBluetoothA2dp mService; + private final Context mContext; + + /** + * Create a BluetoothA2dp proxy object for interacting with the local + * Bluetooth A2DP service. + * @param c Context + */ + public BluetoothA2dp(Context c) { + mContext = c; + IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); + if (b == null) { + throw new RuntimeException("Bluetooth A2DP service not available!"); + } + mService = IBluetoothA2dp.Stub.asInterface(b); + } + + /** Initiate a connection to an A2DP sink. + * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when the + * connection is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public int connectSink(String address) { + try { + return mService.connectSink(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** Initiate disconnect from an A2DP sink. + * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when + * disconnect is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public int disconnectSink(String address) { + try { + return mService.disconnectSink(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** Check if a specified A2DP sink is connected. + * @param address Remote BT address. + * @return True if connected (or playing), false otherwise and on error. + * @hide + */ + public boolean isSinkConnected(String address) { + int state = getSinkState(address); + return state == STATE_CONNECTED || state == STATE_PLAYING; + } + + /** Check if any A2DP sink is connected. + * @return a List of connected A2DP sinks, or null on error. + * @hide + */ + public List listConnectedSinks() { + try { + return mService.listConnectedSinks(); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return null; + } + } + + /** Get the state of an A2DP sink + * @param address Remote BT address. + * @return State code, or negative on error + * @hide + */ + public int getSinkState(String address) { + try { + return mService.getSinkState(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** Helper for converting a state to a string. + * For debug use only - strings are not internationalized. + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + default: + return ""; + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java new file mode 100644 index 00000000000..88ce18b4577 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2008 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; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Static helper methods and constants to decode the device class bit vector + * returned by the Bluetooth API. + * + * The Android Bluetooth API returns a 32-bit integer to represent the class. + * The format of these bits is defined at + * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + * (login required). This class provides static helper methods and constants to + * determine what Service Class(es) and Device Class are encoded in the 32-bit + * class. + * + * Devices typically have zero or more service classes, and exactly one device + * class. The device class is encoded as a major and minor device class, the + * minor being a subset of the major. + * + * Class is useful to describe a device (for example to show an icon), + * but does not reliably describe what profiles a device supports. To determine + * profile support you usually need to perform SDP queries. + * + * Each of these helper methods takes the 32-bit integer class as an argument. + * + * @hide + */ +public class BluetoothClass { + /** Indicates the Bluetooth API could not retrieve the class */ + public static final int ERROR = 0xFF000000; + + /** Every Bluetooth device has zero or more service classes */ + public static class Service { + public static final int BITMASK = 0xFFE000; + + public static final int LIMITED_DISCOVERABILITY = 0x002000; + public static final int POSITIONING = 0x010000; + public static final int NETWORKING = 0x020000; + public static final int RENDER = 0x040000; + public static final int CAPTURE = 0x080000; + public static final int OBJECT_TRANSFER = 0x100000; + public static final int AUDIO = 0x200000; + public static final int TELEPHONY = 0x400000; + public static final int INFORMATION = 0x800000; + + /** Returns true if the given class supports the given Service Class. + * A bluetooth device can claim to support zero or more service classes. + * @param btClass The bluetooth class. + * @param serviceClass The service class constant to test for. For + * example, Service.AUDIO. Must be one of the + * Service.FOO constants. + * @return True if the service class is supported. + */ + public static boolean hasService(int btClass, int serviceClass) { + if (btClass == ERROR) { + return false; + } + return ((btClass & Service.BITMASK & serviceClass) != 0); + } + } + + /** Every Bluetooth device has exactly one device class, comprimised of + * major and minor components. We have not included the minor classes for + * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work + * a little differently. */ + public static class Device { + public static final int BITMASK = 0x1FFC; + + public static class Major { + public static final int BITMASK = 0x1F00; + + public static final int MISC = 0x0000; + public static final int COMPUTER = 0x0100; + public static final int PHONE = 0x0200; + public static final int NETWORKING = 0x0300; + public static final int AUDIO_VIDEO = 0x0400; + public static final int PERIPHERAL = 0x0500; + public static final int IMAGING = 0x0600; + public static final int WEARABLE = 0x0700; + public static final int TOY = 0x0800; + public static final int HEALTH = 0x0900; + public static final int UNCATEGORIZED = 0x1F00; + + /** Returns the Major Device Class component of a bluetooth class. + * Values returned from this function can be compared with the constants + * Device.Major.FOO. A bluetooth device can only be associated + * with one major class. + */ + public static int getDeviceMajor(int btClass) { + if (btClass == ERROR) { + return ERROR; + } + return (btClass & Device.Major.BITMASK); + } + } + + // Devices in the COMPUTER major class + public static final int COMPUTER_UNCATEGORIZED = 0x0100; + public static final int COMPUTER_DESKTOP = 0x0104; + public static final int COMPUTER_SERVER = 0x0108; + public static final int COMPUTER_LAPTOP = 0x010C; + public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; + public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; + public static final int COMPUTER_WEARABLE = 0x0118; + + // Devices in the PHONE major class + public static final int PHONE_UNCATEGORIZED = 0x0200; + public static final int PHONE_CELLULAR = 0x0204; + public static final int PHONE_CORDLESS = 0x0208; + public static final int PHONE_SMART = 0x020C; + public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; + public static final int PHONE_ISDN = 0x0214; + + // Minor classes for the AUDIO_VIDEO major class + public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; + public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; + public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; + //public static final int AUDIO_VIDEO_RESERVED = 0x040C; + public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; + public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; + public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; + public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; + public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; + public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; + public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; + public static final int AUDIO_VIDEO_VCR = 0x042C; + public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; + public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; + public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; + public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; + public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; + //public static final int AUDIO_VIDEO_RESERVED = 0x0444; + public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; + + // Devices in the WEARABLE major class + public static final int WEARABLE_UNCATEGORIZED = 0x0700; + public static final int WEARABLE_WRIST_WATCH = 0x0704; + public static final int WEARABLE_PAGER = 0x0708; + public static final int WEARABLE_JACKET = 0x070C; + public static final int WEARABLE_HELMET = 0x0710; + public static final int WEARABLE_GLASSES = 0x0714; + + // Devices in the TOY major class + public static final int TOY_UNCATEGORIZED = 0x0800; + public static final int TOY_ROBOT = 0x0804; + public static final int TOY_VEHICLE = 0x0808; + public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; + public static final int TOY_CONTROLLER = 0x0810; + public static final int TOY_GAME = 0x0814; + + // Devices in the HEALTH major class + public static final int HEALTH_UNCATEGORIZED = 0x0900; + public static final int HEALTH_BLOOD_PRESSURE = 0x0904; + public static final int HEALTH_THERMOMETER = 0x0908; + public static final int HEALTH_WEIGHING = 0x090C; + public static final int HEALTH_GLUCOSE = 0x0910; + public static final int HEALTH_PULSE_OXIMETER = 0x0914; + public static final int HEALTH_PULSE_RATE = 0x0918; + public static final int HEALTH_DATA_DISPLAY = 0x091C; + + /** Returns the Device Class component of a bluetooth class. This includes + * both the major and minor device components. Values returned from this + * function can be compared with the constants Device.FOO. A bluetooth + * device can only be associated with one device class. + */ + public static int getDevice(int btClass) { + if (btClass == ERROR) { + return ERROR; + } + return (btClass & Device.BITMASK); + } + } +} + diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0b24db662ca..d1f71c55f05 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -514,7 +514,7 @@ public class BluetoothDevice { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} - return DeviceClass.CLASS_UNKNOWN; + return BluetoothClass.ERROR; } public byte[] getRemoteFeatures(String address) { try { diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/BluetoothError.java new file mode 100644 index 00000000000..2554bead0ca --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothError.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 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; + +/** + * Bluetooth API error codes. + * + * Errors are always negative. + * + * @hide + */ +public class BluetoothError { + /** No error */ + public static final int SUCCESS = 0; + + /** Generic error */ + public static final int ERROR = -1000; + + /** Bluetooth currently disabled */ + public static final int ERROR_DISABLED = -1001; + + /** IPC is not ready, for example service is not yet bound */ + public static final int ERROR_IPC_NOT_READY = -1011; + + /** Some other IPC error, for example a RemoteException */ + public static final int ERROR_IPC = -1012; + +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 90db39bc547..905173e3408 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -28,35 +28,36 @@ import android.util.Log; * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. * - * Public API for controlling the Bluetooth Headset Service. + * Public API for controlling the Bluetooth Headset Service. This includes both + * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will + * attempt a handsfree connection first, and fall back to headset. * * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset - * Service. + * Service via IPC. * * Creating a BluetoothHeadset object will create a binding with the * BluetoothHeadset service. Users of this object should call close() when they * are finished with the BluetoothHeadset, so that this proxy object can unbind * from the service. * - * BlueoothHeadset objects are not guarenteed to be connected to the - * BluetoothHeadsetService at all times. Calls on this object while not - * connected to the service will result in default error return values. Even - * after object construction, there is a short delay (~10ms) before this proxy - * object is actually connected to the Service. + * This BluetoothHeadset object is not immediately bound to the + * BluetoothHeadset service. Use the ServiceListener interface to obtain a + * notification when it is bound, this is especially important if you wish to + * immediately call methods on BluetootHeadset after construction. * * Android only supports one connected Bluetooth Headset at a time. * - * Note that in this context, Headset includes both Bluetooth Headset's and - * Handsfree devices. - * * @hide */ public class BluetoothHeadset { - private final static String TAG = "BluetoothHeadset"; + private static final String TAG = "BluetoothHeadset"; + private static final boolean DBG = false; - private final Context mContext; private IBluetoothHeadset mService; + private final Context mContext; + private final ServiceListener mServiceListener; + private ConnectHeadsetCallback mConnectHeadsetCallback; /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -72,25 +73,44 @@ public class BluetoothHeadset { /** Connection cancelled before completetion. */ public static final int RESULT_CANCELLED = 2; - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - mService = IBluetoothHeadset.Stub.asInterface(service); - Log.i(TAG, "Proxy object is now connected to Bluetooth Headset Service"); - } - public void onServiceDisconnected(ComponentName className) { - mService = null; - } - }; + /** + * An interface for notifying BluetoothHeadset IPC clients when they have + * been connected to the BluetoothHeadset service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothHeadset service. Clients must wait for + * this callback before making IPC calls on the BluetoothHeadset + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothHeadset service. Clients must not + * make IPC calls on the BluetoothHeadset service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothHeadset service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + /** + * Interface for connectHeadset() callback. + * This callback can occur in the Binder thread. + */ + public interface ConnectHeadsetCallback { + public void onConnectHeadsetResult(String address, int resultCode); + } /** * Create a BluetoothHeadset proxy object. - * Remeber to call close() when you are done with this object, so that it - * can unbind from the BluetoothHeadsetService. */ - public BluetoothHeadset(Context context) { + public BluetoothHeadset(Context context, ServiceListener l) { mContext = context; - if (!context.bindService( - new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { + mServiceListener = l; + if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Headset Service"); } } @@ -126,6 +146,9 @@ public class BluetoothHeadset { try { return mService.getState(); } 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 BluetoothHeadset.STATE_ERROR; } @@ -141,6 +164,9 @@ public class BluetoothHeadset { try { return mService.getHeadsetAddress(); } 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 null; } @@ -150,20 +176,29 @@ public class BluetoothHeadset { * This call does not block. Fails if a headset is already connecting * or connected. * Will connect to the last connected headset if address is null. + * onConnectHeadsetResult() of your ConnectHeadsetCallback will be called + * on completition. * @param address The Bluetooth Address to connect to, or null to connect * to the last connected headset. - * @param callback A callback with onCreateBondingResult() defined, or - * null. + * @param callback Callback on result. Not called if false is returned. Can + * be null. + * to the last connected headset. * @return False if there was a problem initiating the connection * procedure, and your callback will not be used. True if * the connection procedure was initiated, in which case * your callback is guarenteed to be called. */ - public boolean connectHeadset(String address, IBluetoothHeadsetCallback callback) { + public boolean connectHeadset(String address, ConnectHeadsetCallback callback) { if (mService != null) { try { - return mService.connectHeadset(address, callback); + if (mService.connectHeadset(address, mHeadsetCallback)) { + mConnectHeadsetCallback = callback; + return true; + } } 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; } @@ -178,6 +213,9 @@ public class BluetoothHeadset { try { return mService.isConnected(address); } 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; } @@ -191,8 +229,72 @@ public class BluetoothHeadset { if (mService != null) { try { mService.disconnectHeadset(); + return true; + } 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; + } + + /** + * Start BT Voice Recognition mode, and set up Bluetooth audio path. + * Returns false if there is no headset connected, or if the + * connected headset does not support voice recognition, or on + * error. + */ + public boolean startVoiceRecognition() { + if (mService != null) { + try { + return mService.startVoiceRecognition(); + } 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; + } + + /** + * Stop BT Voice Recognition mode, and shut down Bluetooth audio path. + * Returns false if there is no headset connected, or the connected + * headset is not in voice recognition mode, or on error. + */ + public boolean stopVoiceRecognition() { + if (mService != null) { + try { + return mService.stopVoiceRecognition(); } 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; } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothHeadset.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(); + } + } + }; + + private IBluetoothHeadsetCallback mHeadsetCallback = new IBluetoothHeadsetCallback.Stub() { + public void onConnectHeadsetResult(String address, int resultCode) { + if (mConnectHeadsetCallback != null) { + mConnectHeadsetCallback.onConnectHeadsetResult(address, resultCode); + } + } + }; } diff --git a/framework/java/android/bluetooth/DeviceClass.java b/framework/java/android/bluetooth/DeviceClass.java deleted file mode 100644 index 36035ca4db0..00000000000 --- a/framework/java/android/bluetooth/DeviceClass.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Static helper methods and constants to decode the device class bit vector - * returned by the Bluetooth API. - * - * The Android Bluetooth API returns a 32-bit integer to represent the device - * class. This is actually a bit vector, the format defined at - * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class provides static helper methods and constants to - * determine what Service Class(es), Major Class, and Minor Class are encoded - * in a 32-bit device class. - * - * Each of the helper methods takes the 32-bit integer device class as an - * argument. - * - * @hide - */ -public class DeviceClass { - - // Baseband class information - // See http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - - public static final int SERVICE_CLASS_BITMASK = 0xFFE000; - public static final int SERVICE_CLASS_LIMITED_DISCOVERABILITY = 0x002000; - public static final int SERVICE_CLASS_POSITIONING = 0x010000; - public static final int SERVICE_CLASS_NETWORKING = 0x020000; - public static final int SERVICE_CLASS_RENDER = 0x040000; - public static final int SERVICE_CLASS_CAPTURE = 0x080000; - public static final int SERVICE_CLASS_OBJECT_TRANSFER = 0x100000; - public static final int SERVICE_CLASS_AUDIO = 0x200000; - public static final int SERVICE_CLASS_TELEPHONY = 0x400000; - public static final int SERVICE_CLASS_INFORMATION = 0x800000; - - public static final int MAJOR_CLASS_BITMASK = 0x001F00; - public static final int MAJOR_CLASS_MISC = 0x000000; - public static final int MAJOR_CLASS_COMPUTER = 0x000100; - public static final int MAJOR_CLASS_PHONE = 0x000200; - public static final int MAJOR_CLASS_NETWORKING = 0x000300; - public static final int MAJOR_CLASS_AUDIO_VIDEO = 0x000400; - public static final int MAJOR_CLASS_PERIPHERAL = 0x000500; - public static final int MAJOR_CLASS_IMAGING = 0x000600; - public static final int MAJOR_CLASS_WEARABLE = 0x000700; - public static final int MAJOR_CLASS_TOY = 0x000800; - public static final int MAJOR_CLASS_MEDICAL = 0x000900; - public static final int MAJOR_CLASS_UNCATEGORIZED = 0x001F00; - - // Minor classes for the AUDIO_VIDEO major class - public static final int MINOR_CLASS_AUDIO_VIDEO_BITMASK = 0x0000FC; - public static final int MINOR_CLASS_AUDIO_VIDEO_UNCATEGORIZED = 0x000000; - public static final int MINOR_CLASS_AUDIO_VIDEO_HEADSET = 0x000004; - public static final int MINOR_CLASS_AUDIO_VIDEO_HANDSFREE = 0x000008; - public static final int MINOR_CLASS_AUDIO_VIDEO_MICROPHONE = 0x000010; - public static final int MINOR_CLASS_AUDIO_VIDEO_LOUDSPEAKER = 0x000014; - public static final int MINOR_CLASS_AUDIO_VIDEO_HEADPHONES = 0x000018; - public static final int MINOR_CLASS_AUDIO_VIDEO_PORTABLE_AUDIO = 0x00001C; - public static final int MINOR_CLASS_AUDIO_VIDEO_CAR_AUDIO = 0x000020; - public static final int MINOR_CLASS_AUDIO_VIDEO_SET_TOP_BOX = 0x000024; - public static final int MINOR_CLASS_AUDIO_VIDEO_HIFI_AUDIO = 0x000028; - public static final int MINOR_CLASS_AUDIO_VIDEO_VCR = 0x00002C; - public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_CAMERA = 0x000030; - public static final int MINOR_CLASS_AUDIO_VIDEO_CAMCORDER = 0x000034; - public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_MONITOR = 0x000038; - public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x00003C; - public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_CONFERENCING = 0x000040; - public static final int MINOR_CLASS_AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x000048; - - // Indicates the Bluetooth API could not retrieve the class - public static final int CLASS_UNKNOWN = 0xFF000000; - - /** Returns true if the given device class supports the given Service Class. - * A bluetooth device can claim to support zero or more service classes. - * @param deviceClass The bluetooth device class. - * @param serviceClassType The service class constant to test for. For - * example, DeviceClass.SERVICE_CLASS_AUDIO. This - * must be one of the SERVICE_CLASS_xxx constants, - * results of this function are undefined - * otherwise. - * @return If the deviceClass claims to support the serviceClassType. - */ - public static boolean hasServiceClass(int deviceClass, int serviceClassType) { - if (deviceClass == CLASS_UNKNOWN) { - return false; - } - return ((deviceClass & SERVICE_CLASS_BITMASK & serviceClassType) != 0); - } - - /** Returns the Major Class of a bluetooth device class. - * Values returned from this function can be compared with the constants - * MAJOR_CLASS_xxx. A bluetooth device can only be associated - * with one major class. - */ - public static int getMajorClass(int deviceClass) { - if (deviceClass == CLASS_UNKNOWN) { - return CLASS_UNKNOWN; - } - return (deviceClass & MAJOR_CLASS_BITMASK); - } - - /** Returns the Minor Class of a bluetooth device class. - * Values returned from this function can be compared with the constants - * MINOR_CLASS_xxx_yyy, where xxx is the Major Class. A bluetooth - * device can only be associated with one minor class within its major - * class. - */ - public static int getMinorClass(int deviceClass) { - if (deviceClass == CLASS_UNKNOWN) { - return CLASS_UNKNOWN; - } - return (deviceClass & MINOR_CLASS_AUDIO_VIDEO_BITMASK); - } -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl new file mode 100644 index 00000000000..7e0226d2fd9 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 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; + +/** + * System private API for Bluetooth A2DP service + * + * {@hide} + */ +interface IBluetoothA2dp { + int connectSink(in String address); + int disconnectSink(in String address); + List listConnectedSinks(); + int getSinkState(in String address); +} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 7b6030b1cf6..564861ff4c7 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -36,7 +36,11 @@ interface IBluetoothHeadset { // returns true boolean connectHeadset(in String address, in IBluetoothHeadsetCallback callback); + void disconnectHeadset(); + boolean isConnected(in String address); - void disconnectHeadset(); + boolean startVoiceRecognition(); + + boolean stopVoiceRecognition(); } diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index ccd8fecaf82..79abf0cb4a0 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -9,6 +9,5 @@ query the SDP database of other Bluetooth devices, establish RFCOMM channels/sockets on Android, and connect to specified sockets on other devices.

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

-{@hide} -- GitLab From 0797d431d49495404dbcddf4a2eb75e8620ec0fc Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Fri, 9 Jan 2009 17:51:23 -0800 Subject: [PATCH 0003/1408] auto import from //branches/cupcake/...@125939 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d6ea889e8c9..022a87cd443 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -90,7 +90,7 @@ public class BluetoothA2dp { } /** Initiate a connection to an A2DP sink. - * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when the + * Listen for SINK_STATE_CHANGED_ACTION to find out when the * connection is completed. * @param address Remote BT address. * @return Result code, negative indicates an immediate error. @@ -106,7 +106,7 @@ public class BluetoothA2dp { } /** Initiate disconnect from an A2DP sink. - * Listen for A2DP_SINK_STATE_CHANGED_ACTION to find out when + * Listen for SINK_STATE_CHANGED_ACTION to find out when * disconnect is completed. * @param address Remote BT address. * @return Result code, negative indicates an immediate error. -- GitLab From 15a48e711fdee4b63d7c6aa78989903a3324235b Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 20 Jan 2009 14:03:58 -0800 Subject: [PATCH 0004/1408] auto import from //branches/cupcake/...@127101 --- .../java/android/bluetooth/BluetoothA2dp.java | 66 +++++++++++ .../android/bluetooth/BluetoothHeadset.java | 109 +++++++++++++----- .../android/bluetooth/IBluetoothA2dp.aidl | 2 + .../android/bluetooth/IBluetoothHeadset.aidl | 18 +-- .../bluetooth/IBluetoothHeadsetCallback.aidl | 25 ---- 5 files changed, 149 insertions(+), 71 deletions(-) delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 022a87cd443..b0b0154abc9 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -72,6 +72,12 @@ public class BluetoothA2dp { /** Playing implies connected */ public static final int STATE_PLAYING = 4; + /** Default priority for a2dp devices that should allow incoming + * connections */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for a2dp devices that should not allow incoming + * connections */ + public static final int PRIORITY_OFF = 0; private final IBluetoothA2dp mService; private final Context mContext; @@ -158,6 +164,66 @@ public class BluetoothA2dp { } } + /** + * Set priority of a2dp sink. + * Priority is a non-negative integer. By default paired sinks will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Sinks with priority greater than zero will accept incoming connections + * (if no sink is currently connected). + * Priority for unpaired sink must be PRIORITY_NONE. + * @param address Paired sink + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return Result code, negative indicates an error + */ + public int setSinkPriority(String address, int priority) { + try { + return mService.setSinkPriority(address, priority); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Get priority of a2dp sink. + * @param address Sink + * @return non-negative priority, or negative error code on error. + */ + public int getSinkPriority(String address) { + try { + return mService.getSinkPriority(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Check class bits for possible A2DP Sink support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might be a A2DP Sink. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might be a A2DP sink + */ + public static boolean doesClassMatchSink(int btClass) { + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // By the A2DP spec, sinks must indicate the RENDER service. + // However we found some that do not (Chordette). So lets also + // match on some other class bits. + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + /** Helper for converting a state to a string. * For debug use only - strings are not internationalized. * @hide diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 905173e3408..c3152719bbb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -57,7 +57,6 @@ public class BluetoothHeadset { private IBluetoothHeadset mService; private final Context mContext; private final ServiceListener mServiceListener; - private ConnectHeadsetCallback mConnectHeadsetCallback; /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -73,6 +72,11 @@ public class BluetoothHeadset { /** Connection cancelled before completetion. */ public static final int RESULT_CANCELLED = 2; + /** Default priority for headsets that should be auto-connected */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for headsets that should not be auto-connected */ + public static final int PRIORITY_OFF = 0; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. @@ -96,14 +100,6 @@ public class BluetoothHeadset { public void onServiceDisconnected(); } - /** - * Interface for connectHeadset() callback. - * This callback can occur in the Binder thread. - */ - public interface ConnectHeadsetCallback { - public void onConnectHeadsetResult(String address, int resultCode); - } - /** * Create a BluetoothHeadset proxy object. */ @@ -175,24 +171,18 @@ public class BluetoothHeadset { * Request to initiate a connection to a headset. * This call does not block. Fails if a headset is already connecting * or connected. - * Will connect to the last connected headset if address is null. - * onConnectHeadsetResult() of your ConnectHeadsetCallback will be called - * on completition. - * @param address The Bluetooth Address to connect to, or null to connect - * to the last connected headset. - * @param callback Callback on result. Not called if false is returned. Can - * be null. - * to the last connected headset. + * Initiates auto-connection if address is null. Tries to connect to all + * devices with priority greater than PRIORITY_AUTO in descending order. + * @param address The Bluetooth Address to connect to, or null to + * auto-connect to the last connected headset. * @return False if there was a problem initiating the connection - * procedure, and your callback will not be used. True if - * the connection procedure was initiated, in which case - * your callback is guarenteed to be called. + * procedure, and no further HEADSET_STATE_CHANGED intents + * will be expected. */ - public boolean connectHeadset(String address, ConnectHeadsetCallback callback) { + public boolean connectHeadset(String address) { if (mService != null) { try { - if (mService.connectHeadset(address, mHeadsetCallback)) { - mConnectHeadsetCallback = callback; + if (mService.connectHeadset(address)) { return true; } } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -273,6 +263,71 @@ public class BluetoothHeadset { return false; } + /** + * Set priority of headset. + * Priority is a non-negative integer. By default paired headsets will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Headsets with priority greater than zero will be auto-connected, and + * incoming connections will be accepted (if no other headset is + * connected). + * Auto-connection occurs at the following events: boot, incoming phone + * call, outgoing phone call. + * Headsets with priority equal to zero, or that are unpaired, are not + * auto-connected. + * Incoming connections are ignored regardless of priority if there is + * already a headset connected. + * @param address Paired headset + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return True if successful, false if there was some error. + */ + public boolean setPriority(String address, int priority) { + if (mService != null) { + try { + return mService.setPriority(address, priority); + } 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; + } + + /** + * Get priority of headset. + * @param address Headset + * @return non-negative priority, or negative error code on error. + */ + public int getPriority(String address) { + if (mService != null) { + try { + return mService.getPriority(address); + } 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 -1; + } + + /** + * Check class bits for possible HSP or HFP support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support HSP or HFP. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support HSP or HFP. + */ + public static boolean doesClassMatch(int btClass) { + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); @@ -289,12 +344,4 @@ public class BluetoothHeadset { } } }; - - private IBluetoothHeadsetCallback mHeadsetCallback = new IBluetoothHeadsetCallback.Stub() { - public void onConnectHeadsetResult(String address, int resultCode) { - if (mConnectHeadsetCallback != null) { - mConnectHeadsetCallback.onConnectHeadsetResult(address, resultCode); - } - } - }; } diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 7e0226d2fd9..55ff27f97ec 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -26,4 +26,6 @@ interface IBluetoothA2dp { int disconnectSink(in String address); List listConnectedSinks(); int getSinkState(in String address); + int setSinkPriority(in String address, int priority); + int getSinkPriority(in String address); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 564861ff4c7..582d4e340ee 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.IBluetoothHeadsetCallback; - /** * System private API for Bluetooth Headset service * @@ -25,22 +23,12 @@ import android.bluetooth.IBluetoothHeadsetCallback; */ interface IBluetoothHeadset { int getState(); - String getHeadsetAddress(); - - // Request that the given headset be connected - // Assumes the given headset is already bonded - // Will disconnect any currently connected headset - // returns false if cannot start a connection (for example, there is - // already a pending connect). callback will always be called iff this - // returns true - boolean connectHeadset(in String address, in IBluetoothHeadsetCallback callback); - + boolean connectHeadset(in String address); void disconnectHeadset(); - boolean isConnected(in String address); - boolean startVoiceRecognition(); - boolean stopVoiceRecognition(); + boolean setPriority(in String address, int priority); + int getPriority(in String address); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl deleted file mode 100644 index 03e884b341c..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2008, 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; - -/** - * {@hide} - */ -oneway interface IBluetoothHeadsetCallback -{ - void onConnectHeadsetResult(in String address, int resultCode); -} -- GitLab From efc0179ef2bbdb95252f15fa24b2a2bf90e4b585 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 22 Jan 2009 00:13:42 -0800 Subject: [PATCH 0005/1408] auto import from //branches/cupcake/...@127436 --- .../android/bluetooth/BluetoothDevice.java | 102 +++++++++--------- .../android/bluetooth/BluetoothIntent.java | 23 ++-- .../android/bluetooth/IBluetoothDevice.aidl | 10 +- .../bluetooth/IBluetoothDeviceCallback.aidl | 1 - 4 files changed, 74 insertions(+), 62 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d1f71c55f05..d613e1cfba2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -39,6 +39,28 @@ public class BluetoothDevice { public static final int RESULT_FAILURE = -1; public static final int RESULT_SUCCESS = 0; + /** We do not have a link key for the remote device, and are therefore not + * bonded */ + public static final int BOND_NOT_BONDED = 0; + /** We have a link key for the remote device, and are probably bonded. */ + public static final int BOND_BONDED = 1; + /** We are currently attempting bonding */ + public static final int BOND_BONDING = 2; + + //TODO: Unify these result codes in BluetoothResult or BluetoothError + /** A bond attempt failed because pins did not match, or remote device did + * not respond to pin request in time */ + public static final int UNBOND_REASON_AUTH_FAILED = 1; + /** A bond attempt failed because the other side explicilty rejected + * bonding */ + public static final int UNBOND_REASON_AUTH_REJECTED = 2; + /** A bond attempt failed because we cancelled the bonding process */ + public static final int UNBOND_REASON_CANCELLED = 3; + /** A bond attempt failed because we could not contact the remote device */ + public static final int UNBOND_REASON_AUTH_REMOTE_DEVICE_DOWN = 4; + /** An existing bond was explicitly revoked */ + public static final int UNBOND_REASON_REMOVED = 5; + private static final String TAG = "BluetoothDevice"; private final IBluetoothDevice mService; @@ -325,42 +347,36 @@ public class BluetoothDevice { /** * Create a bonding with a remote bluetooth device. * - * This is an asynchronous call. BluetoothIntent.BONDING_CREATED_ACTION - * will be broadcast if and when the remote device is successfully bonded. + * This is an asynchronous call. The result of this bonding attempt can be + * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. * * @param address the remote device Bluetooth address. - * @return false if we cannot create a bonding to that device, true if - * there were no problems beginning the bonding process. + * @return false If there was an immediate problem creating the bonding, + * true otherwise. */ - public boolean createBonding(String address) { - return createBonding(address, null); + public boolean createBond(String address) { + try { + return mService.createBond(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; } /** - * Create a bonding with a remote bluetooth device. - * - * This is an asynchronous call. onCreateBondingResult() of your callback - * will be called when the call is complete, with either RESULT_SUCCESS or - * RESULT_FAILURE. - * - * In addition to the callback, BluetoothIntent.BONDING_CREATED_ACTION will - * be broadcast if the remote device is successfully bonded. - * - * @param address The remote device Bluetooth address. - * @param callback Your callback, null is ok. - * @return true if your callback was successfully registered, or false if - * there was an error, implying your callback will never be called. + * Cancel an in-progress bonding request started with createBond. */ - public boolean createBonding(String address, IBluetoothDeviceCallback callback) { + public boolean cancelBondProcess(String address) { try { - return mService.createBonding(address, callback); + return mService.cancelBondProcess(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean cancelBondingProcess(String address) { + /** + * Remove an already exisiting bonding (delete the link key). + */ + public boolean removeBond(String address) { try { - return mService.cancelBondingProcess(address); + return mService.removeBond(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -382,48 +398,34 @@ public class BluetoothDevice { * * This function does not check if the remote device is in range. * + * Remote devices that have an in-progress bonding attempt are not + * returned. + * * @return bluetooth hardware addresses of remote devices that are * bonded. Array size is 0 if no devices are bonded. Null on error. */ - public String[] listBondings() { + public String[] listBonds() { try { - return mService.listBondings(); + return mService.listBonds(); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } /** - * Check if a remote device is bonded (paired) to the local device. + * Get the bonding state of a remote device. * - * Bonding (pairing) is the process by which the user enters a pin code for - * the device, which generates a shared link key, allowing for - * authentication and encryption of future connections. In Android we - * require bonding before RFCOMM or SCO connections can be made to a remote - * device. - * - * This function checks if we have a link key with the remote device. It - * does not cause any RF transmission, and does not check if the remote - * device still has it's link key with us. If the other side no longer has - * a link key then the RFCOMM or SCO connection attempt will result in an - * error. - * - * This function does not check if the remote device is in range. + * Result is one of: + * BluetoothError.* + * BOND_* * * @param address Bluetooth hardware address of the remote device to check. - * @return true if bonded, false otherwise and on error. + * @return Result code */ - public boolean hasBonding(String address) { + public int getBondState(String address) { try { - return mService.hasBonding(address); + return mService.getBondState(address); } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public boolean removeBonding(String address) { - try { - return mService.removeBonding(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + return BluetoothError.ERROR_IPC; } public String getRemoteName(String address) { diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 8e227918903..57c46f98c29 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -45,6 +45,12 @@ public interface BluetoothIntent { "android.bluetooth.intent.HEADSET_STATE"; public static final String HEADSET_PREVIOUS_STATE = "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; + public static final String BOND_STATE = + "android.bluetooth.intent.BOND_STATE"; + public static final String BOND_PREVIOUS_STATE = + "android.bluetooth.intent.BOND_PREVIOUS_STATE"; + public static final String REASON = + "android.bluetooth.intent.REASON"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ENABLED_ACTION = @@ -105,12 +111,17 @@ public interface BluetoothIntent { public static final String REMOTE_ALIAS_CLEARED_ACTION = "android.bluetooth.intent.action.REMOTE_ALIAS_CLEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BONDING_CREATED_ACTION = - "android.bluetooth.intent.action.BONDING_CREATED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BONDING_REMOVED_ACTION = - "android.bluetooth.intent.action.BONDING_REMOVED"; + /** + * Broadcast when the bond state of a remote device changes. + * Has string extra ADDRESS and int extras BOND_STATE and + * BOND_PREVIOUS_STATE. + * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will + * also have an int extra REASON with a value of: + * BluetoothDevice.BOND_RESULT_* + * */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String BOND_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String HEADSET_STATE_CHANGED_ACTION = diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index e7cc8edc9c6..59f679f3cd6 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -57,11 +57,11 @@ interface IBluetoothDevice boolean isAclConnected(in String address); boolean disconnectRemoteDeviceAcl(in String address); - boolean createBonding(in String address, in IBluetoothDeviceCallback callback); - boolean cancelBondingProcess(in String address); - String[] listBondings(); - boolean hasBonding(in String address); - boolean removeBonding(in String address); + boolean createBond(in String address); + boolean cancelBondProcess(in String address); + boolean removeBond(in String address); + String[] listBonds(); + int getBondState(in String address); String getRemoteName(in String address); String getRemoteAlias(in String address); diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl index 86f44dde9ac..d25bd560d03 100644 --- a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl @@ -21,7 +21,6 @@ package android.bluetooth; */ oneway interface IBluetoothDeviceCallback { - void onCreateBondingResult(in String address, int result); void onGetRemoteServiceChannelResult(in String address, int channel); void onEnableResult(int result); -- GitLab From f42768f89b8ca48c1f66b15176e80fd04a02fa46 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 10 Feb 2009 15:44:00 -0800 Subject: [PATCH 0006/1408] auto import from //branches/cupcake/...@130745 --- .../android/bluetooth/BluetoothDevice.java | 97 +++++++------------ .../android/bluetooth/BluetoothHeadset.java | 10 +- .../android/bluetooth/BluetoothIntent.java | 19 ++-- .../java/android/bluetooth/HeadsetBase.java | 33 +------ .../android/bluetooth/IBluetoothDevice.aidl | 12 +-- 5 files changed, 59 insertions(+), 112 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d613e1cfba2..56b231fa6b1 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -31,10 +31,15 @@ import java.io.UnsupportedEncodingException; * @hide */ public class BluetoothDevice { - public static final int MODE_UNKNOWN = -1; - public static final int MODE_OFF = 0; - public static final int MODE_CONNECTABLE = 1; - public static final int MODE_DISCOVERABLE = 2; + /** Inquiry scan and page scan are both off. + * Device is neither discoverable nor connectable */ + public static final int SCAN_MODE_NONE = 0; + /** Page scan is on, inquiry scan is off. + * Device is connectable, but not discoverable */ + public static final int SCAN_MODE_CONNECTABLE = 1; + /** Page scan and inquiry scan are on. + * Device is connectable and discoverable */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; public static final int RESULT_FAILURE = -1; public static final int RESULT_SUCCESS = 0; @@ -54,10 +59,10 @@ public class BluetoothDevice { /** A bond attempt failed because the other side explicilty rejected * bonding */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we cancelled the bonding process */ - public static final int UNBOND_REASON_CANCELLED = 3; + /** A bond attempt failed because we canceled the bonding process */ + public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ - public static final int UNBOND_REASON_AUTH_REMOTE_DEVICE_DOWN = 4; + public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 5; @@ -174,18 +179,6 @@ public class BluetoothDevice { return false; } - public String getMajorClass() { - try { - return mService.getMajorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getMinorClass() { - try { - return mService.getMinorClass(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } public String getVersion() { try { return mService.getVersion(); @@ -211,15 +204,26 @@ public class BluetoothDevice { return null; } - public int getMode() { + /** + * Get the current scan mode. + * Used to determine if the local device is connectable and/or discoverable + * @return Scan mode, one of SCAN_MODE_* or an error code + */ + public int getScanMode() { try { - return mService.getMode(); + return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} - return MODE_UNKNOWN; + return BluetoothError.ERROR_IPC; } - public void setMode(int mode) { + + /** + * Set the current scan mode. + * Used to make the local device connectable and/or discoverable + * @param scanMode One of SCAN_MODE_* + */ + public void setScanMode(int scanMode) { try { - mService.setMode(mode); + mService.setScanMode(scanMode); } catch (RemoteException e) {Log.e(TAG, "", e);} } @@ -435,24 +439,6 @@ public class BluetoothDevice { return null; } - public String getRemoteAlias(String address) { - try { - return mService.getRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public boolean setRemoteAlias(String address, String alias) { - try { - return mService.setRemoteAlias(address, alias); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean clearRemoteAlias(String address) { - try { - return mService.clearRemoteAlias(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } public String getRemoteVersion(String address) { try { return mService.getRemoteVersion(address); @@ -477,24 +463,6 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String getRemoteMajorClass(String address) { - try { - return mService.getRemoteMajorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteMinorClass(String address) { - try { - return mService.getRemoteMinorClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String[] getRemoteServiceClasses(String address) { - try { - return mService.getRemoteServiceClasses(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } /** * Returns the RFCOMM channel associated with the 16-byte UUID on @@ -512,12 +480,19 @@ public class BluetoothDevice { return false; } + /** + * Get the major, minor and servics classes of a remote device. + * These classes are encoded as a 32-bit integer. See BluetoothClass. + * @param address remote device + * @return 32-bit class suitable for use with BluetoothClass. + */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothClass.ERROR; } + public byte[] getRemoteFeatures(String address) { try { return mService.getRemoteFeatures(address); @@ -576,8 +551,8 @@ public class BluetoothDevice { } - /* Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ private static final int ADDRESS_LENGTH = 17; + /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { return false; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c3152719bbb..34196bf8dea 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -69,8 +69,8 @@ public class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection cancelled before completetion. */ - public static final int RESULT_CANCELLED = 2; + /** Connection canceled before completetion. */ + public static final int RESULT_CANCELED = 2; /** Default priority for headsets that should be auto-connected */ public static final int PRIORITY_AUTO = 100; @@ -318,6 +318,12 @@ public class BluetoothHeadset { * @return True if this device might support HSP or HFP. */ public static boolean doesClassMatch(int btClass) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // Just in case they forgot the render service class switch (BluetoothClass.Device.getDevice(btClass)) { case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 57c46f98c29..b66b06ef59c 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -29,8 +29,8 @@ import android.annotation.SdkConstant.SdkConstantType; * @hide */ public interface BluetoothIntent { - public static final String MODE = - "android.bluetooth.intent.MODE"; + public static final String SCAN_MODE = + "android.bluetooth.intent.SCAN_MODE"; public static final String ADDRESS = "android.bluetooth.intent.ADDRESS"; public static final String NAME = @@ -62,9 +62,14 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NAME_CHANGED_ACTION = "android.bluetooth.intent.action.NAME_CHANGED"; + + /** + * Broadcast when the scan mode changes. Always contains an int extra + * named SCAN_MODE that contains the new scan mode. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.MODE_CHANGED"; + public static final String SCAN_MODE_CHANGED_ACTION = + "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DISCOVERY_STARTED_ACTION = @@ -104,12 +109,6 @@ public interface BluetoothIntent { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String REMOTE_NAME_FAILED_ACTION = "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CHANGED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CHANGED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_ALIAS_CLEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_ALIAS_CLEARED"; /** * Broadcast when the bond state of a remote device changes. diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index bce3388b6e9..fd2d2ab8848 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -21,13 +21,10 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; -import java.io.IOException; -import java.lang.Thread; - /** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. - * + * * The base RFCOMM (service) connection for a headset or handsfree device. * * In the future this class will be removed. @@ -90,7 +87,7 @@ public class HeadsetBase { /* Create from an already exisiting rfcomm connection */ public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { + int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); mBluetooth = bluetooth; @@ -132,30 +129,8 @@ public class HeadsetBase { */ protected void initializeAtParser() { mAtParser = new AtParser(); - - // Microphone Gain - mAtParser.register("+VGM", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGM= in range [0,15] - // Headset/Handsfree is reporting its current gain setting - //TODO: sync to android UI - //TODO: Send unsolicited +VGM when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); - - // Speaker Gain - mAtParser.register("+VGS", new AtCommandHandler() { - @Override - public AtCommandResult handleSetCommand(Object[] args) { - // AT+VGS= in range [0,15] - // Headset/Handsfree is reporting its current gain to Android - //TODO: sync to AG UI - //TODO: Send unsolicited +VGS when volume changed on AG - return new AtCommandResult(AtCommandResult.OK); - } - }); + //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, + //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree } public AtParser getAtParser() { diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index 59f679f3cd6..4351d2ebc38 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -32,15 +32,13 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getMajorClass(); - String getMinorClass(); String getVersion(); String getRevision(); String getManufacturer(); String getCompany(); - int getMode(); - boolean setMode(int mode); + int getScanMode(); + boolean setScanMode(int mode); int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); @@ -64,17 +62,11 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteAlias(in String address); - boolean setRemoteAlias(in String address, in String alias); - boolean clearRemoteAlias(in String address); String getRemoteVersion(in String address); String getRemoteRevision(in String address); int getRemoteClass(in String address); String getRemoteManufacturer(in String address); String getRemoteCompany(in String address); - String getRemoteMajorClass(in String address); - String getRemoteMinorClass(in String address); - String[] getRemoteServiceClasses(in String address); boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); byte[] getRemoteFeatures(in String adddress); String lastSeen(in String address); -- GitLab From 6917d2a1d73c76b3ed76350872c8133530dc5ba8 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 19 Feb 2009 10:57:31 -0800 Subject: [PATCH 0007/1408] auto import from //branches/cupcake/...@132276 --- .../java/android/bluetooth/ScoSocket.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java index 75b33296dd1..a43a08be105 100644 --- a/framework/java/android/bluetooth/ScoSocket.java +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -16,17 +16,12 @@ package android.bluetooth; -import android.content.Context; import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; -import java.io.IOException; -import java.lang.Thread; - - /** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. @@ -56,7 +51,7 @@ public class ScoSocket { private int mConnectedCode; private int mClosedCode; - private WakeLock mWakeLock; // held while STATE_CONNECTING or STATE_CONNECTED + private WakeLock mWakeLock; // held while in STATE_CONNECTING static { classInitNative(); @@ -130,6 +125,7 @@ public class ScoSocket { public synchronized void close() { if (DBG) log(this + " SCO OBJECT close() mState = " + mState); + acquireWakeLock(); mState = STATE_CLOSED; closeNative(); releaseWakeLock(); @@ -152,19 +148,16 @@ public class ScoSocket { mState = STATE_CLOSED; } mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); - if (result < 0) { - releaseWakeLock(); - } + releaseWakeLock(); } private synchronized void onAccepted(int result) { if (VDBG) log("onAccepted() " + this); if (mState != STATE_ACCEPT) { - if (DBG) log("Strange state" + this); + if (DBG) log("Strange state " + this); return; } if (result >= 0) { - acquireWakeLock(); mState = STATE_CONNECTED; } else { mState = STATE_CLOSED; @@ -184,13 +177,13 @@ public class ScoSocket { private void acquireWakeLock() { if (!mWakeLock.isHeld()) { mWakeLock.acquire(); - if (VDBG) log("mWakeLock.acquire()" + this); + if (VDBG) log("mWakeLock.acquire() " + this); } } private void releaseWakeLock() { if (mWakeLock.isHeld()) { - if (VDBG) log("mWakeLock.release()" + this); + if (VDBG) log("mWakeLock.release() " + this); mWakeLock.release(); } } -- GitLab From 3085d867aea6d4ef994e432e94eb29462cf27984 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Mon, 2 Mar 2009 22:54:33 -0800 Subject: [PATCH 0008/1408] auto import from //depot/cupcake/@137055 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 56b231fa6b1..1ba1c1e86f7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -63,8 +63,10 @@ public class BluetoothDevice { public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; + /** A bond attempt failed because a discovery is in progress */ + public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** An existing bond was explicitly revoked */ - public static final int UNBOND_REASON_REMOVED = 5; + public static final int UNBOND_REASON_REMOVED = 6; private static final String TAG = "BluetoothDevice"; -- GitLab From 005214a0b90668c0c4dc5670f9b159ed96c09150 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 14:04:24 -0800 Subject: [PATCH 0009/1408] auto import from //depot/cupcake/@132589 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1ba1c1e86f7..56b231fa6b1 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -63,10 +63,8 @@ public class BluetoothDevice { public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** A bond attempt failed because we could not contact the remote device */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** A bond attempt failed because a discovery is in progress */ - public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** An existing bond was explicitly revoked */ - public static final int UNBOND_REASON_REMOVED = 6; + public static final int UNBOND_REASON_REMOVED = 5; private static final String TAG = "BluetoothDevice"; -- GitLab From 3679e12ef106a04d71cda31dea9d7bd87a2523a5 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:45 -0800 Subject: [PATCH 0010/1408] auto import from //depot/cupcake/@135843 --- .../android/bluetooth/AtCommandHandler.java | 93 --- .../android/bluetooth/AtCommandResult.java | 117 --- .../java/android/bluetooth/AtParser.java | 370 ---------- .../java/android/bluetooth/BluetoothA2dp.java | 247 ------- .../bluetooth/BluetoothAudioGateway.java | 190 ----- .../android/bluetooth/BluetoothClass.java | 191 ----- .../android/bluetooth/BluetoothDevice.java | 578 --------------- .../android/bluetooth/BluetoothError.java | 42 -- .../android/bluetooth/BluetoothHeadset.java | 353 --------- .../android/bluetooth/BluetoothIntent.java | 128 ---- .../java/android/bluetooth/Database.java | 200 ------ .../java/android/bluetooth/HeadsetBase.java | 285 -------- .../android/bluetooth/IBluetoothA2dp.aidl | 31 - .../android/bluetooth/IBluetoothDevice.aidl | 77 -- .../bluetooth/IBluetoothDeviceCallback.aidl | 27 - .../android/bluetooth/IBluetoothHeadset.aidl | 34 - .../java/android/bluetooth/RfcommSocket.java | 674 ------------------ .../java/android/bluetooth/ScoSocket.java | 194 ----- framework/java/android/bluetooth/package.html | 13 - 19 files changed, 3844 deletions(-) delete mode 100644 framework/java/android/bluetooth/AtCommandHandler.java delete mode 100644 framework/java/android/bluetooth/AtCommandResult.java delete mode 100644 framework/java/android/bluetooth/AtParser.java delete mode 100644 framework/java/android/bluetooth/BluetoothA2dp.java delete mode 100644 framework/java/android/bluetooth/BluetoothAudioGateway.java delete mode 100644 framework/java/android/bluetooth/BluetoothClass.java delete mode 100644 framework/java/android/bluetooth/BluetoothDevice.java delete mode 100644 framework/java/android/bluetooth/BluetoothError.java delete mode 100644 framework/java/android/bluetooth/BluetoothHeadset.java delete mode 100644 framework/java/android/bluetooth/BluetoothIntent.java delete mode 100644 framework/java/android/bluetooth/Database.java delete mode 100644 framework/java/android/bluetooth/HeadsetBase.java delete mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothDevice.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadset.aidl delete mode 100644 framework/java/android/bluetooth/RfcommSocket.java delete mode 100644 framework/java/android/bluetooth/ScoSocket.java delete mode 100644 framework/java/android/bluetooth/package.html diff --git a/framework/java/android/bluetooth/AtCommandHandler.java b/framework/java/android/bluetooth/AtCommandHandler.java deleted file mode 100644 index 8de2133e494..00000000000 --- a/framework/java/android/bluetooth/AtCommandHandler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.AtCommandResult; - -/** - * Handler Interface for {@link AtParser}.

- * @hide - */ -public abstract class AtCommandHandler { - - /** - * Handle Basic commands "ATA".

- * These are single letter commands such as ATA and ATD. Anything following - * the single letter command ('A' and 'D' respectively) will be passed as - * 'arg'.

- * For example, "ATDT1234" would result in the call - * handleBasicCommand("T1234").

- * @param arg Everything following the basic command character. - * @return The result of this command. - */ - public AtCommandResult handleBasicCommand(String arg) { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Actions command "AT+FOO".

- * Action commands are part of the Extended command syntax, and are - * typically used to signal an action on "FOO".

- * @return The result of this command. - */ - public AtCommandResult handleActionCommand() { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Read command "AT+FOO?".

- * Read commands are part of the Extended command syntax, and are - * typically used to read the value of "FOO".

- * @return The result of this command. - */ - public AtCommandResult handleReadCommand() { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Set command "AT+FOO=...".

- * Set commands are part of the Extended command syntax, and are - * typically used to set the value of "FOO". Multiple arguments can be - * sent.

- * AT+FOO=[[,[,...]]]

- * Each argument will be either numeric (Integer) or String. - * handleSetCommand is passed a generic Object[] array in which each - * element will be an Integer (if it can be parsed with parseInt()) or - * String.

- * Missing arguments ",," are set to empty Strings.

- * @param args Array of String and/or Integer's. There will always be at - * least one element in this array. - * @return The result of this command. - */ - // Typically used to set this paramter - public AtCommandResult handleSetCommand(Object[] args) { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Test command "AT+FOO=?".

- * Test commands are part of the Extended command syntax, and are typically - * used to request an indication of the range of legal values that "FOO" - * can take.

- * By defualt we return an OK result, to indicate that this command is at - * least recognized.

- * @return The result of this command. - */ - public AtCommandResult handleTestCommand() { - return new AtCommandResult(AtCommandResult.OK); - } -} diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java deleted file mode 100644 index 638be2d2e99..00000000000 --- a/framework/java/android/bluetooth/AtCommandResult.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2007 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 java.util.*; - -/** - * The result of execution of an single AT command.

- * - * - * This class can represent the final response to an AT command line, and also - * intermediate responses to a single command within a chained AT command - * line.

- * - * The actual responses that are intended to be send in reply to the AT command - * line are stored in a string array. The final response is stored as an - * int enum, converted to a string when toString() is called. Only a single - * final response is sent from multiple commands chained into a single command - * line.

- * @hide - */ -public class AtCommandResult { - // Result code enumerations - public static final int OK = 0; - public static final int ERROR = 1; - public static final int UNSOLICITED = 2; - - private static final String OK_STRING = "OK"; - private static final String ERROR_STRING = "ERROR"; - - private int mResultCode; // Result code - private StringBuilder mResponse; // Response with CRLF line breaks - - /** - * Construct a new AtCommandResult with given result code, and an empty - * response array. - * @param resultCode One of OK, ERROR or UNSOLICITED. - */ - public AtCommandResult(int resultCode) { - mResultCode = resultCode; - mResponse = new StringBuilder(); - } - - /** - * Construct a new AtCommandResult with result code OK, and the specified - * single line response. - * @param response The single line response. - */ - public AtCommandResult(String response) { - this(OK); - addResponse(response); - } - - public int getResultCode() { - return mResultCode; - } - - /** - * Add another line to the response. - */ - public void addResponse(String response) { - appendWithCrlf(mResponse, response); - } - - /** - * Add the given result into this AtCommandResult object.

- * Used to combine results from multiple commands in a single command line - * (command chaining). - * @param result The AtCommandResult to add to this result. - */ - public void addResult(AtCommandResult result) { - if (result != null) { - appendWithCrlf(mResponse, result.mResponse.toString()); - mResultCode = result.mResultCode; - } - } - - /** - * Generate the string response ready to send - */ - public String toString() { - StringBuilder result = new StringBuilder(mResponse.toString()); - switch (mResultCode) { - case OK: - appendWithCrlf(result, OK_STRING); - break; - case ERROR: - appendWithCrlf(result, ERROR_STRING); - break; - } - return result.toString(); - } - - /** Append a string to a string builder, joining with a double - * CRLF. Used to create multi-line AT command replies - */ - public static void appendWithCrlf(StringBuilder str1, String str2) { - if (str1.length() > 0 && str2.length() > 0) { - str1.append("\r\n\r\n"); - } - str1.append(str2); - } -}; diff --git a/framework/java/android/bluetooth/AtParser.java b/framework/java/android/bluetooth/AtParser.java deleted file mode 100644 index 1ea31503bb7..00000000000 --- a/framework/java/android/bluetooth/AtParser.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.AtCommandHandler; -import android.bluetooth.AtCommandResult; - -import java.util.*; - -/** - * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard. - *

- * - * Conforment with the subset of V.250 required for implementation of the - * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP - * specifications. Also implements some V.250 features not required by - * Bluetooth - such as chained commands.

- * - * Command handlers are registered with an AtParser object. These handlers are - * invoked when command lines are processed by AtParser's process() method.

- * - * The AtParser object accepts a new command line to parse via its process() - * method. It breaks each command line into one or more commands. Each command - * is parsed for name, type, and (optional) arguments, and an appropriate - * external handler method is called through the AtCommandHandler interface. - * - * The command types are

    - *
  • Basic Command. For example "ATDT1234567890". Basic command names are a - * single character (e.g. "D"), and everything following this character is - * passed to the handler as a string argument (e.g. "T1234567890"). - *
  • Action Command. For example "AT+CIMI". The command name is "CIMI", and - * there are no arguments for action commands. - *
  • Read Command. For example "AT+VGM?". The command name is "VGM", and there - * are no arguments for get commands. - *
  • Set Command. For example "AT+VGM=14". The command name is "VGM", and - * there is a single integer argument in this case. In the general case then - * can be zero or more arguments (comma deliminated) each of integer or string - * form. - *
  • Test Command. For example "AT+VGM=?. No arguments. - *
- * - * In V.250 the last four command types are known as Extended Commands, and - * they are used heavily in Bluetooth.

- * - * Basic commands cannot be chained in this implementation. For Bluetooth - * headset/handsfree use this is acceptable, because they only use the basic - * commands ATA and ATD, which are not allowed to be chained. For general V.250 - * use we would need to improve this class to allow Basic command chaining - - * however its tricky to get right becuase there is no deliminator for Basic - * command chaining.

- * - * Extended commands can be chained. For example:

- * AT+VGM?;+VGM=14;+CIMI

- * This is equivalent to:

- * AT+VGM? - * AT+VGM=14 - * AT+CIMI - * Except that only one final result code is return (although several - * intermediate responses may be returned), and as soon as one command in the - * chain fails the rest are abandonded.

- * - * Handlers are registered by there command name via register(Char c, ...) or - * register(String s, ...). Handlers for Basic command should be registered by - * the basic command character, and handlers for Extended commands should be - * registered by String.

- * - * Refer to:

    - *
  • ITU-T Recommendation V.250 - *
  • ETSI TS 127.007 (AT Comannd set for User Equipment, 3GPP TS 27.007) - *
  • Bluetooth Headset Profile Spec (K6) - *
  • Bluetooth Handsfree Profile Spec (HFP 1.5) - *
- * @hide - */ -public class AtParser { - - // Extended command type enumeration, only used internally - private static final int TYPE_ACTION = 0; // AT+FOO - private static final int TYPE_READ = 1; // AT+FOO? - private static final int TYPE_SET = 2; // AT+FOO= - private static final int TYPE_TEST = 3; // AT+FOO=? - - private HashMap mExtHandlers; - private HashMap mBasicHandlers; - - private String mLastInput; // for "A/" (repeat last command) support - - /** - * Create a new AtParser.

- * No handlers are registered. - */ - public AtParser() { - mBasicHandlers = new HashMap(); - mExtHandlers = new HashMap(); - mLastInput = ""; - } - - /** - * Register a Basic command handler.

- * Basic command handlers are later called via their - * handleBasicCommand(String args) method. - * @param command Command name - a single character - * @param handler Handler to register - */ - public void register(Character command, AtCommandHandler handler) { - mBasicHandlers.put(command, handler); - } - - /** - * Register an Extended command handler.

- * Extended command handlers are later called via:

    - *
  • handleActionCommand() - *
  • handleGetCommand() - *
  • handleSetCommand() - *
  • handleTestCommand() - *
- * Only one method will be called for each command processed. - * @param command Command name - can be multiple characters - * @param handler Handler to register - */ - public void register(String command, AtCommandHandler handler) { - mExtHandlers.put(command, handler); - } - - - /** - * Strip input of whitespace and force Uppercase - except sections inside - * quotes. Also fixes unmatched quotes (by appending a quote). Double - * quotes " are the only quotes allowed by V.250 - */ - static private String clean(String input) { - StringBuilder out = new StringBuilder(input.length()); - - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '"') { - int j = input.indexOf('"', i + 1 ); // search for closing " - if (j == -1) { // unmatched ", insert one. - out.append(input.substring(i, input.length())); - out.append('"'); - break; - } - out.append(input.substring(i, j + 1)); - i = j; - } else if (c != ' ') { - out.append(Character.toUpperCase(c)); - } - } - - return out.toString(); - } - - static private boolean isAtoZ(char c) { - return (c >= 'A' && c <= 'Z'); - } - - /** - * Find a character ch, ignoring quoted sections. - * Return input.length() if not found. - */ - static private int findChar(char ch, String input, int fromIndex) { - for (int i = fromIndex; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '"') { - i = input.indexOf('"', i + 1); - if (i == -1) { - return input.length(); - } - } else if (c == ch) { - return i; - } - } - return input.length(); - } - - /** - * Break an argument string into individual arguments (comma deliminated). - * Integer arguments are turned into Integer objects. Otherwise a String - * object is used. - */ - static private Object[] generateArgs(String input) { - int i = 0; - int j; - ArrayList out = new ArrayList(); - while (i <= input.length()) { - j = findChar(',', input, i); - - String arg = input.substring(i, j); - try { - out.add(new Integer(arg)); - } catch (NumberFormatException e) { - out.add(arg); - } - - i = j + 1; // move past comma - } - return out.toArray(); - } - - /** - * Return the index of the end of character after the last characeter in - * the extended command name. Uses the V.250 spec for allowed command - * names. - */ - static private int findEndExtendedName(String input, int index) { - for (int i = index; i < input.length(); i++) { - char c = input.charAt(i); - - // V.250 defines the following chars as legal extended command - // names - if (isAtoZ(c)) continue; - if (c >= '0' && c <= '9') continue; - switch (c) { - case '!': - case '%': - case '-': - case '.': - case '/': - case ':': - case '_': - continue; - default: - return i; - } - } - return input.length(); - } - - /** - * Processes an incoming AT command line.

- * This method will invoke zero or one command handler methods for each - * command in the command line.

- * @param raw_input The AT input, without EOL deliminator (e.g. ). - * @return Result object for this command line. This can be - * converted to a String[] response with toStrings(). - */ - public AtCommandResult process(String raw_input) { - String input = clean(raw_input); - - // Handle "A/" (repeat previous line) - if (input.regionMatches(0, "A/", 0, 2)) { - input = new String(mLastInput); - } else { - mLastInput = new String(input); - } - - // Handle empty line - no response necessary - if (input.equals("")) { - // Return [] - return new AtCommandResult(AtCommandResult.UNSOLICITED); - } - - // Anything else deserves an error - if (!input.regionMatches(0, "AT", 0, 2)) { - // Return ["ERROR"] - return new AtCommandResult(AtCommandResult.ERROR); - } - - // Ok we have a command that starts with AT. Process it - int index = 2; - AtCommandResult result = - new AtCommandResult(AtCommandResult.UNSOLICITED); - while (index < input.length()) { - char c = input.charAt(index); - - if (isAtoZ(c)) { - // Option 1: Basic Command - // Pass the rest of the line as is to the handler. Do not - // look for any more commands on this line. - String args = input.substring(index + 1); - if (mBasicHandlers.containsKey((Character)c)) { - result.addResult(mBasicHandlers.get( - (Character)c).handleBasicCommand(args)); - return result; - } else { - // no handler - result.addResult( - new AtCommandResult(AtCommandResult.ERROR)); - return result; - } - // control never reaches here - } - - if (c == '+') { - // Option 2: Extended Command - // Search for first non-name character. Shortcircuit if we dont - // handle this command name. - int i = findEndExtendedName(input, index + 1); - String commandName = input.substring(index, i); - if (!mExtHandlers.containsKey(commandName)) { - // no handler - result.addResult( - new AtCommandResult(AtCommandResult.ERROR)); - return result; - } - AtCommandHandler handler = mExtHandlers.get(commandName); - - // Search for end of this command - this is usually the end of - // line - int endIndex = findChar(';', input, index); - - // Determine what type of command this is. - // Default to TYPE_ACTION if we can't find anything else - // obvious. - int type; - - if (i >= endIndex) { - type = TYPE_ACTION; - } else if (input.charAt(i) == '?') { - type = TYPE_READ; - } else if (input.charAt(i) == '=') { - if (i + 1 < endIndex) { - if (input.charAt(i + 1) == '?') { - type = TYPE_TEST; - } else { - type = TYPE_SET; - } - } else { - type = TYPE_SET; - } - } else { - type = TYPE_ACTION; - } - - // Call this command. Short-circuit as soon as a command fails - switch (type) { - case TYPE_ACTION: - result.addResult(handler.handleActionCommand()); - break; - case TYPE_READ: - result.addResult(handler.handleReadCommand()); - break; - case TYPE_TEST: - result.addResult(handler.handleTestCommand()); - break; - case TYPE_SET: - Object[] args = - generateArgs(input.substring(i + 1, endIndex)); - result.addResult(handler.handleSetCommand(args)); - break; - } - if (result.getResultCode() != AtCommandResult.OK) { - return result; // short-circuit - } - - index = endIndex; - } else { - // Can't tell if this is a basic or extended command. - // Push forwards and hope we hit something. - index++; - } - } - // Finished processing (and all results were ok) - return result; - } -} diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java deleted file mode 100644 index b0b0154abc9..00000000000 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2008 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.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.server.BluetoothA2dpService; -import android.content.Context; -import android.os.ServiceManager; -import android.os.RemoteException; -import android.os.IBinder; -import android.util.Log; - -import java.util.List; - -/** - * Public API for controlling the Bluetooth A2DP Profile Service. - * - * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP - * Service via IPC. - * - * Creating a BluetoothA2dp object will initiate a binding with the - * BluetoothHeadset service. Users of this object should call close() when they - * are finished, so that this proxy object can unbind from the service. - * - * Currently the BluetoothA2dp service runs in the system server and this - * proxy object will be immediately bound to the service on construction. - * However this may change in future releases, and error codes such as - * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the - * proxy object is not yet attached. - * - * Currently this class provides methods to connect to A2DP audio sinks. - * - * @hide - */ -public class BluetoothA2dp { - private static final String TAG = "BluetoothA2dp"; - - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_STATE = - "android.bluetooth.a2dp.intent.SINK_STATE"; - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_PREVIOUS_STATE = - "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; - - /** Indicates the state of an A2DP audio sink has changed. - * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SINK_STATE_CHANGED_ACTION = - "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; - - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; - /** Playing implies connected */ - public static final int STATE_PLAYING = 4; - - /** Default priority for a2dp devices that should allow incoming - * connections */ - public static final int PRIORITY_AUTO = 100; - /** Default priority for a2dp devices that should not allow incoming - * connections */ - public static final int PRIORITY_OFF = 0; - private final IBluetoothA2dp mService; - private final Context mContext; - - /** - * Create a BluetoothA2dp proxy object for interacting with the local - * Bluetooth A2DP service. - * @param c Context - */ - public BluetoothA2dp(Context c) { - mContext = c; - IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); - if (b == null) { - throw new RuntimeException("Bluetooth A2DP service not available!"); - } - mService = IBluetoothA2dp.Stub.asInterface(b); - } - - /** Initiate a connection to an A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when the - * connection is completed. - * @param address Remote BT address. - * @return Result code, negative indicates an immediate error. - * @hide - */ - public int connectSink(String address) { - try { - return mService.connectSink(address); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** Initiate disconnect from an A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when - * disconnect is completed. - * @param address Remote BT address. - * @return Result code, negative indicates an immediate error. - * @hide - */ - public int disconnectSink(String address) { - try { - return mService.disconnectSink(address); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** Check if a specified A2DP sink is connected. - * @param address Remote BT address. - * @return True if connected (or playing), false otherwise and on error. - * @hide - */ - public boolean isSinkConnected(String address) { - int state = getSinkState(address); - return state == STATE_CONNECTED || state == STATE_PLAYING; - } - - /** Check if any A2DP sink is connected. - * @return a List of connected A2DP sinks, or null on error. - * @hide - */ - public List listConnectedSinks() { - try { - return mService.listConnectedSinks(); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return null; - } - } - - /** Get the state of an A2DP sink - * @param address Remote BT address. - * @return State code, or negative on error - * @hide - */ - public int getSinkState(String address) { - try { - return mService.getSinkState(address); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** - * Set priority of a2dp sink. - * Priority is a non-negative integer. By default paired sinks will have - * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). - * Sinks with priority greater than zero will accept incoming connections - * (if no sink is currently connected). - * Priority for unpaired sink must be PRIORITY_NONE. - * @param address Paired sink - * @param priority Integer priority, for example PRIORITY_AUTO or - * PRIORITY_NONE - * @return Result code, negative indicates an error - */ - public int setSinkPriority(String address, int priority) { - try { - return mService.setSinkPriority(address, priority); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** - * Get priority of a2dp sink. - * @param address Sink - * @return non-negative priority, or negative error code on error. - */ - public int getSinkPriority(String address) { - try { - return mService.getSinkPriority(address); - } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** - * Check class bits for possible A2DP Sink support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might be a A2DP Sink. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might be a A2DP sink - */ - public static boolean doesClassMatchSink(int btClass) { - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // By the A2DP spec, sinks must indicate the RENDER service. - // However we found some that do not (Chordette). So lets also - // match on some other class bits. - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: - case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } - - /** Helper for converting a state to a string. - * For debug use only - strings are not internationalized. - * @hide - */ - public static String stateToString(int state) { - switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - case STATE_PLAYING: - return "playing"; - default: - return ""; - } - } -} diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java deleted file mode 100644 index f3afd2a4a0b..00000000000 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ /dev/null @@ -1,190 +0,0 @@ -package android.bluetooth; - -import java.lang.Thread; - -import android.os.Message; -import android.os.Handler; -import android.util.Log; - -/** - * Listen's for incoming RFCOMM connection for the headset / handsfree service. - * - * This class is planned for deletion, in favor of a generic Rfcomm class. - * - * @hide - */ -public class BluetoothAudioGateway { - private static final String TAG = "BT Audio Gateway"; - private static final boolean DBG = false; - - private int mNativeData; - static { classInitNative(); } - - private BluetoothDevice mBluetooth; - - /* in */ - private int mHandsfreeAgRfcommChannel = -1; - private int mHeadsetAgRfcommChannel = -1; - - /* out */ - private String mConnectingHeadsetAddress; - private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ - private int mConnectingHeadsetSocketFd; - private String mConnectingHandsfreeAddress; - private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ - private int mConnectingHandsfreeSocketFd; - private int mTimeoutRemainingMs; /* in/out */ - - public static final int DEFAULT_HF_AG_CHANNEL = 10; - public static final int DEFAULT_HS_AG_CHANNEL = 11; - - public BluetoothAudioGateway(BluetoothDevice bluetooth) { - this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); - } - - public BluetoothAudioGateway(BluetoothDevice bluetooth, - int handsfreeAgRfcommChannel, - int headsetAgRfcommChannel) { - mBluetooth = bluetooth; - mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; - mHeadsetAgRfcommChannel = headsetAgRfcommChannel; - initializeNativeDataNative(); - } - - private Thread mConnectThead; - private volatile boolean mInterrupted; - private static final int SELECT_WAIT_TIMEOUT = 1000; - - private Handler mCallback; - - public class IncomingConnectionInfo { - IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChan) { - mBluetooth = bluetooth; - mAddress = address; - mSocketFd = socketFd; - mRfcommChan = rfcommChan; - } - - public BluetoothDevice mBluetooth; - public String mAddress; - public int mSocketFd; - public int mRfcommChan; - } - - public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; - public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101; - - public synchronized boolean start(Handler callback) { - - if (mConnectThead == null) { - mCallback = callback; - mConnectThead = new Thread(TAG) { - public void run() { - if (DBG) log("Connect Thread starting"); - while (!mInterrupted) { - //Log.i(TAG, "waiting for connect"); - mConnectingHeadsetRfcommChannel = -1; - mConnectingHandsfreeRfcommChannel = -1; - if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) { - if (mTimeoutRemainingMs > 0) { - try { - Log.i(TAG, "select thread timed out, but " + - mTimeoutRemainingMs + "ms of waiting remain."); - Thread.sleep(mTimeoutRemainingMs); - } catch (InterruptedException e) { - Log.i(TAG, "select thread was interrupted (2), exiting"); - mInterrupted = true; - } - } - } - else { - Log.i(TAG, "connect notification!"); - /* A device connected (most likely just one, but - it is possible for two separate devices, one - a headset and one a handsfree, to connect - simultaneously. - */ - if (mConnectingHeadsetRfcommChannel >= 0) { - Log.i(TAG, "Incoming connection from headset " + - mConnectingHeadsetAddress + " on channel " + - mConnectingHeadsetRfcommChannel); - Message msg = Message.obtain(mCallback); - msg.what = MSG_INCOMING_HEADSET_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHeadsetAddress, - mConnectingHeadsetSocketFd, - mConnectingHeadsetRfcommChannel); - msg.sendToTarget(); - } - if (mConnectingHandsfreeRfcommChannel >= 0) { - Log.i(TAG, "Incoming connection from handsfree " + - mConnectingHandsfreeAddress + " on channel " + - mConnectingHandsfreeRfcommChannel); - Message msg = Message.obtain(); - msg.setTarget(mCallback); - msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHandsfreeAddress, - mConnectingHandsfreeSocketFd, - mConnectingHandsfreeRfcommChannel); - msg.sendToTarget(); - } - } - } - if (DBG) log("Connect Thread finished"); - } - }; - - if (setUpListeningSocketsNative() == false) { - Log.e(TAG, "Could not set up listening socket, exiting"); - return false; - } - - mInterrupted = false; - mConnectThead.start(); - } - - return true; - } - - public synchronized void stop() { - if (mConnectThead != null) { - if (DBG) log("stopping Connect Thread"); - mInterrupted = true; - try { - mConnectThead.interrupt(); - if (DBG) log("waiting for thread to terminate"); - mConnectThead.join(); - mConnectThead = null; - mCallback = null; - tearDownListeningSocketsNative(); - } catch (InterruptedException e) { - Log.w(TAG, "Interrupted waiting for Connect Thread to join"); - } - } - } - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private static native void classInitNative(); - private native void initializeNativeDataNative(); - private native void cleanupNativeDataNative(); - private native boolean waitForHandsfreeConnectNative(int timeoutMs); - private native boolean setUpListeningSocketsNative(); - private native void tearDownListeningSocketsNative(); - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java deleted file mode 100644 index 88ce18b4577..00000000000 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Static helper methods and constants to decode the device class bit vector - * returned by the Bluetooth API. - * - * The Android Bluetooth API returns a 32-bit integer to represent the class. - * The format of these bits is defined at - * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class provides static helper methods and constants to - * determine what Service Class(es) and Device Class are encoded in the 32-bit - * class. - * - * Devices typically have zero or more service classes, and exactly one device - * class. The device class is encoded as a major and minor device class, the - * minor being a subset of the major. - * - * Class is useful to describe a device (for example to show an icon), - * but does not reliably describe what profiles a device supports. To determine - * profile support you usually need to perform SDP queries. - * - * Each of these helper methods takes the 32-bit integer class as an argument. - * - * @hide - */ -public class BluetoothClass { - /** Indicates the Bluetooth API could not retrieve the class */ - public static final int ERROR = 0xFF000000; - - /** Every Bluetooth device has zero or more service classes */ - public static class Service { - public static final int BITMASK = 0xFFE000; - - public static final int LIMITED_DISCOVERABILITY = 0x002000; - public static final int POSITIONING = 0x010000; - public static final int NETWORKING = 0x020000; - public static final int RENDER = 0x040000; - public static final int CAPTURE = 0x080000; - public static final int OBJECT_TRANSFER = 0x100000; - public static final int AUDIO = 0x200000; - public static final int TELEPHONY = 0x400000; - public static final int INFORMATION = 0x800000; - - /** Returns true if the given class supports the given Service Class. - * A bluetooth device can claim to support zero or more service classes. - * @param btClass The bluetooth class. - * @param serviceClass The service class constant to test for. For - * example, Service.AUDIO. Must be one of the - * Service.FOO constants. - * @return True if the service class is supported. - */ - public static boolean hasService(int btClass, int serviceClass) { - if (btClass == ERROR) { - return false; - } - return ((btClass & Service.BITMASK & serviceClass) != 0); - } - } - - /** Every Bluetooth device has exactly one device class, comprimised of - * major and minor components. We have not included the minor classes for - * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work - * a little differently. */ - public static class Device { - public static final int BITMASK = 0x1FFC; - - public static class Major { - public static final int BITMASK = 0x1F00; - - public static final int MISC = 0x0000; - public static final int COMPUTER = 0x0100; - public static final int PHONE = 0x0200; - public static final int NETWORKING = 0x0300; - public static final int AUDIO_VIDEO = 0x0400; - public static final int PERIPHERAL = 0x0500; - public static final int IMAGING = 0x0600; - public static final int WEARABLE = 0x0700; - public static final int TOY = 0x0800; - public static final int HEALTH = 0x0900; - public static final int UNCATEGORIZED = 0x1F00; - - /** Returns the Major Device Class component of a bluetooth class. - * Values returned from this function can be compared with the constants - * Device.Major.FOO. A bluetooth device can only be associated - * with one major class. - */ - public static int getDeviceMajor(int btClass) { - if (btClass == ERROR) { - return ERROR; - } - return (btClass & Device.Major.BITMASK); - } - } - - // Devices in the COMPUTER major class - public static final int COMPUTER_UNCATEGORIZED = 0x0100; - public static final int COMPUTER_DESKTOP = 0x0104; - public static final int COMPUTER_SERVER = 0x0108; - public static final int COMPUTER_LAPTOP = 0x010C; - public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; - public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; - public static final int COMPUTER_WEARABLE = 0x0118; - - // Devices in the PHONE major class - public static final int PHONE_UNCATEGORIZED = 0x0200; - public static final int PHONE_CELLULAR = 0x0204; - public static final int PHONE_CORDLESS = 0x0208; - public static final int PHONE_SMART = 0x020C; - public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; - public static final int PHONE_ISDN = 0x0214; - - // Minor classes for the AUDIO_VIDEO major class - public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; - public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; - public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; - //public static final int AUDIO_VIDEO_RESERVED = 0x040C; - public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; - public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; - public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; - public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; - public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; - public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; - public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; - public static final int AUDIO_VIDEO_VCR = 0x042C; - public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; - public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; - public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; - public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; - public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; - //public static final int AUDIO_VIDEO_RESERVED = 0x0444; - public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; - - // Devices in the WEARABLE major class - public static final int WEARABLE_UNCATEGORIZED = 0x0700; - public static final int WEARABLE_WRIST_WATCH = 0x0704; - public static final int WEARABLE_PAGER = 0x0708; - public static final int WEARABLE_JACKET = 0x070C; - public static final int WEARABLE_HELMET = 0x0710; - public static final int WEARABLE_GLASSES = 0x0714; - - // Devices in the TOY major class - public static final int TOY_UNCATEGORIZED = 0x0800; - public static final int TOY_ROBOT = 0x0804; - public static final int TOY_VEHICLE = 0x0808; - public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; - public static final int TOY_CONTROLLER = 0x0810; - public static final int TOY_GAME = 0x0814; - - // Devices in the HEALTH major class - public static final int HEALTH_UNCATEGORIZED = 0x0900; - public static final int HEALTH_BLOOD_PRESSURE = 0x0904; - public static final int HEALTH_THERMOMETER = 0x0908; - public static final int HEALTH_WEIGHING = 0x090C; - public static final int HEALTH_GLUCOSE = 0x0910; - public static final int HEALTH_PULSE_OXIMETER = 0x0914; - public static final int HEALTH_PULSE_RATE = 0x0918; - public static final int HEALTH_DATA_DISPLAY = 0x091C; - - /** Returns the Device Class component of a bluetooth class. This includes - * both the major and minor device components. Values returned from this - * function can be compared with the constants Device.FOO. A bluetooth - * device can only be associated with one device class. - */ - public static int getDevice(int btClass) { - if (btClass == ERROR) { - return ERROR; - } - return (btClass & Device.BITMASK); - } - } -} - diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java deleted file mode 100644 index 56b231fa6b1..00000000000 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Copyright (C) 2008 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.os.RemoteException; -import android.util.Log; - -import java.io.UnsupportedEncodingException; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. - * - * @hide - */ -public class BluetoothDevice { - /** Inquiry scan and page scan are both off. - * Device is neither discoverable nor connectable */ - public static final int SCAN_MODE_NONE = 0; - /** Page scan is on, inquiry scan is off. - * Device is connectable, but not discoverable */ - public static final int SCAN_MODE_CONNECTABLE = 1; - /** Page scan and inquiry scan are on. - * Device is connectable and discoverable */ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; - - public static final int RESULT_FAILURE = -1; - public static final int RESULT_SUCCESS = 0; - - /** We do not have a link key for the remote device, and are therefore not - * bonded */ - public static final int BOND_NOT_BONDED = 0; - /** We have a link key for the remote device, and are probably bonded. */ - public static final int BOND_BONDED = 1; - /** We are currently attempting bonding */ - public static final int BOND_BONDING = 2; - - //TODO: Unify these result codes in BluetoothResult or BluetoothError - /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time */ - public static final int UNBOND_REASON_AUTH_FAILED = 1; - /** A bond attempt failed because the other side explicilty rejected - * bonding */ - public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we canceled the bonding process */ - public static final int UNBOND_REASON_AUTH_CANCELED = 3; - /** A bond attempt failed because we could not contact the remote device */ - public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** An existing bond was explicitly revoked */ - public static final int UNBOND_REASON_REMOVED = 5; - - private static final String TAG = "BluetoothDevice"; - - private final IBluetoothDevice mService; - /** - * @hide - hide this because it takes a parameter of type - * IBluetoothDevice, which is a System private class. - * Also note that Context.getSystemService is a factory that - * returns a BlueToothDevice. That is the right way to get - * a BluetoothDevice. - */ - public BluetoothDevice(IBluetoothDevice service) { - mService = service; - } - - /** - * Get the current status of Bluetooth hardware. - * - * @return true if Bluetooth enabled, false otherwise. - */ - public boolean isEnabled() { - try { - return mService.isEnabled(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Enable the Bluetooth device. - * Turn on the underlying hardware. - * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be - * sent if and when the device is successfully enabled. - * @return false if we cannot enable the Bluetooth device. True does not - * imply the device was enabled, it only implies that so far there were no - * problems. - */ - public boolean enable() { - return enable(null); - } - - /** - * Enable the Bluetooth device. - * Turns on the underlying hardware. - * This is an asynchronous call. onEnableResult() of your callback will be - * called when the call is complete, with either RESULT_SUCCESS or - * RESULT_FAILURE. - * - * Your callback will be called from a binder thread, not the main thread. - * - * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be - * broadcast if the device is successfully enabled. - * - * @param callback Your callback, null is ok. - * @return true if your callback was successfully registered, or false if - * there was an error, implying your callback will never be called. - */ - public boolean enable(IBluetoothDeviceCallback callback) { - try { - return mService.enable(callback); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Disable the Bluetooth device. - * This turns off the underlying hardware. - * - * @return true if successful, false otherwise. - */ - public boolean disable() { - try { - return mService.disable(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String getAddress() { - try { - return mService.getAddress(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Get the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. Currently it is only - * possible to retrieve the Bluetooth name when Bluetooth is enabled. - * - * @return the Bluetooth name, or null if there was a problem. - */ - public String getName() { - try { - return mService.getName(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Set the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. The Bluetooth Service - * is responsible for persisting this name. - * - * @param name the name to set - * @return true, if the name was successfully set. False otherwise. - */ - public boolean setName(String name) { - try { - return mService.setName(name); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String getVersion() { - try { - return mService.getVersion(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRevision() { - try { - return mService.getRevision(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getManufacturer() { - try { - return mService.getManufacturer(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getCompany() { - try { - return mService.getCompany(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Get the current scan mode. - * Used to determine if the local device is connectable and/or discoverable - * @return Scan mode, one of SCAN_MODE_* or an error code - */ - public int getScanMode() { - try { - return mService.getScanMode(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; - } - - /** - * Set the current scan mode. - * Used to make the local device connectable and/or discoverable - * @param scanMode One of SCAN_MODE_* - */ - public void setScanMode(int scanMode) { - try { - mService.setScanMode(scanMode); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public int getDiscoverableTimeout() { - try { - return mService.getDiscoverableTimeout(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return -1; - } - public void setDiscoverableTimeout(int timeout) { - try { - mService.setDiscoverableTimeout(timeout); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public boolean startDiscovery() { - return startDiscovery(true); - } - public boolean startDiscovery(boolean resolveNames) { - try { - return mService.startDiscovery(resolveNames); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public void cancelDiscovery() { - try { - mService.cancelDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public boolean isDiscovering() { - try { - return mService.isDiscovering(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public boolean startPeriodicDiscovery() { - try { - return mService.startPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean stopPeriodicDiscovery() { - try { - return mService.stopPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean isPeriodicDiscovery() { - try { - return mService.isPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String[] listRemoteDevices() { - try { - return mService.listRemoteDevices(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * List remote devices that have a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @return bluetooth hardware addresses of remote devices with a current - * ACL connection. Array size is 0 if no devices have a - * connection. Null on error. - */ - public String[] listAclConnections() { - try { - return mService.listAclConnections(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Check if a specified remote device has a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @param address the Bluetooth hardware address you want to check. - * @return true if there is an ACL connection, false otherwise and on - * error. - */ - public boolean isAclConnected(String address) { - try { - return mService.isAclConnected(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Perform a low level (ACL) disconnection of a remote device. - * - * This forcably disconnects the ACL layer connection to a remote device, - * which will cause all RFCOMM, SDP and L2CAP connections to this remote - * device to close. - * - * @param address the Bluetooth hardware address you want to disconnect. - * @return true if the device was disconnected, false otherwise and on - * error. - */ - public boolean disconnectRemoteDeviceAcl(String address) { - try { - return mService.disconnectRemoteDeviceAcl(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Create a bonding with a remote bluetooth device. - * - * This is an asynchronous call. The result of this bonding attempt can be - * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. - * - * @param address the remote device Bluetooth address. - * @return false If there was an immediate problem creating the bonding, - * true otherwise. - */ - public boolean createBond(String address) { - try { - return mService.createBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Cancel an in-progress bonding request started with createBond. - */ - public boolean cancelBondProcess(String address) { - try { - return mService.cancelBondProcess(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Remove an already exisiting bonding (delete the link key). - */ - public boolean removeBond(String address) { - try { - return mService.removeBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * List remote devices that are bonded (paired) to the local device. - * - * Bonding (pairing) is the process by which the user enters a pin code for - * the device, which generates a shared link key, allowing for - * authentication and encryption of future connections. In Android we - * require bonding before RFCOMM or SCO connections can be made to a remote - * device. - * - * This function lists which remote devices we have a link key for. It does - * not cause any RF transmission, and does not check if the remote device - * still has it's link key with us. If the other side no longer has its - * link key then the RFCOMM or SCO connection attempt will result in an - * error. - * - * This function does not check if the remote device is in range. - * - * Remote devices that have an in-progress bonding attempt are not - * returned. - * - * @return bluetooth hardware addresses of remote devices that are - * bonded. Array size is 0 if no devices are bonded. Null on error. - */ - public String[] listBonds() { - try { - return mService.listBonds(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Get the bonding state of a remote device. - * - * Result is one of: - * BluetoothError.* - * BOND_* - * - * @param address Bluetooth hardware address of the remote device to check. - * @return Result code - */ - public int getBondState(String address) { - try { - return mService.getBondState(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; - } - - public String getRemoteName(String address) { - try { - return mService.getRemoteName(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - public String getRemoteVersion(String address) { - try { - return mService.getRemoteVersion(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteRevision(String address) { - try { - return mService.getRemoteRevision(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteManufacturer(String address) { - try { - return mService.getRemoteManufacturer(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteCompany(String address) { - try { - return mService.getRemoteCompany(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Returns the RFCOMM channel associated with the 16-byte UUID on - * the remote Bluetooth address. - * - * Performs a SDP ServiceSearchAttributeRequest transaction. The provided - * uuid is verified in the returned record. If there was a problem, or the - * specified uuid does not exist, -1 is returned. - */ - public boolean getRemoteServiceChannel(String address, short uuid16, - IBluetoothDeviceCallback callback) { - try { - return mService.getRemoteServiceChannel(address, uuid16, callback); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Get the major, minor and servics classes of a remote device. - * These classes are encoded as a 32-bit integer. See BluetoothClass. - * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass. - */ - public int getRemoteClass(String address) { - try { - return mService.getRemoteClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothClass.ERROR; - } - - public byte[] getRemoteFeatures(String address) { - try { - return mService.getRemoteFeatures(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastSeen(String address) { - try { - return mService.lastSeen(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastUsed(String address) { - try { - return mService.lastUsed(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - public boolean setPin(String address, byte[] pin) { - try { - return mService.setPin(address, pin); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean cancelPin(String address) { - try { - return mService.cancelPin(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Check that a pin is valid and convert to byte array. - * - * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. - * @param pin pin as java String - * @return the pin code as a UTF8 byte array, or null if it is an invalid - * Bluetooth pin. - */ - public static byte[] convertPinToBytes(String pin) { - if (pin == null) { - return null; - } - byte[] pinBytes; - try { - pinBytes = pin.getBytes("UTF8"); - } catch (UnsupportedEncodingException uee) { - Log.e(TAG, "UTF8 not supported?!?"); // this should not happen - return null; - } - if (pinBytes.length <= 0 || pinBytes.length > 16) { - return null; - } - return pinBytes; - } - - - private static final int ADDRESS_LENGTH = 17; - /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ - public static boolean checkBluetoothAddress(String address) { - if (address == null || address.length() != ADDRESS_LENGTH) { - return false; - } - for (int i = 0; i < ADDRESS_LENGTH; i++) { - char c = address.charAt(i); - switch (i % 3) { - case 0: - case 1: - if (Character.digit(c, 16) != -1) { - break; // hex character, OK - } - return false; - case 2: - if (c == ':') { - break; // OK - } - return false; - } - } - return true; - } -} diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/BluetoothError.java deleted file mode 100644 index 2554bead0ca..00000000000 --- a/framework/java/android/bluetooth/BluetoothError.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * Bluetooth API error codes. - * - * Errors are always negative. - * - * @hide - */ -public class BluetoothError { - /** No error */ - public static final int SUCCESS = 0; - - /** Generic error */ - public static final int ERROR = -1000; - - /** Bluetooth currently disabled */ - public static final int ERROR_DISABLED = -1001; - - /** IPC is not ready, for example service is not yet bound */ - public static final int ERROR_IPC_NOT_READY = -1011; - - /** Some other IPC error, for example a RemoteException */ - public static final int ERROR_IPC = -1012; - -} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java deleted file mode 100644 index 34196bf8dea..00000000000 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2008 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.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.RemoteException; -import android.os.IBinder; -import android.util.Log; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Public API for controlling the Bluetooth Headset Service. This includes both - * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will - * attempt a handsfree connection first, and fall back to headset. - * - * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset - * Service via IPC. - * - * Creating a BluetoothHeadset object will create a binding with the - * BluetoothHeadset service. Users of this object should call close() when they - * are finished with the BluetoothHeadset, so that this proxy object can unbind - * from the service. - * - * This BluetoothHeadset object is not immediately bound to the - * BluetoothHeadset service. Use the ServiceListener interface to obtain a - * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetootHeadset after construction. - * - * Android only supports one connected Bluetooth Headset at a time. - * - * @hide - */ -public class BluetoothHeadset { - - private static final String TAG = "BluetoothHeadset"; - private static final boolean DBG = false; - - private IBluetoothHeadset mService; - private final Context mContext; - private final ServiceListener mServiceListener; - - /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; - /** No headset currently connected */ - public static final int STATE_DISCONNECTED = 0; - /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; - /** A headset is currently connected */ - public static final int STATE_CONNECTED = 2; - - public static final int RESULT_FAILURE = 0; - public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completetion. */ - public static final int RESULT_CANCELED = 2; - - /** Default priority for headsets that should be auto-connected */ - public static final int PRIORITY_AUTO = 100; - /** Default priority for headsets that should not be auto-connected */ - public static final int PRIORITY_OFF = 0; - - /** - * An interface for notifying BluetoothHeadset IPC clients when they have - * been connected to the BluetoothHeadset service. - */ - public interface ServiceListener { - /** - * Called to notify the client when this proxy object has been - * connected to the BluetoothHeadset service. Clients must wait for - * this callback before making IPC calls on the BluetoothHeadset - * service. - */ - public void onServiceConnected(); - - /** - * Called to notify the client that this proxy object has been - * disconnected from the BluetoothHeadset service. Clients must not - * make IPC calls on the BluetoothHeadset service after this callback. - * This callback will currently only occur if the application hosting - * the BluetoothHeadset service, but may be called more often in future. - */ - public void onServiceDisconnected(); - } - - /** - * Create a BluetoothHeadset proxy object. - */ - public BluetoothHeadset(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; - if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Service"); - } - } - - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothHeadset will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - */ - public synchronized void close() { - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; - } - } - - /** - * Get the current state of the Bluetooth Headset service. - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Headset service. - */ - public int getState() { - if (mService != null) { - try { - return mService.getState(); - } 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 BluetoothHeadset.STATE_ERROR; - } - - /** - * Get the Bluetooth address of the current headset. - * @return The Bluetooth address, or null if not in connected or connecting - * state, or if this proxy object is not connected to the Headset - * service. - */ - public String getHeadsetAddress() { - if (mService != null) { - try { - return mService.getHeadsetAddress(); - } 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 null; - } - - /** - * Request to initiate a connection to a headset. - * This call does not block. Fails if a headset is already connecting - * or connected. - * Initiates auto-connection if address is null. Tries to connect to all - * devices with priority greater than PRIORITY_AUTO in descending order. - * @param address The Bluetooth Address to connect to, or null to - * auto-connect to the last connected headset. - * @return False if there was a problem initiating the connection - * procedure, and no further HEADSET_STATE_CHANGED intents - * will be expected. - */ - public boolean connectHeadset(String address) { - if (mService != null) { - try { - if (mService.connectHeadset(address)) { - return true; - } - } 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; - } - - /** - * Returns true if the specified headset is connected (does not include - * connecting). Returns false if not connected, or if this proxy object - * if not currently connected to the headset service. - */ - public boolean isConnected(String address) { - if (mService != null) { - try { - return mService.isConnected(address); - } 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; - } - - /** - * Disconnects the current headset. Currently this call blocks, it may soon - * be made asynchornous. Returns false if this proxy object is - * not currently connected to the Headset service. - */ - public boolean disconnectHeadset() { - if (mService != null) { - try { - mService.disconnectHeadset(); - return true; - } 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; - } - - /** - * Start BT Voice Recognition mode, and set up Bluetooth audio path. - * Returns false if there is no headset connected, or if the - * connected headset does not support voice recognition, or on - * error. - */ - public boolean startVoiceRecognition() { - if (mService != null) { - try { - return mService.startVoiceRecognition(); - } 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; - } - - /** - * Stop BT Voice Recognition mode, and shut down Bluetooth audio path. - * Returns false if there is no headset connected, or the connected - * headset is not in voice recognition mode, or on error. - */ - public boolean stopVoiceRecognition() { - if (mService != null) { - try { - return mService.stopVoiceRecognition(); - } 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; - } - - /** - * Set priority of headset. - * Priority is a non-negative integer. By default paired headsets will have - * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). - * Headsets with priority greater than zero will be auto-connected, and - * incoming connections will be accepted (if no other headset is - * connected). - * Auto-connection occurs at the following events: boot, incoming phone - * call, outgoing phone call. - * Headsets with priority equal to zero, or that are unpaired, are not - * auto-connected. - * Incoming connections are ignored regardless of priority if there is - * already a headset connected. - * @param address Paired headset - * @param priority Integer priority, for example PRIORITY_AUTO or - * PRIORITY_NONE - * @return True if successful, false if there was some error. - */ - public boolean setPriority(String address, int priority) { - if (mService != null) { - try { - return mService.setPriority(address, priority); - } 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; - } - - /** - * Get priority of headset. - * @param address Headset - * @return non-negative priority, or negative error code on error. - */ - public int getPriority(String address) { - if (mService != null) { - try { - return mService.getPriority(address); - } 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 -1; - } - - /** - * Check class bits for possible HSP or HFP support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support HSP or HFP. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might support HSP or HFP. - */ - public static boolean doesClassMatch(int btClass) { - // The render service class is required by the spec for HFP, so is a - // pretty good signal - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // Just in case they forgot the render service class - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } - - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadset.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(); - } - } - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(); - } - } - }; -} diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java deleted file mode 100644 index b66b06ef59c..00000000000 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2008 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.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. - * - * @hide - */ -public interface BluetoothIntent { - public static final String SCAN_MODE = - "android.bluetooth.intent.SCAN_MODE"; - public static final String ADDRESS = - "android.bluetooth.intent.ADDRESS"; - public static final String NAME = - "android.bluetooth.intent.NAME"; - public static final String ALIAS = - "android.bluetooth.intent.ALIAS"; - public static final String RSSI = - "android.bluetooth.intent.RSSI"; - public static final String CLASS = - "android.bluetooth.intent.CLASS"; - public static final String HEADSET_STATE = - "android.bluetooth.intent.HEADSET_STATE"; - public static final String HEADSET_PREVIOUS_STATE = - "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; - public static final String BOND_STATE = - "android.bluetooth.intent.BOND_STATE"; - public static final String BOND_PREVIOUS_STATE = - "android.bluetooth.intent.BOND_PREVIOUS_STATE"; - public static final String REASON = - "android.bluetooth.intent.REASON"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ENABLED_ACTION = - "android.bluetooth.intent.action.ENABLED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISABLED_ACTION = - "android.bluetooth.intent.action.DISABLED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String NAME_CHANGED_ACTION = - "android.bluetooth.intent.action.NAME_CHANGED"; - - /** - * Broadcast when the scan mode changes. Always contains an int extra - * named SCAN_MODE that contains the new scan mode. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SCAN_MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_STARTED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_STARTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_COMPLETED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_COMPLETED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_REQUEST_ACTION = - "android.bluetooth.intent.action.PAIRING_REQUEST"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_CANCEL_ACTION = - "android.bluetooth.intent.action.PAIRING_CANCEL"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_FOUND_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISAPPEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_UPDATED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_FAILED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - - /** - * Broadcast when the bond state of a remote device changes. - * Has string extra ADDRESS and int extras BOND_STATE and - * BOND_PREVIOUS_STATE. - * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will - * also have an int extra REASON with a value of: - * BluetoothDevice.BOND_RESULT_* - * */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BOND_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String HEADSET_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; -} diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java deleted file mode 100644 index fef641a5f7c..00000000000 --- a/framework/java/android/bluetooth/Database.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.RfcommSocket; - -import android.util.Log; - -import java.io.*; -import java.util.*; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * A low-level API to the Service Discovery Protocol (SDP) Database. - * - * Allows service records to be added to the local SDP database. Once added, - * these services will be advertised to remote devices when they make SDP - * queries on this device. - * - * Currently this API is a thin wrapper to the bluez SDP Database API. See: - * http://wiki.bluez.org/wiki/Database - * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords - * @hide - */ -public final class Database { - private static Database mInstance; - - private static final String sLogName = "android.bluetooth.Database"; - - /** - * Class load time initialization - */ - static { - classInitNative(); - } - private native static void classInitNative(); - - /** - * Private to enforce singleton property - */ - private Database() { - initializeNativeDataNative(); - } - private native void initializeNativeDataNative(); - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - private native void cleanupNativeDataNative(); - - /** - * Singelton accessor - * @return The singleton instance of Database - */ - public static synchronized Database getInstance() { - if (mInstance == null) { - mInstance = new Database(); - } - return mInstance; - } - - /** - * Advertise a service with an RfcommSocket. - * - * This adds the service the SDP Database with the following attributes - * set: Service Name, Protocol Descriptor List, Service Class ID List - * TODO: Construct a byte[] record directly, rather than via XML. - * @param socket The rfcomm socket to advertise (by channel). - * @param serviceName A short name for this service - * @param uuid - * Unique identifier for this service, by which clients - * can search for your service - * @return Handle to the new service record - */ - public int advertiseRfcommService(RfcommSocket socket, - String serviceName, - UUID uuid) throws IOException { - String xmlRecord = - "\n" + - "\n" + - " \n" + // ServiceClassIDList - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ProtocolDescriptorList - " \n" + - " \n" + - " \n" + // L2CAP - " \n" + - " \n" + - " \n" + // RFCOMM - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ServiceName - " \n" + - " \n" + - "\n"; - Log.i(sLogName, xmlRecord); - return addServiceRecordFromXml(xmlRecord); - } - - - /** - * Add a new service record. - * @param record The byte[] record - * @return A handle to the new record - */ - public synchronized int addServiceRecord(byte[] record) throws IOException { - int handle = addServiceRecordNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordNative(byte[] record) - throws IOException; - - /** - * Add a new service record, using XML. - * @param record The record as an XML string - * @return A handle to the new record - */ - public synchronized int addServiceRecordFromXml(String record) throws IOException { - int handle = addServiceRecordFromXmlNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordFromXmlNative(String record) - throws IOException; - - /** - * Update an exisiting service record. - * @param handle Handle to exisiting record - * @param record The updated byte[] record - */ - public synchronized void updateServiceRecord(int handle, byte[] record) { - try { - updateServiceRecordNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordNative(int handle, byte[] record) - throws IOException; - - /** - * Update an exisiting record, using XML. - * @param handle Handle to exisiting record - * @param record The record as an XML string. - */ - public synchronized void updateServiceRecordFromXml(int handle, String record) { - try { - updateServiceRecordFromXmlNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordFromXmlNative(int handle, String record) - throws IOException; - - /** - * Remove a service record. - * It is only possible to remove service records that were added by the - * current connection. - * @param handle Handle to exisiting record to be removed - */ - public synchronized void removeServiceRecord(int handle) { - try { - removeServiceRecordNative(handle); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void removeServiceRecordNative(int handle) throws IOException; -} diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java deleted file mode 100644 index fd2d2ab8848..00000000000 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2007 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.os.Handler; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * The base RFCOMM (service) connection for a headset or handsfree device. - * - * In the future this class will be removed. - * - * @hide - */ -public class HeadsetBase { - private static final String TAG = "Bluetooth HeadsetBase"; - private static final boolean DBG = false; - - public static final int RFCOMM_DISCONNECTED = 1; - - public static final int DIRECTION_INCOMING = 1; - public static final int DIRECTION_OUTGOING = 2; - - private final BluetoothDevice mBluetooth; - private final String mAddress; - private final int mRfcommChannel; - private int mNativeData; - private Thread mEventThread; - private volatile boolean mEventThreadInterrupted; - private Handler mEventThreadHandler; - private int mTimeoutRemainingMs; - private final int mDirection; - private final long mConnectTimestamp; - - protected AtParser mAtParser; - - private WakeLock mWakeLock; // held while processing an AT command - - private native static void classInitNative(); - static { - classInitNative(); - } - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - releaseWakeLock(); - } finally { - super.finalize(); - } - } - - private native void cleanupNativeDataNative(); - - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, - int rfcommChannel) { - mDirection = DIRECTION_OUTGOING; - mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; - mRfcommChannel = rfcommChannel; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); - mWakeLock.setReferenceCounted(false); - initializeAtParser(); - // Must be called after this.mAddress is set. - initializeNativeDataNative(-1); - } - - /* Create from an already exisiting rfcomm connection */ - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { - mDirection = DIRECTION_INCOMING; - mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; - mRfcommChannel = rfcommChannel; - mEventThreadHandler = handler; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); - mWakeLock.setReferenceCounted(false); - initializeAtParser(); - // Must be called after this.mAddress is set. - initializeNativeDataNative(socketFd); - } - - private native void initializeNativeDataNative(int socketFd); - - /* Process an incoming AT command line - */ - protected synchronized void handleInput(String input) { - acquireWakeLock(); - long timestamp; - - if (DBG) timestamp = System.currentTimeMillis(); - AtCommandResult result = mAtParser.process(input); - if (DBG) Log.d(TAG, "Processing " + input + " took " + - (System.currentTimeMillis() - timestamp) + " ms"); - - if (result.getResultCode() == AtCommandResult.ERROR) { - Log.i(TAG, "Error pocessing <" + input + ">"); - } - - sendURC(result.toString()); - - releaseWakeLock(); - } - - /** - * Register AT commands that are common to all Headset / Handsets. This - * function is called by the HeadsetBase constructor. - */ - protected void initializeAtParser() { - mAtParser = new AtParser(); - //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, - //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree - } - - public AtParser getAtParser() { - return mAtParser; - } - - public void startEventThread() { - mEventThread = - new Thread("HeadsetBase Event Thread") { - public void run() { - int last_read_error; - while (!mEventThreadInterrupted) { - String input = readNative(500); - if (input != null) { - handleInput(input); - } - else { - last_read_error = getLastReadStatusNative(); - if (last_read_error != 0) { - Log.i(TAG, "headset read error " + last_read_error); - if (mEventThreadHandler != null) { - mEventThreadHandler.obtainMessage(RFCOMM_DISCONNECTED) - .sendToTarget(); - } - disconnectNative(); - break; - } - } - } - } - }; - mEventThreadInterrupted = false; - mEventThread.start(); - } - - - - private native String readNative(int timeout_ms); - private native int getLastReadStatusNative(); - - private void stopEventThread() { - mEventThreadInterrupted = true; - mEventThread.interrupt(); - try { - mEventThread.join(); - } catch (java.lang.InterruptedException e) { - // FIXME: handle this, - } - mEventThread = null; - } - - public boolean connect(Handler handler) { - if (mEventThread == null) { - if (!connectNative()) return false; - mEventThreadHandler = handler; - } - return true; - } - private native boolean connectNative(); - - /* - * Returns true when either the asynchronous connect is in progress, or - * the connect is complete. Call waitForAsyncConnect() to find out whether - * the connect is actually complete, or disconnect() to cancel. - */ - - public boolean connectAsync() { - return connectAsyncNative(); - } - private native boolean connectAsyncNative(); - - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /* - * Returns 1 when an async connect is complete, 0 on timeout, and -1 on - * error. On error, handler will be called, and you need to re-initiate - * the async connect. - */ - public int waitForAsyncConnect(int timeout_ms, Handler handler) { - int res = waitForAsyncConnectNative(timeout_ms); - if (res > 0) { - mEventThreadHandler = handler; - } - return res; - } - private native int waitForAsyncConnectNative(int timeout_ms); - - public void disconnect() { - if (mEventThread != null) { - stopEventThread(); - } - disconnectNative(); - } - private native void disconnectNative(); - - - /* - * Note that if a remote side disconnects, this method will still return - * true until disconnect() is called. You know when a remote side - * disconnects because you will receive the intent - * IBluetoothService.REMOTE_DEVICE_DISCONNECTED_ACTION. If, when you get - * this intent, method isConnected() returns true, you know that the - * disconnect was initiated by the remote device. - */ - - public boolean isConnected() { - return mEventThread != null; - } - - public String getAddress() { - return mAddress; - } - - public String getName() { - return mBluetooth.getRemoteName(mAddress); - } - - public int getDirection() { - return mDirection; - } - - public long getConnectTimestamp() { - return mConnectTimestamp; - } - - public synchronized boolean sendURC(String urc) { - if (urc.length() > 0) { - boolean ret = sendURCNative(urc); - return ret; - } - return true; - } - private native boolean sendURCNative(String urc); - - private void acquireWakeLock() { - if (!mWakeLock.isHeld()) { - mWakeLock.acquire(); - } - } - - private void releaseWakeLock() { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - } - - private void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl deleted file mode 100644 index 55ff27f97ec..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * System private API for Bluetooth A2DP service - * - * {@hide} - */ -interface IBluetoothA2dp { - int connectSink(in String address); - int disconnectSink(in String address); - List listConnectedSinks(); - int getSinkState(in String address); - int setSinkPriority(in String address, int priority); - int getSinkPriority(in String address); -} diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl deleted file mode 100644 index 4351d2ebc38..00000000000 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008, 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.bluetooth.IBluetoothDeviceCallback; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetoothDevice -{ - boolean isEnabled(); - boolean enable(in IBluetoothDeviceCallback callback); // async - boolean disable(); - - String getAddress(); - String getName(); - boolean setName(in String name); - String getVersion(); - String getRevision(); - String getManufacturer(); - String getCompany(); - - int getScanMode(); - boolean setScanMode(int mode); - - int getDiscoverableTimeout(); - boolean setDiscoverableTimeout(int timeout); - - boolean startDiscovery(boolean resolveNames); - boolean cancelDiscovery(); - boolean isDiscovering(); - boolean startPeriodicDiscovery(); - boolean stopPeriodicDiscovery(); - boolean isPeriodicDiscovery(); - String[] listRemoteDevices(); - - String[] listAclConnections(); - boolean isAclConnected(in String address); - boolean disconnectRemoteDeviceAcl(in String address); - - boolean createBond(in String address); - boolean cancelBondProcess(in String address); - boolean removeBond(in String address); - String[] listBonds(); - int getBondState(in String address); - - String getRemoteName(in String address); - String getRemoteVersion(in String address); - String getRemoteRevision(in String address); - int getRemoteClass(in String address); - String getRemoteManufacturer(in String address); - String getRemoteCompany(in String address); - boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); - byte[] getRemoteFeatures(in String adddress); - String lastSeen(in String address); - String lastUsed(in String address); - - boolean setPin(in String address, in byte[] pin); - boolean cancelPin(in String address); -} diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl deleted file mode 100644 index d25bd560d03..00000000000 --- a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2008, 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; - -/** - * {@hide} - */ -oneway interface IBluetoothDeviceCallback -{ - void onGetRemoteServiceChannelResult(in String address, int channel); - - void onEnableResult(int result); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl deleted file mode 100644 index 582d4e340ee..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * System private API for Bluetooth Headset service - * - * {@hide} - */ -interface IBluetoothHeadset { - int getState(); - String getHeadsetAddress(); - boolean connectHeadset(in String address); - void disconnectHeadset(); - boolean isConnected(in String address); - boolean startVoiceRecognition(); - boolean stopVoiceRecognition(); - boolean setPriority(in String address, int priority); - int getPriority(in String address); -} diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java deleted file mode 100644 index a33263f5261..00000000000 --- a/framework/java/android/bluetooth/RfcommSocket.java +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (C) 2007 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 java.io.IOException; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.FileDescriptor; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket - * is similar to a normal socket in that it takes an address and a port number. - * The difference is of course that the address is a Bluetooth-device address, - * and the port number is an RFCOMM channel. The API allows for the - * establishment of listening sockets via methods - * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and - * {@link #accept(RfcommSocket, int) accept}, as well as for the making of - * outgoing connections with {@link #connect(String, int) connect}, - * {@link #connectAsync(String, int) connectAsync}, and - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * After constructing a socket, you need to {@link #create() create} it and then - * {@link #destroy() destroy} it when you are done using it. Both - * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return - * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. - * Alternatively, you may call {@link #getInputStream() getInputStream} and - * {@link #getOutputStream() getOutputStream} to retrieve the respective streams - * without going through the FileDescriptor. - * - * @hide - */ -public class RfcommSocket { - - /** - * Used by the native implementation of the class. - */ - private int mNativeData; - - /** - * Used by the native implementation of the class. - */ - private int mPort; - - /** - * Used by the native implementation of the class. - */ - private String mAddress; - - /** - * We save the return value of {@link #create() create} and - * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to - * retrieve the I/O streams. - */ - private FileDescriptor mFd; - - /** - * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, - * if the return value is zero, then, the the remaining time left to wait is - * written into this variable (by the native implementation). It is possible - * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before - * the user-specified timeout expires, which is why we save the remaining - * time in this member variable for the user to retrieve by calling method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. - */ - private int mTimeoutRemainingMs; - - /** - * Set to true when an asynchronous (nonblocking) connect is in progress. - * {@see #connectAsync(String,int)}. - */ - private boolean mIsConnecting; - - /** - * Set to true after a successful call to {@link #bind(String,int) bind} and - * used for error checking in {@link #listen(int) listen}. Reset to false - * on {@link #destroy() destroy}. - */ - private boolean mIsBound = false; - - /** - * Set to true after a successful call to {@link #listen(int) listen} and - * used for error checking in {@link #accept(RfcommSocket,int) accept}. - * Reset to false on {@link #destroy() destroy}. - */ - private boolean mIsListening = false; - - /** - * Used to store the remaining time after an accept with a non-negative - * timeout returns unsuccessfully. It is possible that a blocking - * {@link #accept(int) accept} may wait for less than the time specified by - * the user, which is why we store the remainder in this member variable for - * it to be retrieved with method - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. - */ - private int mAcceptTimeoutRemainingMs; - - /** - * Maintained by {@link #getInputStream() getInputStream}. - */ - protected FileInputStream mInputStream; - - /** - * Maintained by {@link #getOutputStream() getOutputStream}. - */ - protected FileOutputStream mOutputStream; - - private native void initializeNativeDataNative(); - - /** - * Constructor. - */ - public RfcommSocket() { - initializeNativeDataNative(); - } - - private native void cleanupNativeDataNative(); - - /** - * Called by the GC to clean up the native data that we set up when we - * construct the object. - */ - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private native static void classInitNative(); - - static { - classInitNative(); - } - - /** - * Creates a socket. You need to call this method before performing any - * other operation on a socket. - * - * @return FileDescriptor for the data stream. - * @throws IOException - * @see #destroy() - */ - public FileDescriptor create() throws IOException { - if (mFd == null) { - mFd = createNative(); - } - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - private native FileDescriptor createNative(); - - /** - * Destroys a socket created by {@link #create() create}. Call this - * function when you no longer use the socket in order to release the - * underlying OS resources. - * - * @see #create() - */ - public void destroy() { - synchronized (this) { - destroyNative(); - mFd = null; - mIsBound = false; - mIsListening = false; - } - } - - private native void destroyNative(); - - /** - * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. - * - * @return the FileDescriptor - * @throws IOException - * when the socket has not been {@link #create() created}. - */ - public FileDescriptor getFileDescriptor() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - /** - * Retrieves the input stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return InputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public InputStream getInputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mInputStream == null) { - mInputStream = new FileInputStream(mFd); - } - - return mInputStream; - } - } - - /** - * Retrieves the output stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return OutputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public OutputStream getOutputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mOutputStream == null) { - mOutputStream = new FileOutputStream(mFd); - } - - return mOutputStream; - } - } - - /** - * Starts a blocking connect to a remote RFCOMM socket. It takes the address - * of a device and the RFCOMM channel (port) to which to connect. - * - * @param address - * is the Bluetooth address of the remote device. - * @param port - * is the RFCOMM channel - * @return true on success, false on failure - * @throws IOException - * if {@link #create() create} has not been called. - * @see #connectAsync(String, int) - */ - public boolean connect(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return connectNative(address, port); - } - } - - private native boolean connectNative(String address, int port); - - /** - * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. - * It takes the address of the device to connect to, as well as the RFCOMM - * channel (port). On successful return (return value is true), you need to - * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to - * block for up to a specified number of milliseconds while waiting for the - * asyncronous connect to complete. - * - * @param address - * of remote device - * @param port - * the RFCOMM channel - * @return true when the asynchronous connect has successfully started, - * false if there was an error. - * @throws IOException - * is you have not called {@link #create() create} - * @see #waitForAsyncConnect(int) - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connect(String, int) - */ - public boolean connectAsync(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsConnecting = connectAsyncNative(address, port); - return mIsConnecting; - } - } - - private native boolean connectAsyncNative(String address, int port); - - /** - * Interrupts an asynchronous connect in progress. This method does nothing - * when there is no asynchronous connect in progress. - * - * @throws IOException - * if you have not called {@link #create() create}. - * @see #connectAsync(String, int) - */ - public void interruptAsyncConnect() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsConnecting) { - mIsConnecting = !interruptAsyncConnectNative(); - } - } - } - - private native boolean interruptAsyncConnectNative(); - - /** - * Tells you whether there is an asynchronous connect in progress. This - * method returns an undefined value when there is a synchronous connect in - * progress. - * - * @return true if there is an asyc connect in progress, false otherwise - * @see #connectAsync(String, int) - */ - public boolean isConnecting() { - return mIsConnecting; - } - - /** - * Blocks for a specified amount of milliseconds while waiting for an - * asynchronous connect to complete. Returns an integer value to indicate - * one of the following: the connect succeeded, the connect is still in - * progress, or the connect failed. It is possible for this method to block - * for less than the time specified by the user, and still return zero - * (i.e., async connect is still in progress.) For this reason, if the - * return value is zero, you need to call method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} - * to retrieve the remaining time. - * - * @param timeoutMs - * the time to block while waiting for the async connect to - * complete. - * @return a positive value if the connect succeeds; zero, if the connect is - * still in progress, and a negative value if the connect failed. - * - * @throws IOException - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connectAsync(String, int) - */ - public int waitForAsyncConnect(int timeoutMs) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - int ret = waitForAsyncConnectNative(timeoutMs); - if (ret != 0) { - mIsConnecting = false; - } - return ret; - } - } - - private native int waitForAsyncConnectNative(int timeoutMs); - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * It is possible that waitForAsyncConnect() waits for less than the time - * specified by the user, and still returns zero (i.e., async connect is - * still in progress.) For this reason, if the return value is zero, you - * need to call this method to retrieve the remaining time before you call - * waitForAsyncConnect again. - * - * @return the remaining timeout in milliseconds. - * @see #waitForAsyncConnect(int) - * @see #connectAsync(String, int) - */ - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /** - * Shuts down both directions on a socket. - * - * @return true on success, false on failure; if the return value is false, - * the socket might be left in a patially shut-down state (i.e. one - * direction is shut down, but the other is still open.) In this - * case, you should {@link #destroy() destroy} and then - * {@link #create() create} the socket again. - * @throws IOException - * is you have not caled {@link #create() create}. - * @see #shutdownInput() - * @see #shutdownOutput() - */ - public boolean shutdown() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (shutdownNative(true)) { - return shutdownNative(false); - } - - return false; - } - } - - /** - * Shuts down the input stream of the socket, but leaves the output stream - * in its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownOutput() - */ - public boolean shutdownInput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(true); - } - } - - /** - * Shut down the output stream of the socket, but leaves the input stream in - * its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownInput() - */ - public boolean shutdownOutput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(false); - } - } - - private native boolean shutdownNative(boolean shutdownInput); - - /** - * Tells you whether a socket is connected to another socket. This could be - * for input or output or both. - * - * @return true if connected, false otherwise. - * @see #isInputConnected() - * @see #isOutputConnected() - */ - public boolean isConnected() { - return isConnectedNative() > 0; - } - - /** - * Determines whether input is connected (i.e., whether you can receive data - * on this socket.) - * - * @return true if input is connected, false otherwise. - * @see #isConnected() - * @see #isOutputConnected() - */ - public boolean isInputConnected() { - return (isConnectedNative() & 1) != 0; - } - - /** - * Determines whether output is connected (i.e., whether you can send data - * on this socket.) - * - * @return true if output is connected, false otherwise. - * @see #isConnected() - * @see #isInputConnected() - */ - public boolean isOutputConnected() { - return (isConnectedNative() & 2) != 0; - } - - private native int isConnectedNative(); - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. The port is automatically selected as the first - * available port in the range 12 to 30. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * TODO: Use bind(0) in native code to have the kernel select an unused - * port. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - for (int port = 12; port <= 30; port++) { - if (bindNative(device, port)) { - mIsBound = true; - return true; - } - } - mIsBound = false; - return false; - } - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @param port - * RFCOMM channel to bind socket to. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device, int port) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsBound = bindNative(device, port); - return mIsBound; - } - - private native boolean bindNative(String device, int port); - - /** - * Starts listening for incoming connections on this socket, after it has - * been bound to an address and RFCOMM channel with - * {@link #bind(String,int) bind}. - * - * @param backlog - * the number of pending incoming connections to queue for - * {@link #accept(RfcommSocket, int) accept}. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} or if the - * socket has not been bound to a device and RFCOMM channel. - */ - public boolean listen(int backlog) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsBound) { - throw new IOException("socket not bound"); - } - mIsListening = listenNative(backlog); - return mIsListening; - } - - private native boolean listenNative(int backlog); - - /** - * Accepts incoming-connection requests for a listening socket bound to an - * RFCOMM channel. The user may provide a time to wait for an incoming - * connection. - * - * Note that this method may return null (i.e., no incoming connection) - * before the user-specified timeout expires. For this reason, on a null - * return value, you need to call - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} - * in order to see how much time is left to wait, before you call this - * method again. - * - * @param newSock - * is set to the new socket that is created as a result of a - * successful accept. - * @param timeoutMs - * time (in milliseconds) to block while waiting to an - * incoming-connection request. A negative value is an infinite - * wait. - * @return FileDescriptor of newSock on success, null on failure. Failure - * occurs if the timeout expires without a successful connect. - * @throws IOException - * if the socket has not been {@link #create() create}ed, is - * not bound, or is not a listening socket. - * @see #bind(String, int) - * @see #listen(int) - * @see #getRemainingAcceptWaitingTimeMs() - */ - public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) - throws IOException { - synchronized (newSock) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsListening == false) { - throw new IOException("not listening on socket"); - } - newSock.mFd = acceptNative(newSock, timeoutMs); - return newSock.mFd; - } - } - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #accept(RfcommSocket, int) accept}. - * - * Since accept() may return null (i.e., no incoming connection) before the - * user-specified timeout expires, you need to call this method in order to - * see how much time is left to wait, and wait for that amount of time - * before you call accept again. - * - * @return the remaining time, in milliseconds. - */ - public int getRemainingAcceptWaitingTimeMs() { - return mAcceptTimeoutRemainingMs; - } - - private native FileDescriptor acceptNative(RfcommSocket newSock, - int timeoutMs); - - /** - * Get the port (rfcomm channel) associated with this socket. - * - * This is only valid if the port has been set via a successful call to - * {@link #bind(String, int)}, {@link #connect(String, int)} - * or {@link #connectAsync(String, int)}. This can be checked - * with {@link #isListening()} and {@link #isConnected()}. - * @return Port (rfcomm channel) - */ - public int getPort() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsListening && !isConnected()) { - throw new IOException("not listening or connected on socket"); - } - return mPort; - } - - /** - * Return true if this socket is listening ({@link #listen(int)} - * has been called successfully). - */ - public boolean isListening() { - return mIsListening; - } -} diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java deleted file mode 100644 index a43a08be105..00000000000 --- a/framework/java/android/bluetooth/ScoSocket.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2008 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.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Simple SCO Socket. - * Currently in Android, there is no support for sending data over a SCO - * socket - this is managed by the hardware link to the Bluetooth Chip. This - * class is instead intended for management of the SCO socket lifetime, - * and is tailored for use with the headset / handsfree profiles. - * @hide - */ -public class ScoSocket { - private static final String TAG = "ScoSocket"; - private static final boolean DBG = true; - private static final boolean VDBG = false; // even more logging - - public static final int STATE_READY = 1; // Ready for use. No threads or sockets - public static final int STATE_ACCEPT = 2; // accept() thread running - public static final int STATE_CONNECTING = 3; // connect() thread running - public static final int STATE_CONNECTED = 4; // connected, waiting for close() - public static final int STATE_CLOSED = 5; // was connected, now closed. - - private int mState; - private int mNativeData; - private Handler mHandler; - private int mAcceptedCode; - private int mConnectedCode; - private int mClosedCode; - - private WakeLock mWakeLock; // held while in STATE_CONNECTING - - static { - classInitNative(); - } - private native static void classInitNative(); - - public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode, - int closedCode) { - initNative(); - mState = STATE_READY; - mHandler = handler; - mAcceptedCode = acceptedCode; - mConnectedCode = connectedCode; - mClosedCode = closedCode; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket"); - mWakeLock.setReferenceCounted(false); - if (VDBG) log(this + " SCO OBJECT CTOR"); - } - private native void initNative(); - - protected void finalize() throws Throwable { - try { - if (VDBG) log(this + " SCO OBJECT DTOR"); - destroyNative(); - releaseWakeLock(); - } finally { - super.finalize(); - } - } - private native void destroyNative(); - - /** Connect this SCO socket to the given BT address. - * Does not block. - */ - public synchronized boolean connect(String address) { - if (VDBG) log("connect() " + this); - if (mState != STATE_READY) { - if (DBG) log("connect(): Bad state"); - return false; - } - acquireWakeLock(); - if (connectNative(address)) { - mState = STATE_CONNECTING; - return true; - } else { - mState = STATE_CLOSED; - releaseWakeLock(); - return false; - } - } - private native boolean connectNative(String address); - - /** Accept incoming SCO connections. - * Does not block. - */ - public synchronized boolean accept() { - if (VDBG) log("accept() " + this); - if (mState != STATE_READY) { - if (DBG) log("Bad state"); - return false; - } - if (acceptNative()) { - mState = STATE_ACCEPT; - return true; - } else { - mState = STATE_CLOSED; - return false; - } - } - private native boolean acceptNative(); - - public synchronized void close() { - if (DBG) log(this + " SCO OBJECT close() mState = " + mState); - acquireWakeLock(); - mState = STATE_CLOSED; - closeNative(); - releaseWakeLock(); - } - private native void closeNative(); - - public synchronized int getState() { - return mState; - } - - private synchronized void onConnected(int result) { - if (VDBG) log(this + " onConnected() mState = " + mState + " " + this); - if (mState != STATE_CONNECTING) { - if (DBG) log("Strange state, closing " + mState + " " + this); - return; - } - if (result >= 0) { - mState = STATE_CONNECTED; - } else { - mState = STATE_CLOSED; - } - mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); - releaseWakeLock(); - } - - private synchronized void onAccepted(int result) { - if (VDBG) log("onAccepted() " + this); - if (mState != STATE_ACCEPT) { - if (DBG) log("Strange state " + this); - return; - } - if (result >= 0) { - mState = STATE_CONNECTED; - } else { - mState = STATE_CLOSED; - } - mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget(); - } - - private synchronized void onClosed() { - if (DBG) log("onClosed() " + this); - if (mState != STATE_CLOSED) { - mState = STATE_CLOSED; - mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget(); - releaseWakeLock(); - } - } - - private void acquireWakeLock() { - if (!mWakeLock.isHeld()) { - mWakeLock.acquire(); - if (VDBG) log("mWakeLock.acquire() " + this); - } - } - - private void releaseWakeLock() { - if (mWakeLock.isHeld()) { - if (VDBG) log("mWakeLock.release() " + this); - mWakeLock.release(); - } - } - - private void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html deleted file mode 100644 index 79abf0cb4a0..00000000000 --- a/framework/java/android/bluetooth/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - -Provides classes that manage Bluetooth functionality on the device. -

-The Bluetooth APIs allow applications can connect and disconnect headsets, or scan -for other kinds of Bluetooth devices and pair them. Further control includes the -ability to write and modify the local Service Discovery Protocol (SDP) database, -query the SDP database of other Bluetooth devices, establish RFCOMM -channels/sockets on Android, and connect to specified sockets on other devices. -

-

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

- - -- GitLab From 33897767e12e0e099d9bb4839eea1c275e7be40d Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: [PATCH 0011/1408] auto import from //depot/cupcake/@135843 --- .../android/bluetooth/AtCommandHandler.java | 93 +++ .../android/bluetooth/AtCommandResult.java | 117 +++ .../java/android/bluetooth/AtParser.java | 370 ++++++++++ .../java/android/bluetooth/BluetoothA2dp.java | 247 +++++++ .../bluetooth/BluetoothAudioGateway.java | 190 +++++ .../android/bluetooth/BluetoothClass.java | 191 +++++ .../android/bluetooth/BluetoothDevice.java | 580 +++++++++++++++ .../android/bluetooth/BluetoothError.java | 42 ++ .../android/bluetooth/BluetoothHeadset.java | 353 +++++++++ .../android/bluetooth/BluetoothIntent.java | 128 ++++ .../java/android/bluetooth/Database.java | 200 ++++++ .../java/android/bluetooth/HeadsetBase.java | 285 ++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 31 + .../android/bluetooth/IBluetoothDevice.aidl | 77 ++ .../bluetooth/IBluetoothDeviceCallback.aidl | 27 + .../android/bluetooth/IBluetoothHeadset.aidl | 34 + .../java/android/bluetooth/RfcommSocket.java | 674 ++++++++++++++++++ .../java/android/bluetooth/ScoSocket.java | 194 +++++ framework/java/android/bluetooth/package.html | 13 + 19 files changed, 3846 insertions(+) create mode 100644 framework/java/android/bluetooth/AtCommandHandler.java create mode 100644 framework/java/android/bluetooth/AtCommandResult.java create mode 100644 framework/java/android/bluetooth/AtParser.java create mode 100644 framework/java/android/bluetooth/BluetoothA2dp.java create mode 100644 framework/java/android/bluetooth/BluetoothAudioGateway.java create mode 100644 framework/java/android/bluetooth/BluetoothClass.java create mode 100644 framework/java/android/bluetooth/BluetoothDevice.java create mode 100644 framework/java/android/bluetooth/BluetoothError.java create mode 100644 framework/java/android/bluetooth/BluetoothHeadset.java create mode 100644 framework/java/android/bluetooth/BluetoothIntent.java create mode 100644 framework/java/android/bluetooth/Database.java create mode 100644 framework/java/android/bluetooth/HeadsetBase.java create mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothDevice.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothHeadset.aidl create mode 100644 framework/java/android/bluetooth/RfcommSocket.java create mode 100644 framework/java/android/bluetooth/ScoSocket.java create mode 100644 framework/java/android/bluetooth/package.html diff --git a/framework/java/android/bluetooth/AtCommandHandler.java b/framework/java/android/bluetooth/AtCommandHandler.java new file mode 100644 index 00000000000..8de2133e494 --- /dev/null +++ b/framework/java/android/bluetooth/AtCommandHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 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.bluetooth.AtCommandResult; + +/** + * Handler Interface for {@link AtParser}.

+ * @hide + */ +public abstract class AtCommandHandler { + + /** + * Handle Basic commands "ATA".

+ * These are single letter commands such as ATA and ATD. Anything following + * the single letter command ('A' and 'D' respectively) will be passed as + * 'arg'.

+ * For example, "ATDT1234" would result in the call + * handleBasicCommand("T1234").

+ * @param arg Everything following the basic command character. + * @return The result of this command. + */ + public AtCommandResult handleBasicCommand(String arg) { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Actions command "AT+FOO".

+ * Action commands are part of the Extended command syntax, and are + * typically used to signal an action on "FOO".

+ * @return The result of this command. + */ + public AtCommandResult handleActionCommand() { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Read command "AT+FOO?".

+ * Read commands are part of the Extended command syntax, and are + * typically used to read the value of "FOO".

+ * @return The result of this command. + */ + public AtCommandResult handleReadCommand() { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Set command "AT+FOO=...".

+ * Set commands are part of the Extended command syntax, and are + * typically used to set the value of "FOO". Multiple arguments can be + * sent.

+ * AT+FOO=[[,[,...]]]

+ * Each argument will be either numeric (Integer) or String. + * handleSetCommand is passed a generic Object[] array in which each + * element will be an Integer (if it can be parsed with parseInt()) or + * String.

+ * Missing arguments ",," are set to empty Strings.

+ * @param args Array of String and/or Integer's. There will always be at + * least one element in this array. + * @return The result of this command. + */ + // Typically used to set this paramter + public AtCommandResult handleSetCommand(Object[] args) { + return new AtCommandResult(AtCommandResult.ERROR); + } + + /** + * Handle Test command "AT+FOO=?".

+ * Test commands are part of the Extended command syntax, and are typically + * used to request an indication of the range of legal values that "FOO" + * can take.

+ * By defualt we return an OK result, to indicate that this command is at + * least recognized.

+ * @return The result of this command. + */ + public AtCommandResult handleTestCommand() { + return new AtCommandResult(AtCommandResult.OK); + } +} diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java new file mode 100644 index 00000000000..638be2d2e99 --- /dev/null +++ b/framework/java/android/bluetooth/AtCommandResult.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 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 java.util.*; + +/** + * The result of execution of an single AT command.

+ * + * + * This class can represent the final response to an AT command line, and also + * intermediate responses to a single command within a chained AT command + * line.

+ * + * The actual responses that are intended to be send in reply to the AT command + * line are stored in a string array. The final response is stored as an + * int enum, converted to a string when toString() is called. Only a single + * final response is sent from multiple commands chained into a single command + * line.

+ * @hide + */ +public class AtCommandResult { + // Result code enumerations + public static final int OK = 0; + public static final int ERROR = 1; + public static final int UNSOLICITED = 2; + + private static final String OK_STRING = "OK"; + private static final String ERROR_STRING = "ERROR"; + + private int mResultCode; // Result code + private StringBuilder mResponse; // Response with CRLF line breaks + + /** + * Construct a new AtCommandResult with given result code, and an empty + * response array. + * @param resultCode One of OK, ERROR or UNSOLICITED. + */ + public AtCommandResult(int resultCode) { + mResultCode = resultCode; + mResponse = new StringBuilder(); + } + + /** + * Construct a new AtCommandResult with result code OK, and the specified + * single line response. + * @param response The single line response. + */ + public AtCommandResult(String response) { + this(OK); + addResponse(response); + } + + public int getResultCode() { + return mResultCode; + } + + /** + * Add another line to the response. + */ + public void addResponse(String response) { + appendWithCrlf(mResponse, response); + } + + /** + * Add the given result into this AtCommandResult object.

+ * Used to combine results from multiple commands in a single command line + * (command chaining). + * @param result The AtCommandResult to add to this result. + */ + public void addResult(AtCommandResult result) { + if (result != null) { + appendWithCrlf(mResponse, result.mResponse.toString()); + mResultCode = result.mResultCode; + } + } + + /** + * Generate the string response ready to send + */ + public String toString() { + StringBuilder result = new StringBuilder(mResponse.toString()); + switch (mResultCode) { + case OK: + appendWithCrlf(result, OK_STRING); + break; + case ERROR: + appendWithCrlf(result, ERROR_STRING); + break; + } + return result.toString(); + } + + /** Append a string to a string builder, joining with a double + * CRLF. Used to create multi-line AT command replies + */ + public static void appendWithCrlf(StringBuilder str1, String str2) { + if (str1.length() > 0 && str2.length() > 0) { + str1.append("\r\n\r\n"); + } + str1.append(str2); + } +}; diff --git a/framework/java/android/bluetooth/AtParser.java b/framework/java/android/bluetooth/AtParser.java new file mode 100644 index 00000000000..1ea31503bb7 --- /dev/null +++ b/framework/java/android/bluetooth/AtParser.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2007 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.bluetooth.AtCommandHandler; +import android.bluetooth.AtCommandResult; + +import java.util.*; + +/** + * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard. + *

+ * + * Conforment with the subset of V.250 required for implementation of the + * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP + * specifications. Also implements some V.250 features not required by + * Bluetooth - such as chained commands.

+ * + * Command handlers are registered with an AtParser object. These handlers are + * invoked when command lines are processed by AtParser's process() method.

+ * + * The AtParser object accepts a new command line to parse via its process() + * method. It breaks each command line into one or more commands. Each command + * is parsed for name, type, and (optional) arguments, and an appropriate + * external handler method is called through the AtCommandHandler interface. + * + * The command types are

    + *
  • Basic Command. For example "ATDT1234567890". Basic command names are a + * single character (e.g. "D"), and everything following this character is + * passed to the handler as a string argument (e.g. "T1234567890"). + *
  • Action Command. For example "AT+CIMI". The command name is "CIMI", and + * there are no arguments for action commands. + *
  • Read Command. For example "AT+VGM?". The command name is "VGM", and there + * are no arguments for get commands. + *
  • Set Command. For example "AT+VGM=14". The command name is "VGM", and + * there is a single integer argument in this case. In the general case then + * can be zero or more arguments (comma deliminated) each of integer or string + * form. + *
  • Test Command. For example "AT+VGM=?. No arguments. + *
+ * + * In V.250 the last four command types are known as Extended Commands, and + * they are used heavily in Bluetooth.

+ * + * Basic commands cannot be chained in this implementation. For Bluetooth + * headset/handsfree use this is acceptable, because they only use the basic + * commands ATA and ATD, which are not allowed to be chained. For general V.250 + * use we would need to improve this class to allow Basic command chaining - + * however its tricky to get right becuase there is no deliminator for Basic + * command chaining.

+ * + * Extended commands can be chained. For example:

+ * AT+VGM?;+VGM=14;+CIMI

+ * This is equivalent to:

+ * AT+VGM? + * AT+VGM=14 + * AT+CIMI + * Except that only one final result code is return (although several + * intermediate responses may be returned), and as soon as one command in the + * chain fails the rest are abandonded.

+ * + * Handlers are registered by there command name via register(Char c, ...) or + * register(String s, ...). Handlers for Basic command should be registered by + * the basic command character, and handlers for Extended commands should be + * registered by String.

+ * + * Refer to:

    + *
  • ITU-T Recommendation V.250 + *
  • ETSI TS 127.007 (AT Comannd set for User Equipment, 3GPP TS 27.007) + *
  • Bluetooth Headset Profile Spec (K6) + *
  • Bluetooth Handsfree Profile Spec (HFP 1.5) + *
+ * @hide + */ +public class AtParser { + + // Extended command type enumeration, only used internally + private static final int TYPE_ACTION = 0; // AT+FOO + private static final int TYPE_READ = 1; // AT+FOO? + private static final int TYPE_SET = 2; // AT+FOO= + private static final int TYPE_TEST = 3; // AT+FOO=? + + private HashMap mExtHandlers; + private HashMap mBasicHandlers; + + private String mLastInput; // for "A/" (repeat last command) support + + /** + * Create a new AtParser.

+ * No handlers are registered. + */ + public AtParser() { + mBasicHandlers = new HashMap(); + mExtHandlers = new HashMap(); + mLastInput = ""; + } + + /** + * Register a Basic command handler.

+ * Basic command handlers are later called via their + * handleBasicCommand(String args) method. + * @param command Command name - a single character + * @param handler Handler to register + */ + public void register(Character command, AtCommandHandler handler) { + mBasicHandlers.put(command, handler); + } + + /** + * Register an Extended command handler.

+ * Extended command handlers are later called via:

    + *
  • handleActionCommand() + *
  • handleGetCommand() + *
  • handleSetCommand() + *
  • handleTestCommand() + *
+ * Only one method will be called for each command processed. + * @param command Command name - can be multiple characters + * @param handler Handler to register + */ + public void register(String command, AtCommandHandler handler) { + mExtHandlers.put(command, handler); + } + + + /** + * Strip input of whitespace and force Uppercase - except sections inside + * quotes. Also fixes unmatched quotes (by appending a quote). Double + * quotes " are the only quotes allowed by V.250 + */ + static private String clean(String input) { + StringBuilder out = new StringBuilder(input.length()); + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '"') { + int j = input.indexOf('"', i + 1 ); // search for closing " + if (j == -1) { // unmatched ", insert one. + out.append(input.substring(i, input.length())); + out.append('"'); + break; + } + out.append(input.substring(i, j + 1)); + i = j; + } else if (c != ' ') { + out.append(Character.toUpperCase(c)); + } + } + + return out.toString(); + } + + static private boolean isAtoZ(char c) { + return (c >= 'A' && c <= 'Z'); + } + + /** + * Find a character ch, ignoring quoted sections. + * Return input.length() if not found. + */ + static private int findChar(char ch, String input, int fromIndex) { + for (int i = fromIndex; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '"') { + i = input.indexOf('"', i + 1); + if (i == -1) { + return input.length(); + } + } else if (c == ch) { + return i; + } + } + return input.length(); + } + + /** + * Break an argument string into individual arguments (comma deliminated). + * Integer arguments are turned into Integer objects. Otherwise a String + * object is used. + */ + static private Object[] generateArgs(String input) { + int i = 0; + int j; + ArrayList out = new ArrayList(); + while (i <= input.length()) { + j = findChar(',', input, i); + + String arg = input.substring(i, j); + try { + out.add(new Integer(arg)); + } catch (NumberFormatException e) { + out.add(arg); + } + + i = j + 1; // move past comma + } + return out.toArray(); + } + + /** + * Return the index of the end of character after the last characeter in + * the extended command name. Uses the V.250 spec for allowed command + * names. + */ + static private int findEndExtendedName(String input, int index) { + for (int i = index; i < input.length(); i++) { + char c = input.charAt(i); + + // V.250 defines the following chars as legal extended command + // names + if (isAtoZ(c)) continue; + if (c >= '0' && c <= '9') continue; + switch (c) { + case '!': + case '%': + case '-': + case '.': + case '/': + case ':': + case '_': + continue; + default: + return i; + } + } + return input.length(); + } + + /** + * Processes an incoming AT command line.

+ * This method will invoke zero or one command handler methods for each + * command in the command line.

+ * @param raw_input The AT input, without EOL deliminator (e.g. ). + * @return Result object for this command line. This can be + * converted to a String[] response with toStrings(). + */ + public AtCommandResult process(String raw_input) { + String input = clean(raw_input); + + // Handle "A/" (repeat previous line) + if (input.regionMatches(0, "A/", 0, 2)) { + input = new String(mLastInput); + } else { + mLastInput = new String(input); + } + + // Handle empty line - no response necessary + if (input.equals("")) { + // Return [] + return new AtCommandResult(AtCommandResult.UNSOLICITED); + } + + // Anything else deserves an error + if (!input.regionMatches(0, "AT", 0, 2)) { + // Return ["ERROR"] + return new AtCommandResult(AtCommandResult.ERROR); + } + + // Ok we have a command that starts with AT. Process it + int index = 2; + AtCommandResult result = + new AtCommandResult(AtCommandResult.UNSOLICITED); + while (index < input.length()) { + char c = input.charAt(index); + + if (isAtoZ(c)) { + // Option 1: Basic Command + // Pass the rest of the line as is to the handler. Do not + // look for any more commands on this line. + String args = input.substring(index + 1); + if (mBasicHandlers.containsKey((Character)c)) { + result.addResult(mBasicHandlers.get( + (Character)c).handleBasicCommand(args)); + return result; + } else { + // no handler + result.addResult( + new AtCommandResult(AtCommandResult.ERROR)); + return result; + } + // control never reaches here + } + + if (c == '+') { + // Option 2: Extended Command + // Search for first non-name character. Shortcircuit if we dont + // handle this command name. + int i = findEndExtendedName(input, index + 1); + String commandName = input.substring(index, i); + if (!mExtHandlers.containsKey(commandName)) { + // no handler + result.addResult( + new AtCommandResult(AtCommandResult.ERROR)); + return result; + } + AtCommandHandler handler = mExtHandlers.get(commandName); + + // Search for end of this command - this is usually the end of + // line + int endIndex = findChar(';', input, index); + + // Determine what type of command this is. + // Default to TYPE_ACTION if we can't find anything else + // obvious. + int type; + + if (i >= endIndex) { + type = TYPE_ACTION; + } else if (input.charAt(i) == '?') { + type = TYPE_READ; + } else if (input.charAt(i) == '=') { + if (i + 1 < endIndex) { + if (input.charAt(i + 1) == '?') { + type = TYPE_TEST; + } else { + type = TYPE_SET; + } + } else { + type = TYPE_SET; + } + } else { + type = TYPE_ACTION; + } + + // Call this command. Short-circuit as soon as a command fails + switch (type) { + case TYPE_ACTION: + result.addResult(handler.handleActionCommand()); + break; + case TYPE_READ: + result.addResult(handler.handleReadCommand()); + break; + case TYPE_TEST: + result.addResult(handler.handleTestCommand()); + break; + case TYPE_SET: + Object[] args = + generateArgs(input.substring(i + 1, endIndex)); + result.addResult(handler.handleSetCommand(args)); + break; + } + if (result.getResultCode() != AtCommandResult.OK) { + return result; // short-circuit + } + + index = endIndex; + } else { + // Can't tell if this is a basic or extended command. + // Push forwards and hope we hit something. + index++; + } + } + // Finished processing (and all results were ok) + return result; + } +} diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java new file mode 100644 index 00000000000..b0b0154abc9 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.server.BluetoothA2dpService; +import android.content.Context; +import android.os.ServiceManager; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +import java.util.List; + +/** + * Public API for controlling the Bluetooth A2DP Profile Service. + * + * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP + * Service via IPC. + * + * Creating a BluetoothA2dp object will initiate a binding with the + * BluetoothHeadset service. Users of this object should call close() when they + * are finished, so that this proxy object can unbind from the service. + * + * Currently the BluetoothA2dp service runs in the system server and this + * proxy object will be immediately bound to the service on construction. + * However this may change in future releases, and error codes such as + * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the + * proxy object is not yet attached. + * + * Currently this class provides methods to connect to A2DP audio sinks. + * + * @hide + */ +public class BluetoothA2dp { + private static final String TAG = "BluetoothA2dp"; + + /** int extra for SINK_STATE_CHANGED_ACTION */ + public static final String SINK_STATE = + "android.bluetooth.a2dp.intent.SINK_STATE"; + /** int extra for SINK_STATE_CHANGED_ACTION */ + public static final String SINK_PREVIOUS_STATE = + "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; + + /** Indicates the state of an A2DP audio sink has changed. + * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SINK_STATE_CHANGED_ACTION = + "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; + + public static final int STATE_DISCONNECTED = 0; + public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTED = 2; + public static final int STATE_DISCONNECTING = 3; + /** Playing implies connected */ + public static final int STATE_PLAYING = 4; + + /** Default priority for a2dp devices that should allow incoming + * connections */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for a2dp devices that should not allow incoming + * connections */ + public static final int PRIORITY_OFF = 0; + private final IBluetoothA2dp mService; + private final Context mContext; + + /** + * Create a BluetoothA2dp proxy object for interacting with the local + * Bluetooth A2DP service. + * @param c Context + */ + public BluetoothA2dp(Context c) { + mContext = c; + IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); + if (b == null) { + throw new RuntimeException("Bluetooth A2DP service not available!"); + } + mService = IBluetoothA2dp.Stub.asInterface(b); + } + + /** Initiate a connection to an A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when the + * connection is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public int connectSink(String address) { + try { + return mService.connectSink(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** Initiate disconnect from an A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * disconnect is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public int disconnectSink(String address) { + try { + return mService.disconnectSink(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** Check if a specified A2DP sink is connected. + * @param address Remote BT address. + * @return True if connected (or playing), false otherwise and on error. + * @hide + */ + public boolean isSinkConnected(String address) { + int state = getSinkState(address); + return state == STATE_CONNECTED || state == STATE_PLAYING; + } + + /** Check if any A2DP sink is connected. + * @return a List of connected A2DP sinks, or null on error. + * @hide + */ + public List listConnectedSinks() { + try { + return mService.listConnectedSinks(); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return null; + } + } + + /** Get the state of an A2DP sink + * @param address Remote BT address. + * @return State code, or negative on error + * @hide + */ + public int getSinkState(String address) { + try { + return mService.getSinkState(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Set priority of a2dp sink. + * Priority is a non-negative integer. By default paired sinks will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Sinks with priority greater than zero will accept incoming connections + * (if no sink is currently connected). + * Priority for unpaired sink must be PRIORITY_NONE. + * @param address Paired sink + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return Result code, negative indicates an error + */ + public int setSinkPriority(String address, int priority) { + try { + return mService.setSinkPriority(address, priority); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Get priority of a2dp sink. + * @param address Sink + * @return non-negative priority, or negative error code on error. + */ + public int getSinkPriority(String address) { + try { + return mService.getSinkPriority(address); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothError.ERROR_IPC; + } + } + + /** + * Check class bits for possible A2DP Sink support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might be a A2DP Sink. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might be a A2DP sink + */ + public static boolean doesClassMatchSink(int btClass) { + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // By the A2DP spec, sinks must indicate the RENDER service. + // However we found some that do not (Chordette). So lets also + // match on some other class bits. + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + + /** Helper for converting a state to a string. + * For debug use only - strings are not internationalized. + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + default: + return ""; + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java new file mode 100644 index 00000000000..f3afd2a4a0b --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -0,0 +1,190 @@ +package android.bluetooth; + +import java.lang.Thread; + +import android.os.Message; +import android.os.Handler; +import android.util.Log; + +/** + * Listen's for incoming RFCOMM connection for the headset / handsfree service. + * + * This class is planned for deletion, in favor of a generic Rfcomm class. + * + * @hide + */ +public class BluetoothAudioGateway { + private static final String TAG = "BT Audio Gateway"; + private static final boolean DBG = false; + + private int mNativeData; + static { classInitNative(); } + + private BluetoothDevice mBluetooth; + + /* in */ + private int mHandsfreeAgRfcommChannel = -1; + private int mHeadsetAgRfcommChannel = -1; + + /* out */ + private String mConnectingHeadsetAddress; + private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ + private int mConnectingHeadsetSocketFd; + private String mConnectingHandsfreeAddress; + private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ + private int mConnectingHandsfreeSocketFd; + private int mTimeoutRemainingMs; /* in/out */ + + public static final int DEFAULT_HF_AG_CHANNEL = 10; + public static final int DEFAULT_HS_AG_CHANNEL = 11; + + public BluetoothAudioGateway(BluetoothDevice bluetooth) { + this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); + } + + public BluetoothAudioGateway(BluetoothDevice bluetooth, + int handsfreeAgRfcommChannel, + int headsetAgRfcommChannel) { + mBluetooth = bluetooth; + mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; + mHeadsetAgRfcommChannel = headsetAgRfcommChannel; + initializeNativeDataNative(); + } + + private Thread mConnectThead; + private volatile boolean mInterrupted; + private static final int SELECT_WAIT_TIMEOUT = 1000; + + private Handler mCallback; + + public class IncomingConnectionInfo { + IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd, + int rfcommChan) { + mBluetooth = bluetooth; + mAddress = address; + mSocketFd = socketFd; + mRfcommChan = rfcommChan; + } + + public BluetoothDevice mBluetooth; + public String mAddress; + public int mSocketFd; + public int mRfcommChan; + } + + public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; + public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101; + + public synchronized boolean start(Handler callback) { + + if (mConnectThead == null) { + mCallback = callback; + mConnectThead = new Thread(TAG) { + public void run() { + if (DBG) log("Connect Thread starting"); + while (!mInterrupted) { + //Log.i(TAG, "waiting for connect"); + mConnectingHeadsetRfcommChannel = -1; + mConnectingHandsfreeRfcommChannel = -1; + if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) { + if (mTimeoutRemainingMs > 0) { + try { + Log.i(TAG, "select thread timed out, but " + + mTimeoutRemainingMs + "ms of waiting remain."); + Thread.sleep(mTimeoutRemainingMs); + } catch (InterruptedException e) { + Log.i(TAG, "select thread was interrupted (2), exiting"); + mInterrupted = true; + } + } + } + else { + Log.i(TAG, "connect notification!"); + /* A device connected (most likely just one, but + it is possible for two separate devices, one + a headset and one a handsfree, to connect + simultaneously. + */ + if (mConnectingHeadsetRfcommChannel >= 0) { + Log.i(TAG, "Incoming connection from headset " + + mConnectingHeadsetAddress + " on channel " + + mConnectingHeadsetRfcommChannel); + Message msg = Message.obtain(mCallback); + msg.what = MSG_INCOMING_HEADSET_CONNECTION; + msg.obj = + new IncomingConnectionInfo( + mBluetooth, + mConnectingHeadsetAddress, + mConnectingHeadsetSocketFd, + mConnectingHeadsetRfcommChannel); + msg.sendToTarget(); + } + if (mConnectingHandsfreeRfcommChannel >= 0) { + Log.i(TAG, "Incoming connection from handsfree " + + mConnectingHandsfreeAddress + " on channel " + + mConnectingHandsfreeRfcommChannel); + Message msg = Message.obtain(); + msg.setTarget(mCallback); + msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; + msg.obj = + new IncomingConnectionInfo( + mBluetooth, + mConnectingHandsfreeAddress, + mConnectingHandsfreeSocketFd, + mConnectingHandsfreeRfcommChannel); + msg.sendToTarget(); + } + } + } + if (DBG) log("Connect Thread finished"); + } + }; + + if (setUpListeningSocketsNative() == false) { + Log.e(TAG, "Could not set up listening socket, exiting"); + return false; + } + + mInterrupted = false; + mConnectThead.start(); + } + + return true; + } + + public synchronized void stop() { + if (mConnectThead != null) { + if (DBG) log("stopping Connect Thread"); + mInterrupted = true; + try { + mConnectThead.interrupt(); + if (DBG) log("waiting for thread to terminate"); + mConnectThead.join(); + mConnectThead = null; + mCallback = null; + tearDownListeningSocketsNative(); + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted waiting for Connect Thread to join"); + } + } + } + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + + private static native void classInitNative(); + private native void initializeNativeDataNative(); + private native void cleanupNativeDataNative(); + private native boolean waitForHandsfreeConnectNative(int timeoutMs); + private native boolean setUpListeningSocketsNative(); + private native void tearDownListeningSocketsNative(); + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java new file mode 100644 index 00000000000..88ce18b4577 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2008 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; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Static helper methods and constants to decode the device class bit vector + * returned by the Bluetooth API. + * + * The Android Bluetooth API returns a 32-bit integer to represent the class. + * The format of these bits is defined at + * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + * (login required). This class provides static helper methods and constants to + * determine what Service Class(es) and Device Class are encoded in the 32-bit + * class. + * + * Devices typically have zero or more service classes, and exactly one device + * class. The device class is encoded as a major and minor device class, the + * minor being a subset of the major. + * + * Class is useful to describe a device (for example to show an icon), + * but does not reliably describe what profiles a device supports. To determine + * profile support you usually need to perform SDP queries. + * + * Each of these helper methods takes the 32-bit integer class as an argument. + * + * @hide + */ +public class BluetoothClass { + /** Indicates the Bluetooth API could not retrieve the class */ + public static final int ERROR = 0xFF000000; + + /** Every Bluetooth device has zero or more service classes */ + public static class Service { + public static final int BITMASK = 0xFFE000; + + public static final int LIMITED_DISCOVERABILITY = 0x002000; + public static final int POSITIONING = 0x010000; + public static final int NETWORKING = 0x020000; + public static final int RENDER = 0x040000; + public static final int CAPTURE = 0x080000; + public static final int OBJECT_TRANSFER = 0x100000; + public static final int AUDIO = 0x200000; + public static final int TELEPHONY = 0x400000; + public static final int INFORMATION = 0x800000; + + /** Returns true if the given class supports the given Service Class. + * A bluetooth device can claim to support zero or more service classes. + * @param btClass The bluetooth class. + * @param serviceClass The service class constant to test for. For + * example, Service.AUDIO. Must be one of the + * Service.FOO constants. + * @return True if the service class is supported. + */ + public static boolean hasService(int btClass, int serviceClass) { + if (btClass == ERROR) { + return false; + } + return ((btClass & Service.BITMASK & serviceClass) != 0); + } + } + + /** Every Bluetooth device has exactly one device class, comprimised of + * major and minor components. We have not included the minor classes for + * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work + * a little differently. */ + public static class Device { + public static final int BITMASK = 0x1FFC; + + public static class Major { + public static final int BITMASK = 0x1F00; + + public static final int MISC = 0x0000; + public static final int COMPUTER = 0x0100; + public static final int PHONE = 0x0200; + public static final int NETWORKING = 0x0300; + public static final int AUDIO_VIDEO = 0x0400; + public static final int PERIPHERAL = 0x0500; + public static final int IMAGING = 0x0600; + public static final int WEARABLE = 0x0700; + public static final int TOY = 0x0800; + public static final int HEALTH = 0x0900; + public static final int UNCATEGORIZED = 0x1F00; + + /** Returns the Major Device Class component of a bluetooth class. + * Values returned from this function can be compared with the constants + * Device.Major.FOO. A bluetooth device can only be associated + * with one major class. + */ + public static int getDeviceMajor(int btClass) { + if (btClass == ERROR) { + return ERROR; + } + return (btClass & Device.Major.BITMASK); + } + } + + // Devices in the COMPUTER major class + public static final int COMPUTER_UNCATEGORIZED = 0x0100; + public static final int COMPUTER_DESKTOP = 0x0104; + public static final int COMPUTER_SERVER = 0x0108; + public static final int COMPUTER_LAPTOP = 0x010C; + public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; + public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; + public static final int COMPUTER_WEARABLE = 0x0118; + + // Devices in the PHONE major class + public static final int PHONE_UNCATEGORIZED = 0x0200; + public static final int PHONE_CELLULAR = 0x0204; + public static final int PHONE_CORDLESS = 0x0208; + public static final int PHONE_SMART = 0x020C; + public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; + public static final int PHONE_ISDN = 0x0214; + + // Minor classes for the AUDIO_VIDEO major class + public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; + public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; + public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; + //public static final int AUDIO_VIDEO_RESERVED = 0x040C; + public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; + public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; + public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; + public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; + public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; + public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; + public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; + public static final int AUDIO_VIDEO_VCR = 0x042C; + public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; + public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; + public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; + public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; + public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; + //public static final int AUDIO_VIDEO_RESERVED = 0x0444; + public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; + + // Devices in the WEARABLE major class + public static final int WEARABLE_UNCATEGORIZED = 0x0700; + public static final int WEARABLE_WRIST_WATCH = 0x0704; + public static final int WEARABLE_PAGER = 0x0708; + public static final int WEARABLE_JACKET = 0x070C; + public static final int WEARABLE_HELMET = 0x0710; + public static final int WEARABLE_GLASSES = 0x0714; + + // Devices in the TOY major class + public static final int TOY_UNCATEGORIZED = 0x0800; + public static final int TOY_ROBOT = 0x0804; + public static final int TOY_VEHICLE = 0x0808; + public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; + public static final int TOY_CONTROLLER = 0x0810; + public static final int TOY_GAME = 0x0814; + + // Devices in the HEALTH major class + public static final int HEALTH_UNCATEGORIZED = 0x0900; + public static final int HEALTH_BLOOD_PRESSURE = 0x0904; + public static final int HEALTH_THERMOMETER = 0x0908; + public static final int HEALTH_WEIGHING = 0x090C; + public static final int HEALTH_GLUCOSE = 0x0910; + public static final int HEALTH_PULSE_OXIMETER = 0x0914; + public static final int HEALTH_PULSE_RATE = 0x0918; + public static final int HEALTH_DATA_DISPLAY = 0x091C; + + /** Returns the Device Class component of a bluetooth class. This includes + * both the major and minor device components. Values returned from this + * function can be compared with the constants Device.FOO. A bluetooth + * device can only be associated with one device class. + */ + public static int getDevice(int btClass) { + if (btClass == ERROR) { + return ERROR; + } + return (btClass & Device.BITMASK); + } + } +} + diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java new file mode 100644 index 00000000000..1ba1c1e86f7 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -0,0 +1,580 @@ +/* + * Copyright (C) 2008 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.os.RemoteException; +import android.util.Log; + +import java.io.UnsupportedEncodingException; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Manages the local Bluetooth device. Scan for devices, create bondings, + * power up and down the adapter. + * + * @hide + */ +public class BluetoothDevice { + /** Inquiry scan and page scan are both off. + * Device is neither discoverable nor connectable */ + public static final int SCAN_MODE_NONE = 0; + /** Page scan is on, inquiry scan is off. + * Device is connectable, but not discoverable */ + public static final int SCAN_MODE_CONNECTABLE = 1; + /** Page scan and inquiry scan are on. + * Device is connectable and discoverable */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; + + public static final int RESULT_FAILURE = -1; + public static final int RESULT_SUCCESS = 0; + + /** We do not have a link key for the remote device, and are therefore not + * bonded */ + public static final int BOND_NOT_BONDED = 0; + /** We have a link key for the remote device, and are probably bonded. */ + public static final int BOND_BONDED = 1; + /** We are currently attempting bonding */ + public static final int BOND_BONDING = 2; + + //TODO: Unify these result codes in BluetoothResult or BluetoothError + /** A bond attempt failed because pins did not match, or remote device did + * not respond to pin request in time */ + public static final int UNBOND_REASON_AUTH_FAILED = 1; + /** A bond attempt failed because the other side explicilty rejected + * bonding */ + public static final int UNBOND_REASON_AUTH_REJECTED = 2; + /** A bond attempt failed because we canceled the bonding process */ + public static final int UNBOND_REASON_AUTH_CANCELED = 3; + /** A bond attempt failed because we could not contact the remote device */ + public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; + /** A bond attempt failed because a discovery is in progress */ + public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + /** An existing bond was explicitly revoked */ + public static final int UNBOND_REASON_REMOVED = 6; + + private static final String TAG = "BluetoothDevice"; + + private final IBluetoothDevice mService; + /** + * @hide - hide this because it takes a parameter of type + * IBluetoothDevice, which is a System private class. + * Also note that Context.getSystemService is a factory that + * returns a BlueToothDevice. That is the right way to get + * a BluetoothDevice. + */ + public BluetoothDevice(IBluetoothDevice service) { + mService = service; + } + + /** + * Get the current status of Bluetooth hardware. + * + * @return true if Bluetooth enabled, false otherwise. + */ + public boolean isEnabled() { + try { + return mService.isEnabled(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Enable the Bluetooth device. + * Turn on the underlying hardware. + * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be + * sent if and when the device is successfully enabled. + * @return false if we cannot enable the Bluetooth device. True does not + * imply the device was enabled, it only implies that so far there were no + * problems. + */ + public boolean enable() { + return enable(null); + } + + /** + * Enable the Bluetooth device. + * Turns on the underlying hardware. + * This is an asynchronous call. onEnableResult() of your callback will be + * called when the call is complete, with either RESULT_SUCCESS or + * RESULT_FAILURE. + * + * Your callback will be called from a binder thread, not the main thread. + * + * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be + * broadcast if the device is successfully enabled. + * + * @param callback Your callback, null is ok. + * @return true if your callback was successfully registered, or false if + * there was an error, implying your callback will never be called. + */ + public boolean enable(IBluetoothDeviceCallback callback) { + try { + return mService.enable(callback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Disable the Bluetooth device. + * This turns off the underlying hardware. + * + * @return true if successful, false otherwise. + */ + public boolean disable() { + try { + return mService.disable(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getAddress() { + try { + return mService.getAddress(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. Currently it is only + * possible to retrieve the Bluetooth name when Bluetooth is enabled. + * + * @return the Bluetooth name, or null if there was a problem. + */ + public String getName() { + try { + return mService.getName(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Set the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. The Bluetooth Service + * is responsible for persisting this name. + * + * @param name the name to set + * @return true, if the name was successfully set. False otherwise. + */ + public boolean setName(String name) { + try { + return mService.setName(name); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getVersion() { + try { + return mService.getVersion(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRevision() { + try { + return mService.getRevision(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getManufacturer() { + try { + return mService.getManufacturer(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getCompany() { + try { + return mService.getCompany(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the current scan mode. + * Used to determine if the local device is connectable and/or discoverable + * @return Scan mode, one of SCAN_MODE_* or an error code + */ + public int getScanMode() { + try { + return mService.getScanMode(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; + } + + /** + * Set the current scan mode. + * Used to make the local device connectable and/or discoverable + * @param scanMode One of SCAN_MODE_* + */ + public void setScanMode(int scanMode) { + try { + mService.setScanMode(scanMode); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public int getDiscoverableTimeout() { + try { + return mService.getDiscoverableTimeout(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return -1; + } + public void setDiscoverableTimeout(int timeout) { + try { + mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean startDiscovery() { + return startDiscovery(true); + } + public boolean startDiscovery(boolean resolveNames) { + try { + return mService.startDiscovery(resolveNames); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public void cancelDiscovery() { + try { + mService.cancelDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean isDiscovering() { + try { + return mService.isDiscovering(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean startPeriodicDiscovery() { + try { + return mService.startPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean stopPeriodicDiscovery() { + try { + return mService.stopPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean isPeriodicDiscovery() { + try { + return mService.isPeriodicDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String[] listRemoteDevices() { + try { + return mService.listRemoteDevices(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * List remote devices that have a low level (ACL) connection. + * + * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have + * an ACL connection even when not paired - this is common for SDP queries + * or for in-progress pairing requests. + * + * In most cases you probably want to test if a higher level protocol is + * connected, rather than testing ACL connections. + * + * @return bluetooth hardware addresses of remote devices with a current + * ACL connection. Array size is 0 if no devices have a + * connection. Null on error. + */ + public String[] listAclConnections() { + try { + return mService.listAclConnections(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Check if a specified remote device has a low level (ACL) connection. + * + * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have + * an ACL connection even when not paired - this is common for SDP queries + * or for in-progress pairing requests. + * + * In most cases you probably want to test if a higher level protocol is + * connected, rather than testing ACL connections. + * + * @param address the Bluetooth hardware address you want to check. + * @return true if there is an ACL connection, false otherwise and on + * error. + */ + public boolean isAclConnected(String address) { + try { + return mService.isAclConnected(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Perform a low level (ACL) disconnection of a remote device. + * + * This forcably disconnects the ACL layer connection to a remote device, + * which will cause all RFCOMM, SDP and L2CAP connections to this remote + * device to close. + * + * @param address the Bluetooth hardware address you want to disconnect. + * @return true if the device was disconnected, false otherwise and on + * error. + */ + public boolean disconnectRemoteDeviceAcl(String address) { + try { + return mService.disconnectRemoteDeviceAcl(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Create a bonding with a remote bluetooth device. + * + * This is an asynchronous call. The result of this bonding attempt can be + * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. + * + * @param address the remote device Bluetooth address. + * @return false If there was an immediate problem creating the bonding, + * true otherwise. + */ + public boolean createBond(String address) { + try { + return mService.createBond(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Cancel an in-progress bonding request started with createBond. + */ + public boolean cancelBondProcess(String address) { + try { + return mService.cancelBondProcess(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Remove an already exisiting bonding (delete the link key). + */ + public boolean removeBond(String address) { + try { + return mService.removeBond(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * List remote devices that are bonded (paired) to the local device. + * + * Bonding (pairing) is the process by which the user enters a pin code for + * the device, which generates a shared link key, allowing for + * authentication and encryption of future connections. In Android we + * require bonding before RFCOMM or SCO connections can be made to a remote + * device. + * + * This function lists which remote devices we have a link key for. It does + * not cause any RF transmission, and does not check if the remote device + * still has it's link key with us. If the other side no longer has its + * link key then the RFCOMM or SCO connection attempt will result in an + * error. + * + * This function does not check if the remote device is in range. + * + * Remote devices that have an in-progress bonding attempt are not + * returned. + * + * @return bluetooth hardware addresses of remote devices that are + * bonded. Array size is 0 if no devices are bonded. Null on error. + */ + public String[] listBonds() { + try { + return mService.listBonds(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the bonding state of a remote device. + * + * Result is one of: + * BluetoothError.* + * BOND_* + * + * @param address Bluetooth hardware address of the remote device to check. + * @return Result code + */ + public int getBondState(String address) { + try { + return mService.getBondState(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; + } + + public String getRemoteName(String address) { + try { + return mService.getRemoteName(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + public String getRemoteVersion(String address) { + try { + return mService.getRemoteVersion(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteRevision(String address) { + try { + return mService.getRemoteRevision(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteManufacturer(String address) { + try { + return mService.getRemoteManufacturer(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String getRemoteCompany(String address) { + try { + return mService.getRemoteCompany(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Returns the RFCOMM channel associated with the 16-byte UUID on + * the remote Bluetooth address. + * + * Performs a SDP ServiceSearchAttributeRequest transaction. The provided + * uuid is verified in the returned record. If there was a problem, or the + * specified uuid does not exist, -1 is returned. + */ + public boolean getRemoteServiceChannel(String address, short uuid16, + IBluetoothDeviceCallback callback) { + try { + return mService.getRemoteServiceChannel(address, uuid16, callback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the major, minor and servics classes of a remote device. + * These classes are encoded as a 32-bit integer. See BluetoothClass. + * @param address remote device + * @return 32-bit class suitable for use with BluetoothClass. + */ + public int getRemoteClass(String address) { + try { + return mService.getRemoteClass(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothClass.ERROR; + } + + public byte[] getRemoteFeatures(String address) { + try { + return mService.getRemoteFeatures(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String lastSeen(String address) { + try { + return mService.lastSeen(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + public String lastUsed(String address) { + try { + return mService.lastUsed(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + public boolean setPin(String address, byte[] pin) { + try { + return mService.setPin(address, pin); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + public boolean cancelPin(String address) { + try { + return mService.cancelPin(address); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Check that a pin is valid and convert to byte array. + * + * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. + * @param pin pin as java String + * @return the pin code as a UTF8 byte array, or null if it is an invalid + * Bluetooth pin. + */ + public static byte[] convertPinToBytes(String pin) { + if (pin == null) { + return null; + } + byte[] pinBytes; + try { + pinBytes = pin.getBytes("UTF8"); + } catch (UnsupportedEncodingException uee) { + Log.e(TAG, "UTF8 not supported?!?"); // this should not happen + return null; + } + if (pinBytes.length <= 0 || pinBytes.length > 16) { + return null; + } + return pinBytes; + } + + + private static final int ADDRESS_LENGTH = 17; + /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ + public static boolean checkBluetoothAddress(String address) { + if (address == null || address.length() != ADDRESS_LENGTH) { + return false; + } + for (int i = 0; i < ADDRESS_LENGTH; i++) { + char c = address.charAt(i); + switch (i % 3) { + case 0: + case 1: + if (Character.digit(c, 16) != -1) { + break; // hex character, OK + } + return false; + case 2: + if (c == ':') { + break; // OK + } + return false; + } + } + return true; + } +} diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/BluetoothError.java new file mode 100644 index 00000000000..2554bead0ca --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothError.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 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; + +/** + * Bluetooth API error codes. + * + * Errors are always negative. + * + * @hide + */ +public class BluetoothError { + /** No error */ + public static final int SUCCESS = 0; + + /** Generic error */ + public static final int ERROR = -1000; + + /** Bluetooth currently disabled */ + public static final int ERROR_DISABLED = -1001; + + /** IPC is not ready, for example service is not yet bound */ + public static final int ERROR_IPC_NOT_READY = -1011; + + /** Some other IPC error, for example a RemoteException */ + public static final int ERROR_IPC = -1012; + +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java new file mode 100644 index 00000000000..34196bf8dea --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2008 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Public API for controlling the Bluetooth Headset Service. This includes both + * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will + * attempt a handsfree connection first, and fall back to headset. + * + * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset + * Service via IPC. + * + * Creating a BluetoothHeadset object will create a binding with the + * BluetoothHeadset service. Users of this object should call close() when they + * are finished with the BluetoothHeadset, so that this proxy object can unbind + * from the service. + * + * This BluetoothHeadset object is not immediately bound to the + * BluetoothHeadset service. Use the ServiceListener interface to obtain a + * notification when it is bound, this is especially important if you wish to + * immediately call methods on BluetootHeadset after construction. + * + * Android only supports one connected Bluetooth Headset at a time. + * + * @hide + */ +public class BluetoothHeadset { + + private static final String TAG = "BluetoothHeadset"; + private static final boolean DBG = false; + + private IBluetoothHeadset mService; + private final Context mContext; + private final ServiceListener mServiceListener; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + /** No headset currently connected */ + public static final int STATE_DISCONNECTED = 0; + /** Connection attempt in progress */ + public static final int STATE_CONNECTING = 1; + /** A headset is currently connected */ + public static final int STATE_CONNECTED = 2; + + public static final int RESULT_FAILURE = 0; + public static final int RESULT_SUCCESS = 1; + /** Connection canceled before completetion. */ + public static final int RESULT_CANCELED = 2; + + /** Default priority for headsets that should be auto-connected */ + public static final int PRIORITY_AUTO = 100; + /** Default priority for headsets that should not be auto-connected */ + public static final int PRIORITY_OFF = 0; + + /** + * An interface for notifying BluetoothHeadset IPC clients when they have + * been connected to the BluetoothHeadset service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothHeadset service. Clients must wait for + * this callback before making IPC calls on the BluetoothHeadset + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothHeadset service. Clients must not + * make IPC calls on the BluetoothHeadset service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothHeadset service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + /** + * Create a BluetoothHeadset proxy object. + */ + public BluetoothHeadset(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service"); + } + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothHeadset will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized void close() { + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } + } + + /** + * Get the current state of the Bluetooth Headset service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Headset service. + */ + public int getState() { + if (mService != null) { + try { + return mService.getState(); + } 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 BluetoothHeadset.STATE_ERROR; + } + + /** + * Get the Bluetooth address of the current headset. + * @return The Bluetooth address, or null if not in connected or connecting + * state, or if this proxy object is not connected to the Headset + * service. + */ + public String getHeadsetAddress() { + if (mService != null) { + try { + return mService.getHeadsetAddress(); + } 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 null; + } + + /** + * Request to initiate a connection to a headset. + * This call does not block. Fails if a headset is already connecting + * or connected. + * Initiates auto-connection if address is null. Tries to connect to all + * devices with priority greater than PRIORITY_AUTO in descending order. + * @param address The Bluetooth Address to connect to, or null to + * auto-connect to the last connected headset. + * @return False if there was a problem initiating the connection + * procedure, and no further HEADSET_STATE_CHANGED intents + * will be expected. + */ + public boolean connectHeadset(String address) { + if (mService != null) { + try { + if (mService.connectHeadset(address)) { + return true; + } + } 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; + } + + /** + * Returns true if the specified headset is connected (does not include + * connecting). Returns false if not connected, or if this proxy object + * if not currently connected to the headset service. + */ + public boolean isConnected(String address) { + if (mService != null) { + try { + return mService.isConnected(address); + } 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; + } + + /** + * Disconnects the current headset. Currently this call blocks, it may soon + * be made asynchornous. Returns false if this proxy object is + * not currently connected to the Headset service. + */ + public boolean disconnectHeadset() { + if (mService != null) { + try { + mService.disconnectHeadset(); + return true; + } 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; + } + + /** + * Start BT Voice Recognition mode, and set up Bluetooth audio path. + * Returns false if there is no headset connected, or if the + * connected headset does not support voice recognition, or on + * error. + */ + public boolean startVoiceRecognition() { + if (mService != null) { + try { + return mService.startVoiceRecognition(); + } 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; + } + + /** + * Stop BT Voice Recognition mode, and shut down Bluetooth audio path. + * Returns false if there is no headset connected, or the connected + * headset is not in voice recognition mode, or on error. + */ + public boolean stopVoiceRecognition() { + if (mService != null) { + try { + return mService.stopVoiceRecognition(); + } 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; + } + + /** + * Set priority of headset. + * Priority is a non-negative integer. By default paired headsets will have + * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). + * Headsets with priority greater than zero will be auto-connected, and + * incoming connections will be accepted (if no other headset is + * connected). + * Auto-connection occurs at the following events: boot, incoming phone + * call, outgoing phone call. + * Headsets with priority equal to zero, or that are unpaired, are not + * auto-connected. + * Incoming connections are ignored regardless of priority if there is + * already a headset connected. + * @param address Paired headset + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return True if successful, false if there was some error. + */ + public boolean setPriority(String address, int priority) { + if (mService != null) { + try { + return mService.setPriority(address, priority); + } 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; + } + + /** + * Get priority of headset. + * @param address Headset + * @return non-negative priority, or negative error code on error. + */ + public int getPriority(String address) { + if (mService != null) { + try { + return mService.getPriority(address); + } 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 -1; + } + + /** + * Check class bits for possible HSP or HFP support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support HSP or HFP. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support HSP or HFP. + */ + public static boolean doesClassMatch(int btClass) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // Just in case they forgot the render service class + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothHeadset.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(); + } + } + }; +} diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java new file mode 100644 index 00000000000..b66b06ef59c --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Manages the local Bluetooth device. Scan for devices, create bondings, + * power up and down the adapter. + * + * @hide + */ +public interface BluetoothIntent { + public static final String SCAN_MODE = + "android.bluetooth.intent.SCAN_MODE"; + public static final String ADDRESS = + "android.bluetooth.intent.ADDRESS"; + public static final String NAME = + "android.bluetooth.intent.NAME"; + public static final String ALIAS = + "android.bluetooth.intent.ALIAS"; + public static final String RSSI = + "android.bluetooth.intent.RSSI"; + public static final String CLASS = + "android.bluetooth.intent.CLASS"; + public static final String HEADSET_STATE = + "android.bluetooth.intent.HEADSET_STATE"; + public static final String HEADSET_PREVIOUS_STATE = + "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; + public static final String BOND_STATE = + "android.bluetooth.intent.BOND_STATE"; + public static final String BOND_PREVIOUS_STATE = + "android.bluetooth.intent.BOND_PREVIOUS_STATE"; + public static final String REASON = + "android.bluetooth.intent.REASON"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ENABLED_ACTION = + "android.bluetooth.intent.action.ENABLED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISABLED_ACTION = + "android.bluetooth.intent.action.DISABLED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String NAME_CHANGED_ACTION = + "android.bluetooth.intent.action.NAME_CHANGED"; + + /** + * Broadcast when the scan mode changes. Always contains an int extra + * named SCAN_MODE that contains the new scan mode. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SCAN_MODE_CHANGED_ACTION = + "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISCOVERY_STARTED_ACTION = + "android.bluetooth.intent.action.DISCOVERY_STARTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DISCOVERY_COMPLETED_ACTION = + "android.bluetooth.intent.action.DISCOVERY_COMPLETED"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String PAIRING_REQUEST_ACTION = + "android.bluetooth.intent.action.PAIRING_REQUEST"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String PAIRING_CANCEL_ACTION = + "android.bluetooth.intent.action.PAIRING_CANCEL"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_FOUND_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISAPPEARED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_CONNECTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_DEVICE_DISCONNECTED_ACTION = + "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_NAME_UPDATED_ACTION = + "android.bluetooth.intent.action.REMOTE_NAME_UPDATED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String REMOTE_NAME_FAILED_ACTION = + "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; + + /** + * Broadcast when the bond state of a remote device changes. + * Has string extra ADDRESS and int extras BOND_STATE and + * BOND_PREVIOUS_STATE. + * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will + * also have an int extra REASON with a value of: + * BluetoothDevice.BOND_RESULT_* + * */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String BOND_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String HEADSET_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; +} diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java new file mode 100644 index 00000000000..fef641a5f7c --- /dev/null +++ b/framework/java/android/bluetooth/Database.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2007 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.bluetooth.RfcommSocket; + +import android.util.Log; + +import java.io.*; +import java.util.*; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * A low-level API to the Service Discovery Protocol (SDP) Database. + * + * Allows service records to be added to the local SDP database. Once added, + * these services will be advertised to remote devices when they make SDP + * queries on this device. + * + * Currently this API is a thin wrapper to the bluez SDP Database API. See: + * http://wiki.bluez.org/wiki/Database + * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords + * @hide + */ +public final class Database { + private static Database mInstance; + + private static final String sLogName = "android.bluetooth.Database"; + + /** + * Class load time initialization + */ + static { + classInitNative(); + } + private native static void classInitNative(); + + /** + * Private to enforce singleton property + */ + private Database() { + initializeNativeDataNative(); + } + private native void initializeNativeDataNative(); + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + private native void cleanupNativeDataNative(); + + /** + * Singelton accessor + * @return The singleton instance of Database + */ + public static synchronized Database getInstance() { + if (mInstance == null) { + mInstance = new Database(); + } + return mInstance; + } + + /** + * Advertise a service with an RfcommSocket. + * + * This adds the service the SDP Database with the following attributes + * set: Service Name, Protocol Descriptor List, Service Class ID List + * TODO: Construct a byte[] record directly, rather than via XML. + * @param socket The rfcomm socket to advertise (by channel). + * @param serviceName A short name for this service + * @param uuid + * Unique identifier for this service, by which clients + * can search for your service + * @return Handle to the new service record + */ + public int advertiseRfcommService(RfcommSocket socket, + String serviceName, + UUID uuid) throws IOException { + String xmlRecord = + "\n" + + "\n" + + " \n" + // ServiceClassIDList + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // ProtocolDescriptorList + " \n" + + " \n" + + " \n" + // L2CAP + " \n" + + " \n" + + " \n" + // RFCOMM + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + // ServiceName + " \n" + + " \n" + + "\n"; + Log.i(sLogName, xmlRecord); + return addServiceRecordFromXml(xmlRecord); + } + + + /** + * Add a new service record. + * @param record The byte[] record + * @return A handle to the new record + */ + public synchronized int addServiceRecord(byte[] record) throws IOException { + int handle = addServiceRecordNative(record); + Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); + return handle; + } + private native int addServiceRecordNative(byte[] record) + throws IOException; + + /** + * Add a new service record, using XML. + * @param record The record as an XML string + * @return A handle to the new record + */ + public synchronized int addServiceRecordFromXml(String record) throws IOException { + int handle = addServiceRecordFromXmlNative(record); + Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); + return handle; + } + private native int addServiceRecordFromXmlNative(String record) + throws IOException; + + /** + * Update an exisiting service record. + * @param handle Handle to exisiting record + * @param record The updated byte[] record + */ + public synchronized void updateServiceRecord(int handle, byte[] record) { + try { + updateServiceRecordNative(handle, record); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void updateServiceRecordNative(int handle, byte[] record) + throws IOException; + + /** + * Update an exisiting record, using XML. + * @param handle Handle to exisiting record + * @param record The record as an XML string. + */ + public synchronized void updateServiceRecordFromXml(int handle, String record) { + try { + updateServiceRecordFromXmlNative(handle, record); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void updateServiceRecordFromXmlNative(int handle, String record) + throws IOException; + + /** + * Remove a service record. + * It is only possible to remove service records that were added by the + * current connection. + * @param handle Handle to exisiting record to be removed + */ + public synchronized void removeServiceRecord(int handle) { + try { + removeServiceRecordNative(handle); + } catch (IOException e) { + Log.e(getClass().toString(), e.getMessage()); + } + } + private native void removeServiceRecordNative(int handle) throws IOException; +} diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java new file mode 100644 index 00000000000..fd2d2ab8848 --- /dev/null +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2007 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.os.Handler; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * The base RFCOMM (service) connection for a headset or handsfree device. + * + * In the future this class will be removed. + * + * @hide + */ +public class HeadsetBase { + private static final String TAG = "Bluetooth HeadsetBase"; + private static final boolean DBG = false; + + public static final int RFCOMM_DISCONNECTED = 1; + + public static final int DIRECTION_INCOMING = 1; + public static final int DIRECTION_OUTGOING = 2; + + private final BluetoothDevice mBluetooth; + private final String mAddress; + private final int mRfcommChannel; + private int mNativeData; + private Thread mEventThread; + private volatile boolean mEventThreadInterrupted; + private Handler mEventThreadHandler; + private int mTimeoutRemainingMs; + private final int mDirection; + private final long mConnectTimestamp; + + protected AtParser mAtParser; + + private WakeLock mWakeLock; // held while processing an AT command + + private native static void classInitNative(); + static { + classInitNative(); + } + + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + releaseWakeLock(); + } finally { + super.finalize(); + } + } + + private native void cleanupNativeDataNative(); + + public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, + int rfcommChannel) { + mDirection = DIRECTION_OUTGOING; + mConnectTimestamp = System.currentTimeMillis(); + mBluetooth = bluetooth; + mAddress = address; + mRfcommChannel = rfcommChannel; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); + mWakeLock.setReferenceCounted(false); + initializeAtParser(); + // Must be called after this.mAddress is set. + initializeNativeDataNative(-1); + } + + /* Create from an already exisiting rfcomm connection */ + public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, + int rfcommChannel, Handler handler) { + mDirection = DIRECTION_INCOMING; + mConnectTimestamp = System.currentTimeMillis(); + mBluetooth = bluetooth; + mAddress = address; + mRfcommChannel = rfcommChannel; + mEventThreadHandler = handler; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); + mWakeLock.setReferenceCounted(false); + initializeAtParser(); + // Must be called after this.mAddress is set. + initializeNativeDataNative(socketFd); + } + + private native void initializeNativeDataNative(int socketFd); + + /* Process an incoming AT command line + */ + protected synchronized void handleInput(String input) { + acquireWakeLock(); + long timestamp; + + if (DBG) timestamp = System.currentTimeMillis(); + AtCommandResult result = mAtParser.process(input); + if (DBG) Log.d(TAG, "Processing " + input + " took " + + (System.currentTimeMillis() - timestamp) + " ms"); + + if (result.getResultCode() == AtCommandResult.ERROR) { + Log.i(TAG, "Error pocessing <" + input + ">"); + } + + sendURC(result.toString()); + + releaseWakeLock(); + } + + /** + * Register AT commands that are common to all Headset / Handsets. This + * function is called by the HeadsetBase constructor. + */ + protected void initializeAtParser() { + mAtParser = new AtParser(); + //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, + //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree + } + + public AtParser getAtParser() { + return mAtParser; + } + + public void startEventThread() { + mEventThread = + new Thread("HeadsetBase Event Thread") { + public void run() { + int last_read_error; + while (!mEventThreadInterrupted) { + String input = readNative(500); + if (input != null) { + handleInput(input); + } + else { + last_read_error = getLastReadStatusNative(); + if (last_read_error != 0) { + Log.i(TAG, "headset read error " + last_read_error); + if (mEventThreadHandler != null) { + mEventThreadHandler.obtainMessage(RFCOMM_DISCONNECTED) + .sendToTarget(); + } + disconnectNative(); + break; + } + } + } + } + }; + mEventThreadInterrupted = false; + mEventThread.start(); + } + + + + private native String readNative(int timeout_ms); + private native int getLastReadStatusNative(); + + private void stopEventThread() { + mEventThreadInterrupted = true; + mEventThread.interrupt(); + try { + mEventThread.join(); + } catch (java.lang.InterruptedException e) { + // FIXME: handle this, + } + mEventThread = null; + } + + public boolean connect(Handler handler) { + if (mEventThread == null) { + if (!connectNative()) return false; + mEventThreadHandler = handler; + } + return true; + } + private native boolean connectNative(); + + /* + * Returns true when either the asynchronous connect is in progress, or + * the connect is complete. Call waitForAsyncConnect() to find out whether + * the connect is actually complete, or disconnect() to cancel. + */ + + public boolean connectAsync() { + return connectAsyncNative(); + } + private native boolean connectAsyncNative(); + + public int getRemainingAsyncConnectWaitingTimeMs() { + return mTimeoutRemainingMs; + } + + /* + * Returns 1 when an async connect is complete, 0 on timeout, and -1 on + * error. On error, handler will be called, and you need to re-initiate + * the async connect. + */ + public int waitForAsyncConnect(int timeout_ms, Handler handler) { + int res = waitForAsyncConnectNative(timeout_ms); + if (res > 0) { + mEventThreadHandler = handler; + } + return res; + } + private native int waitForAsyncConnectNative(int timeout_ms); + + public void disconnect() { + if (mEventThread != null) { + stopEventThread(); + } + disconnectNative(); + } + private native void disconnectNative(); + + + /* + * Note that if a remote side disconnects, this method will still return + * true until disconnect() is called. You know when a remote side + * disconnects because you will receive the intent + * IBluetoothService.REMOTE_DEVICE_DISCONNECTED_ACTION. If, when you get + * this intent, method isConnected() returns true, you know that the + * disconnect was initiated by the remote device. + */ + + public boolean isConnected() { + return mEventThread != null; + } + + public String getAddress() { + return mAddress; + } + + public String getName() { + return mBluetooth.getRemoteName(mAddress); + } + + public int getDirection() { + return mDirection; + } + + public long getConnectTimestamp() { + return mConnectTimestamp; + } + + public synchronized boolean sendURC(String urc) { + if (urc.length() > 0) { + boolean ret = sendURCNative(urc); + return ret; + } + return true; + } + private native boolean sendURCNative(String urc); + + private void acquireWakeLock() { + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } + + private void releaseWakeLock() { + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + + private void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl new file mode 100644 index 00000000000..55ff27f97ec --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 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; + +/** + * System private API for Bluetooth A2DP service + * + * {@hide} + */ +interface IBluetoothA2dp { + int connectSink(in String address); + int disconnectSink(in String address); + List listConnectedSinks(); + int getSinkState(in String address); + int setSinkPriority(in String address, int priority); + int getSinkPriority(in String address); +} diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl new file mode 100644 index 00000000000..4351d2ebc38 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008, 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.bluetooth.IBluetoothDeviceCallback; + +/** + * System private API for talking with the Bluetooth service. + * + * {@hide} + */ +interface IBluetoothDevice +{ + boolean isEnabled(); + boolean enable(in IBluetoothDeviceCallback callback); // async + boolean disable(); + + String getAddress(); + String getName(); + boolean setName(in String name); + String getVersion(); + String getRevision(); + String getManufacturer(); + String getCompany(); + + int getScanMode(); + boolean setScanMode(int mode); + + int getDiscoverableTimeout(); + boolean setDiscoverableTimeout(int timeout); + + boolean startDiscovery(boolean resolveNames); + boolean cancelDiscovery(); + boolean isDiscovering(); + boolean startPeriodicDiscovery(); + boolean stopPeriodicDiscovery(); + boolean isPeriodicDiscovery(); + String[] listRemoteDevices(); + + String[] listAclConnections(); + boolean isAclConnected(in String address); + boolean disconnectRemoteDeviceAcl(in String address); + + boolean createBond(in String address); + boolean cancelBondProcess(in String address); + boolean removeBond(in String address); + String[] listBonds(); + int getBondState(in String address); + + String getRemoteName(in String address); + String getRemoteVersion(in String address); + String getRemoteRevision(in String address); + int getRemoteClass(in String address); + String getRemoteManufacturer(in String address); + String getRemoteCompany(in String address); + boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); + byte[] getRemoteFeatures(in String adddress); + String lastSeen(in String address); + String lastUsed(in String address); + + boolean setPin(in String address, in byte[] pin); + boolean cancelPin(in String address); +} diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl new file mode 100644 index 00000000000..d25bd560d03 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008, 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; + +/** + * {@hide} + */ +oneway interface IBluetoothDeviceCallback +{ + void onGetRemoteServiceChannelResult(in String address, int channel); + + void onEnableResult(int result); +} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl new file mode 100644 index 00000000000..582d4e340ee --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 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; + +/** + * System private API for Bluetooth Headset service + * + * {@hide} + */ +interface IBluetoothHeadset { + int getState(); + String getHeadsetAddress(); + boolean connectHeadset(in String address); + void disconnectHeadset(); + boolean isConnected(in String address); + boolean startVoiceRecognition(); + boolean stopVoiceRecognition(); + boolean setPriority(in String address, int priority); + int getPriority(in String address); +} diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java new file mode 100644 index 00000000000..a33263f5261 --- /dev/null +++ b/framework/java/android/bluetooth/RfcommSocket.java @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2007 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 java.io.IOException; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.FileDescriptor; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket + * is similar to a normal socket in that it takes an address and a port number. + * The difference is of course that the address is a Bluetooth-device address, + * and the port number is an RFCOMM channel. The API allows for the + * establishment of listening sockets via methods + * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and + * {@link #accept(RfcommSocket, int) accept}, as well as for the making of + * outgoing connections with {@link #connect(String, int) connect}, + * {@link #connectAsync(String, int) connectAsync}, and + * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. + * + * After constructing a socket, you need to {@link #create() create} it and then + * {@link #destroy() destroy} it when you are done using it. Both + * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return + * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. + * Alternatively, you may call {@link #getInputStream() getInputStream} and + * {@link #getOutputStream() getOutputStream} to retrieve the respective streams + * without going through the FileDescriptor. + * + * @hide + */ +public class RfcommSocket { + + /** + * Used by the native implementation of the class. + */ + private int mNativeData; + + /** + * Used by the native implementation of the class. + */ + private int mPort; + + /** + * Used by the native implementation of the class. + */ + private String mAddress; + + /** + * We save the return value of {@link #create() create} and + * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to + * retrieve the I/O streams. + */ + private FileDescriptor mFd; + + /** + * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, + * if the return value is zero, then, the the remaining time left to wait is + * written into this variable (by the native implementation). It is possible + * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before + * the user-specified timeout expires, which is why we save the remaining + * time in this member variable for the user to retrieve by calling method + * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. + */ + private int mTimeoutRemainingMs; + + /** + * Set to true when an asynchronous (nonblocking) connect is in progress. + * {@see #connectAsync(String,int)}. + */ + private boolean mIsConnecting; + + /** + * Set to true after a successful call to {@link #bind(String,int) bind} and + * used for error checking in {@link #listen(int) listen}. Reset to false + * on {@link #destroy() destroy}. + */ + private boolean mIsBound = false; + + /** + * Set to true after a successful call to {@link #listen(int) listen} and + * used for error checking in {@link #accept(RfcommSocket,int) accept}. + * Reset to false on {@link #destroy() destroy}. + */ + private boolean mIsListening = false; + + /** + * Used to store the remaining time after an accept with a non-negative + * timeout returns unsuccessfully. It is possible that a blocking + * {@link #accept(int) accept} may wait for less than the time specified by + * the user, which is why we store the remainder in this member variable for + * it to be retrieved with method + * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. + */ + private int mAcceptTimeoutRemainingMs; + + /** + * Maintained by {@link #getInputStream() getInputStream}. + */ + protected FileInputStream mInputStream; + + /** + * Maintained by {@link #getOutputStream() getOutputStream}. + */ + protected FileOutputStream mOutputStream; + + private native void initializeNativeDataNative(); + + /** + * Constructor. + */ + public RfcommSocket() { + initializeNativeDataNative(); + } + + private native void cleanupNativeDataNative(); + + /** + * Called by the GC to clean up the native data that we set up when we + * construct the object. + */ + protected void finalize() throws Throwable { + try { + cleanupNativeDataNative(); + } finally { + super.finalize(); + } + } + + private native static void classInitNative(); + + static { + classInitNative(); + } + + /** + * Creates a socket. You need to call this method before performing any + * other operation on a socket. + * + * @return FileDescriptor for the data stream. + * @throws IOException + * @see #destroy() + */ + public FileDescriptor create() throws IOException { + if (mFd == null) { + mFd = createNative(); + } + if (mFd == null) { + throw new IOException("socket not created"); + } + return mFd; + } + + private native FileDescriptor createNative(); + + /** + * Destroys a socket created by {@link #create() create}. Call this + * function when you no longer use the socket in order to release the + * underlying OS resources. + * + * @see #create() + */ + public void destroy() { + synchronized (this) { + destroyNative(); + mFd = null; + mIsBound = false; + mIsListening = false; + } + } + + private native void destroyNative(); + + /** + * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. + * + * @return the FileDescriptor + * @throws IOException + * when the socket has not been {@link #create() created}. + */ + public FileDescriptor getFileDescriptor() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + return mFd; + } + + /** + * Retrieves the input stream from the socket. Alternatively, you can do + * that from the FileDescriptor returned by {@link #create() create} or + * {@link #accept(RfcommSocket, int) accept}. + * + * @return InputStream + * @throws IOException + * if you have not called {@link #create() create} on the + * socket. + */ + public InputStream getInputStream() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + + synchronized (this) { + if (mInputStream == null) { + mInputStream = new FileInputStream(mFd); + } + + return mInputStream; + } + } + + /** + * Retrieves the output stream from the socket. Alternatively, you can do + * that from the FileDescriptor returned by {@link #create() create} or + * {@link #accept(RfcommSocket, int) accept}. + * + * @return OutputStream + * @throws IOException + * if you have not called {@link #create() create} on the + * socket. + */ + public OutputStream getOutputStream() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + + synchronized (this) { + if (mOutputStream == null) { + mOutputStream = new FileOutputStream(mFd); + } + + return mOutputStream; + } + } + + /** + * Starts a blocking connect to a remote RFCOMM socket. It takes the address + * of a device and the RFCOMM channel (port) to which to connect. + * + * @param address + * is the Bluetooth address of the remote device. + * @param port + * is the RFCOMM channel + * @return true on success, false on failure + * @throws IOException + * if {@link #create() create} has not been called. + * @see #connectAsync(String, int) + */ + public boolean connect(String address, int port) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return connectNative(address, port); + } + } + + private native boolean connectNative(String address, int port); + + /** + * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. + * It takes the address of the device to connect to, as well as the RFCOMM + * channel (port). On successful return (return value is true), you need to + * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to + * block for up to a specified number of milliseconds while waiting for the + * asyncronous connect to complete. + * + * @param address + * of remote device + * @param port + * the RFCOMM channel + * @return true when the asynchronous connect has successfully started, + * false if there was an error. + * @throws IOException + * is you have not called {@link #create() create} + * @see #waitForAsyncConnect(int) + * @see #getRemainingAsyncConnectWaitingTimeMs() + * @see #connect(String, int) + */ + public boolean connectAsync(String address, int port) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + mIsConnecting = connectAsyncNative(address, port); + return mIsConnecting; + } + } + + private native boolean connectAsyncNative(String address, int port); + + /** + * Interrupts an asynchronous connect in progress. This method does nothing + * when there is no asynchronous connect in progress. + * + * @throws IOException + * if you have not called {@link #create() create}. + * @see #connectAsync(String, int) + */ + public void interruptAsyncConnect() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (mIsConnecting) { + mIsConnecting = !interruptAsyncConnectNative(); + } + } + } + + private native boolean interruptAsyncConnectNative(); + + /** + * Tells you whether there is an asynchronous connect in progress. This + * method returns an undefined value when there is a synchronous connect in + * progress. + * + * @return true if there is an asyc connect in progress, false otherwise + * @see #connectAsync(String, int) + */ + public boolean isConnecting() { + return mIsConnecting; + } + + /** + * Blocks for a specified amount of milliseconds while waiting for an + * asynchronous connect to complete. Returns an integer value to indicate + * one of the following: the connect succeeded, the connect is still in + * progress, or the connect failed. It is possible for this method to block + * for less than the time specified by the user, and still return zero + * (i.e., async connect is still in progress.) For this reason, if the + * return value is zero, you need to call method + * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} + * to retrieve the remaining time. + * + * @param timeoutMs + * the time to block while waiting for the async connect to + * complete. + * @return a positive value if the connect succeeds; zero, if the connect is + * still in progress, and a negative value if the connect failed. + * + * @throws IOException + * @see #getRemainingAsyncConnectWaitingTimeMs() + * @see #connectAsync(String, int) + */ + public int waitForAsyncConnect(int timeoutMs) throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + int ret = waitForAsyncConnectNative(timeoutMs); + if (ret != 0) { + mIsConnecting = false; + } + return ret; + } + } + + private native int waitForAsyncConnectNative(int timeoutMs); + + /** + * Returns the number of milliseconds left to wait after the last call to + * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. + * + * It is possible that waitForAsyncConnect() waits for less than the time + * specified by the user, and still returns zero (i.e., async connect is + * still in progress.) For this reason, if the return value is zero, you + * need to call this method to retrieve the remaining time before you call + * waitForAsyncConnect again. + * + * @return the remaining timeout in milliseconds. + * @see #waitForAsyncConnect(int) + * @see #connectAsync(String, int) + */ + public int getRemainingAsyncConnectWaitingTimeMs() { + return mTimeoutRemainingMs; + } + + /** + * Shuts down both directions on a socket. + * + * @return true on success, false on failure; if the return value is false, + * the socket might be left in a patially shut-down state (i.e. one + * direction is shut down, but the other is still open.) In this + * case, you should {@link #destroy() destroy} and then + * {@link #create() create} the socket again. + * @throws IOException + * is you have not caled {@link #create() create}. + * @see #shutdownInput() + * @see #shutdownOutput() + */ + public boolean shutdown() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (shutdownNative(true)) { + return shutdownNative(false); + } + + return false; + } + } + + /** + * Shuts down the input stream of the socket, but leaves the output stream + * in its current state. + * + * @return true on success, false on failure + * @throws IOException + * is you have not called {@link #create() create} + * @see #shutdown() + * @see #shutdownOutput() + */ + public boolean shutdownInput() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return shutdownNative(true); + } + } + + /** + * Shut down the output stream of the socket, but leaves the input stream in + * its current state. + * + * @return true on success, false on failure + * @throws IOException + * is you have not called {@link #create() create} + * @see #shutdown() + * @see #shutdownInput() + */ + public boolean shutdownOutput() throws IOException { + synchronized (this) { + if (mFd == null) { + throw new IOException("socket not created"); + } + return shutdownNative(false); + } + } + + private native boolean shutdownNative(boolean shutdownInput); + + /** + * Tells you whether a socket is connected to another socket. This could be + * for input or output or both. + * + * @return true if connected, false otherwise. + * @see #isInputConnected() + * @see #isOutputConnected() + */ + public boolean isConnected() { + return isConnectedNative() > 0; + } + + /** + * Determines whether input is connected (i.e., whether you can receive data + * on this socket.) + * + * @return true if input is connected, false otherwise. + * @see #isConnected() + * @see #isOutputConnected() + */ + public boolean isInputConnected() { + return (isConnectedNative() & 1) != 0; + } + + /** + * Determines whether output is connected (i.e., whether you can send data + * on this socket.) + * + * @return true if output is connected, false otherwise. + * @see #isConnected() + * @see #isInputConnected() + */ + public boolean isOutputConnected() { + return (isConnectedNative() & 2) != 0; + } + + private native int isConnectedNative(); + + /** + * Binds a listening socket to the local device, or a non-listening socket + * to a remote device. The port is automatically selected as the first + * available port in the range 12 to 30. + * + * NOTE: Currently we ignore the device parameter and always bind the socket + * to the local device, assuming that it is a listening socket. + * + * TODO: Use bind(0) in native code to have the kernel select an unused + * port. + * + * @param device + * Bluetooth address of device to bind to (currently ignored). + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} + * @see #listen(int) + * @see #accept(RfcommSocket,int) + */ + public boolean bind(String device) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + for (int port = 12; port <= 30; port++) { + if (bindNative(device, port)) { + mIsBound = true; + return true; + } + } + mIsBound = false; + return false; + } + + /** + * Binds a listening socket to the local device, or a non-listening socket + * to a remote device. + * + * NOTE: Currently we ignore the device parameter and always bind the socket + * to the local device, assuming that it is a listening socket. + * + * @param device + * Bluetooth address of device to bind to (currently ignored). + * @param port + * RFCOMM channel to bind socket to. + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} + * @see #listen(int) + * @see #accept(RfcommSocket,int) + */ + public boolean bind(String device, int port) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + mIsBound = bindNative(device, port); + return mIsBound; + } + + private native boolean bindNative(String device, int port); + + /** + * Starts listening for incoming connections on this socket, after it has + * been bound to an address and RFCOMM channel with + * {@link #bind(String,int) bind}. + * + * @param backlog + * the number of pending incoming connections to queue for + * {@link #accept(RfcommSocket, int) accept}. + * @return true on success, false on failure + * @throws IOException + * if you have not called {@link #create() create} or if the + * socket has not been bound to a device and RFCOMM channel. + */ + public boolean listen(int backlog) throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (!mIsBound) { + throw new IOException("socket not bound"); + } + mIsListening = listenNative(backlog); + return mIsListening; + } + + private native boolean listenNative(int backlog); + + /** + * Accepts incoming-connection requests for a listening socket bound to an + * RFCOMM channel. The user may provide a time to wait for an incoming + * connection. + * + * Note that this method may return null (i.e., no incoming connection) + * before the user-specified timeout expires. For this reason, on a null + * return value, you need to call + * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} + * in order to see how much time is left to wait, before you call this + * method again. + * + * @param newSock + * is set to the new socket that is created as a result of a + * successful accept. + * @param timeoutMs + * time (in milliseconds) to block while waiting to an + * incoming-connection request. A negative value is an infinite + * wait. + * @return FileDescriptor of newSock on success, null on failure. Failure + * occurs if the timeout expires without a successful connect. + * @throws IOException + * if the socket has not been {@link #create() create}ed, is + * not bound, or is not a listening socket. + * @see #bind(String, int) + * @see #listen(int) + * @see #getRemainingAcceptWaitingTimeMs() + */ + public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) + throws IOException { + synchronized (newSock) { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (mIsListening == false) { + throw new IOException("not listening on socket"); + } + newSock.mFd = acceptNative(newSock, timeoutMs); + return newSock.mFd; + } + } + + /** + * Returns the number of milliseconds left to wait after the last call to + * {@link #accept(RfcommSocket, int) accept}. + * + * Since accept() may return null (i.e., no incoming connection) before the + * user-specified timeout expires, you need to call this method in order to + * see how much time is left to wait, and wait for that amount of time + * before you call accept again. + * + * @return the remaining time, in milliseconds. + */ + public int getRemainingAcceptWaitingTimeMs() { + return mAcceptTimeoutRemainingMs; + } + + private native FileDescriptor acceptNative(RfcommSocket newSock, + int timeoutMs); + + /** + * Get the port (rfcomm channel) associated with this socket. + * + * This is only valid if the port has been set via a successful call to + * {@link #bind(String, int)}, {@link #connect(String, int)} + * or {@link #connectAsync(String, int)}. This can be checked + * with {@link #isListening()} and {@link #isConnected()}. + * @return Port (rfcomm channel) + */ + public int getPort() throws IOException { + if (mFd == null) { + throw new IOException("socket not created"); + } + if (!mIsListening && !isConnected()) { + throw new IOException("not listening or connected on socket"); + } + return mPort; + } + + /** + * Return true if this socket is listening ({@link #listen(int)} + * has been called successfully). + */ + public boolean isListening() { + return mIsListening; + } +} diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java new file mode 100644 index 00000000000..a43a08be105 --- /dev/null +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2008 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.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Simple SCO Socket. + * Currently in Android, there is no support for sending data over a SCO + * socket - this is managed by the hardware link to the Bluetooth Chip. This + * class is instead intended for management of the SCO socket lifetime, + * and is tailored for use with the headset / handsfree profiles. + * @hide + */ +public class ScoSocket { + private static final String TAG = "ScoSocket"; + private static final boolean DBG = true; + private static final boolean VDBG = false; // even more logging + + public static final int STATE_READY = 1; // Ready for use. No threads or sockets + public static final int STATE_ACCEPT = 2; // accept() thread running + public static final int STATE_CONNECTING = 3; // connect() thread running + public static final int STATE_CONNECTED = 4; // connected, waiting for close() + public static final int STATE_CLOSED = 5; // was connected, now closed. + + private int mState; + private int mNativeData; + private Handler mHandler; + private int mAcceptedCode; + private int mConnectedCode; + private int mClosedCode; + + private WakeLock mWakeLock; // held while in STATE_CONNECTING + + static { + classInitNative(); + } + private native static void classInitNative(); + + public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode, + int closedCode) { + initNative(); + mState = STATE_READY; + mHandler = handler; + mAcceptedCode = acceptedCode; + mConnectedCode = connectedCode; + mClosedCode = closedCode; + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket"); + mWakeLock.setReferenceCounted(false); + if (VDBG) log(this + " SCO OBJECT CTOR"); + } + private native void initNative(); + + protected void finalize() throws Throwable { + try { + if (VDBG) log(this + " SCO OBJECT DTOR"); + destroyNative(); + releaseWakeLock(); + } finally { + super.finalize(); + } + } + private native void destroyNative(); + + /** Connect this SCO socket to the given BT address. + * Does not block. + */ + public synchronized boolean connect(String address) { + if (VDBG) log("connect() " + this); + if (mState != STATE_READY) { + if (DBG) log("connect(): Bad state"); + return false; + } + acquireWakeLock(); + if (connectNative(address)) { + mState = STATE_CONNECTING; + return true; + } else { + mState = STATE_CLOSED; + releaseWakeLock(); + return false; + } + } + private native boolean connectNative(String address); + + /** Accept incoming SCO connections. + * Does not block. + */ + public synchronized boolean accept() { + if (VDBG) log("accept() " + this); + if (mState != STATE_READY) { + if (DBG) log("Bad state"); + return false; + } + if (acceptNative()) { + mState = STATE_ACCEPT; + return true; + } else { + mState = STATE_CLOSED; + return false; + } + } + private native boolean acceptNative(); + + public synchronized void close() { + if (DBG) log(this + " SCO OBJECT close() mState = " + mState); + acquireWakeLock(); + mState = STATE_CLOSED; + closeNative(); + releaseWakeLock(); + } + private native void closeNative(); + + public synchronized int getState() { + return mState; + } + + private synchronized void onConnected(int result) { + if (VDBG) log(this + " onConnected() mState = " + mState + " " + this); + if (mState != STATE_CONNECTING) { + if (DBG) log("Strange state, closing " + mState + " " + this); + return; + } + if (result >= 0) { + mState = STATE_CONNECTED; + } else { + mState = STATE_CLOSED; + } + mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); + releaseWakeLock(); + } + + private synchronized void onAccepted(int result) { + if (VDBG) log("onAccepted() " + this); + if (mState != STATE_ACCEPT) { + if (DBG) log("Strange state " + this); + return; + } + if (result >= 0) { + mState = STATE_CONNECTED; + } else { + mState = STATE_CLOSED; + } + mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget(); + } + + private synchronized void onClosed() { + if (DBG) log("onClosed() " + this); + if (mState != STATE_CLOSED) { + mState = STATE_CLOSED; + mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget(); + releaseWakeLock(); + } + } + + private void acquireWakeLock() { + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + if (VDBG) log("mWakeLock.acquire() " + this); + } + } + + private void releaseWakeLock() { + if (mWakeLock.isHeld()) { + if (VDBG) log("mWakeLock.release() " + this); + mWakeLock.release(); + } + } + + private void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html new file mode 100644 index 00000000000..79abf0cb4a0 --- /dev/null +++ b/framework/java/android/bluetooth/package.html @@ -0,0 +1,13 @@ + + +Provides classes that manage Bluetooth functionality on the device. +

+The Bluetooth APIs allow applications can connect and disconnect headsets, or scan +for other kinds of Bluetooth devices and pair them. Further control includes the +ability to write and modify the local Service Discovery Protocol (SDP) database, +query the SDP database of other Bluetooth devices, establish RFCOMM +channels/sockets on Android, and connect to specified sockets on other devices. +

+

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

+ + -- GitLab From 0047a0f6574baa8bc350cbecd74ff5738dbe83ae Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 5 Mar 2009 20:00:43 -0800 Subject: [PATCH 0012/1408] auto import from //depot/cupcake/@136745 --- .../java/android/bluetooth/BluetoothA2dp.java | 12 ++++++++++++ .../java/android/bluetooth/BluetoothHeadset.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b0b0154abc9..2ea45d567f6 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -49,6 +49,7 @@ import java.util.List; */ public class BluetoothA2dp { private static final String TAG = "BluetoothA2dp"; + private static final boolean DBG = false; /** int extra for SINK_STATE_CHANGED_ACTION */ public static final String SINK_STATE = @@ -103,6 +104,7 @@ public class BluetoothA2dp { * @hide */ public int connectSink(String address) { + if (DBG) log("connectSink(" + address + ")"); try { return mService.connectSink(address); } catch (RemoteException e) { @@ -119,6 +121,7 @@ public class BluetoothA2dp { * @hide */ public int disconnectSink(String address) { + if (DBG) log("disconnectSink(" + address + ")"); try { return mService.disconnectSink(address); } catch (RemoteException e) { @@ -133,6 +136,7 @@ public class BluetoothA2dp { * @hide */ public boolean isSinkConnected(String address) { + if (DBG) log("isSinkConnected(" + address + ")"); int state = getSinkState(address); return state == STATE_CONNECTED || state == STATE_PLAYING; } @@ -142,6 +146,7 @@ public class BluetoothA2dp { * @hide */ public List listConnectedSinks() { + if (DBG) log("listConnectedSinks()"); try { return mService.listConnectedSinks(); } catch (RemoteException e) { @@ -156,6 +161,7 @@ public class BluetoothA2dp { * @hide */ public int getSinkState(String address) { + if (DBG) log("getSinkState(" + address + ")"); try { return mService.getSinkState(address); } catch (RemoteException e) { @@ -177,6 +183,7 @@ public class BluetoothA2dp { * @return Result code, negative indicates an error */ public int setSinkPriority(String address, int priority) { + if (DBG) log("setSinkPriority(" + address + ", " + priority + ")"); try { return mService.setSinkPriority(address, priority); } catch (RemoteException e) { @@ -191,6 +198,7 @@ public class BluetoothA2dp { * @return non-negative priority, or negative error code on error. */ public int getSinkPriority(String address) { + if (DBG) log("getSinkPriority(" + address + ")"); try { return mService.getSinkPriority(address); } catch (RemoteException e) { @@ -244,4 +252,8 @@ public class BluetoothA2dp { return ""; } } + + private static void log(String msg) { + Log.d(TAG, msg); + } } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 34196bf8dea..e23545b25eb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -126,6 +126,7 @@ public class BluetoothHeadset { * are ok. */ public synchronized void close() { + if (DBG) log("close()"); if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; @@ -138,6 +139,7 @@ public class BluetoothHeadset { * object is currently not connected to the Headset service. */ public int getState() { + if (DBG) log("getState()"); if (mService != null) { try { return mService.getState(); @@ -156,6 +158,7 @@ public class BluetoothHeadset { * service. */ public String getHeadsetAddress() { + if (DBG) log("getHeadsetAddress()"); if (mService != null) { try { return mService.getHeadsetAddress(); @@ -180,6 +183,7 @@ public class BluetoothHeadset { * will be expected. */ public boolean connectHeadset(String address) { + if (DBG) log("connectHeadset(" + address + ")"); if (mService != null) { try { if (mService.connectHeadset(address)) { @@ -199,6 +203,7 @@ public class BluetoothHeadset { * if not currently connected to the headset service. */ public boolean isConnected(String address) { + if (DBG) log("isConnected(" + address + ")"); if (mService != null) { try { return mService.isConnected(address); @@ -216,6 +221,7 @@ public class BluetoothHeadset { * not currently connected to the Headset service. */ public boolean disconnectHeadset() { + if (DBG) log("disconnectHeadset()"); if (mService != null) { try { mService.disconnectHeadset(); @@ -235,6 +241,7 @@ public class BluetoothHeadset { * error. */ public boolean startVoiceRecognition() { + if (DBG) log("startVoiceRecognition()"); if (mService != null) { try { return mService.startVoiceRecognition(); @@ -252,6 +259,7 @@ public class BluetoothHeadset { * headset is not in voice recognition mode, or on error. */ public boolean stopVoiceRecognition() { + if (DBG) log("stopVoiceRecognition()"); if (mService != null) { try { return mService.stopVoiceRecognition(); @@ -282,6 +290,7 @@ public class BluetoothHeadset { * @return True if successful, false if there was some error. */ public boolean setPriority(String address, int priority) { + if (DBG) log("setPriority(" + address + ", " + priority + ")"); if (mService != null) { try { return mService.setPriority(address, priority); @@ -299,6 +308,7 @@ public class BluetoothHeadset { * @return non-negative priority, or negative error code on error. */ public int getPriority(String address) { + if (DBG) log("getPriority(" + address + ")"); if (mService != null) { try { return mService.getPriority(address); @@ -350,4 +360,8 @@ public class BluetoothHeadset { } } }; + + private static void log(String msg) { + Log.d(TAG, msg); + } } -- GitLab From 2eb744eebac2f32958fe0801c7a42e51cf18e983 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Fri, 13 Mar 2009 13:04:22 -0700 Subject: [PATCH 0013/1408] auto import from //branches/cupcake_rel/...@138607 --- .../java/android/bluetooth/BluetoothHeadset.java | 5 +++++ .../java/android/bluetooth/BluetoothIntent.java | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e23545b25eb..1dbe0cccda5 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -67,6 +67,11 @@ public class BluetoothHeadset { /** A headset is currently connected */ public static final int STATE_CONNECTED = 2; + /** A SCO audio channel is not established */ + public static final int AUDIO_STATE_DISCONNECTED = 0; + /** A SCO audio channel is established */ + public static final int AUDIO_STATE_CONNECTED = 1; + public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; /** Connection canceled before completetion. */ diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index b66b06ef59c..9273d0defae 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -45,6 +45,8 @@ public interface BluetoothIntent { "android.bluetooth.intent.HEADSET_STATE"; public static final String HEADSET_PREVIOUS_STATE = "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; + public static final String HEADSET_AUDIO_STATE = + "android.bluetooth.intent.HEADSET_AUDIO_STATE"; public static final String BOND_STATE = "android.bluetooth.intent.BOND_STATE"; public static final String BOND_PREVIOUS_STATE = @@ -122,7 +124,18 @@ public interface BluetoothIntent { public static final String BOND_STATE_CHANGED_ACTION = "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; + /** + * TODO(API release): Move into BluetoothHeadset + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String HEADSET_STATE_CHANGED_ACTION = "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; + + /** + * TODO(API release): Consider incorporating as new state in + * HEADSET_STATE_CHANGED + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String HEADSET_AUDIO_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.HEADSET_ADUIO_STATE_CHANGED"; } -- GitLab From 7f372783ab90bc5fae833d39aea1b117fe6812eb Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 18 Mar 2009 17:39:46 -0700 Subject: [PATCH 0014/1408] auto import from //branches/cupcake_rel/...@140373 --- .../android/bluetooth/BluetoothDevice.java | 50 +++++++++---------- .../android/bluetooth/BluetoothIntent.java | 14 ++++-- .../android/bluetooth/IBluetoothDevice.aidl | 5 +- .../bluetooth/IBluetoothDeviceCallback.aidl | 2 - .../java/android/bluetooth/ScoSocket.java | 20 ++++++-- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1ba1c1e86f7..abf08cbfc76 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -31,6 +31,12 @@ import java.io.UnsupportedEncodingException; * @hide */ public class BluetoothDevice { + + public static final int BLUETOOTH_STATE_OFF = 0; + public static final int BLUETOOTH_STATE_TURNING_ON = 1; + public static final int BLUETOOTH_STATE_ON = 2; + public static final int BLUETOOTH_STATE_TURNING_OFF = 3; + /** Inquiry scan and page scan are both off. * Device is neither discoverable nor connectable */ public static final int SCAN_MODE_NONE = 0; @@ -83,7 +89,7 @@ public class BluetoothDevice { } /** - * Get the current status of Bluetooth hardware. + * Is Bluetooth currently turned on. * * @return true if Bluetooth enabled, false otherwise. */ @@ -94,38 +100,31 @@ public class BluetoothDevice { return false; } + /** + * Get the current state of Bluetooth. + * + * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. + */ + public int getBluetoothState() { + try { + return mService.getBluetoothState(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR; + } + /** * Enable the Bluetooth device. * Turn on the underlying hardware. - * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be - * sent if and when the device is successfully enabled. + * This is an asynchronous call, + * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if + * and when the device is sucessfully enabled. * @return false if we cannot enable the Bluetooth device. True does not * imply the device was enabled, it only implies that so far there were no * problems. */ public boolean enable() { - return enable(null); - } - - /** - * Enable the Bluetooth device. - * Turns on the underlying hardware. - * This is an asynchronous call. onEnableResult() of your callback will be - * called when the call is complete, with either RESULT_SUCCESS or - * RESULT_FAILURE. - * - * Your callback will be called from a binder thread, not the main thread. - * - * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be - * broadcast if the device is successfully enabled. - * - * @param callback Your callback, null is ok. - * @return true if your callback was successfully registered, or false if - * there was an error, implying your callback will never be called. - */ - public boolean enable(IBluetoothDeviceCallback callback) { try { - return mService.enable(callback); + return mService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -138,7 +137,7 @@ public class BluetoothDevice { */ public boolean disable() { try { - return mService.disable(); + return mService.disable(true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -551,7 +550,6 @@ public class BluetoothDevice { } return pinBytes; } - private static final int ADDRESS_LENGTH = 17; /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 9273d0defae..344601b0b89 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -41,6 +41,10 @@ public interface BluetoothIntent { "android.bluetooth.intent.RSSI"; public static final String CLASS = "android.bluetooth.intent.CLASS"; + public static final String BLUETOOTH_STATE = + "android.bluetooth.intent.BLUETOOTH_STATE"; + public static final String BLUETOOTH_PREVIOUS_STATE = + "android.bluetooth.intent.BLUETOOTH_PREVIOUS_STATE"; public static final String HEADSET_STATE = "android.bluetooth.intent.HEADSET_STATE"; public static final String HEADSET_PREVIOUS_STATE = @@ -54,12 +58,12 @@ public interface BluetoothIntent { public static final String REASON = "android.bluetooth.intent.REASON"; + /** Broadcast when the local Bluetooth device state changes, for example + * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and + * BLUETOOTH_PREVIOUS_STATE. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ENABLED_ACTION = - "android.bluetooth.intent.action.ENABLED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISABLED_ACTION = - "android.bluetooth.intent.action.DISABLED"; + public static final String BLUETOOTH_STATE_CHANGED_ACTION = + "android.bluetooth.intent.action.BLUETOOTH_STATE_CHANGED"; @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NAME_CHANGED_ACTION = diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index 4351d2ebc38..6cd792e26a5 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -26,8 +26,9 @@ import android.bluetooth.IBluetoothDeviceCallback; interface IBluetoothDevice { boolean isEnabled(); - boolean enable(in IBluetoothDeviceCallback callback); // async - boolean disable(); + int getBluetoothState(); + boolean enable(); + boolean disable(boolean persistSetting); String getAddress(); String getName(); diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl index d25bd560d03..d05709330b7 100644 --- a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl @@ -22,6 +22,4 @@ package android.bluetooth; oneway interface IBluetoothDeviceCallback { void onGetRemoteServiceChannelResult(in String address, int channel); - - void onEnableResult(int result); } diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java index a43a08be105..1bf786ffd3a 100644 --- a/framework/java/android/bluetooth/ScoSocket.java +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -76,7 +76,7 @@ public class ScoSocket { try { if (VDBG) log(this + " SCO OBJECT DTOR"); destroyNative(); - releaseWakeLock(); + releaseWakeLockNow(); } finally { super.finalize(); } @@ -98,7 +98,7 @@ public class ScoSocket { return true; } else { mState = STATE_CLOSED; - releaseWakeLock(); + releaseWakeLockNow(); return false; } } @@ -148,7 +148,7 @@ public class ScoSocket { mState = STATE_CLOSED; } mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); - releaseWakeLock(); + releaseWakeLockNow(); } private synchronized void onAccepted(int result) { @@ -183,7 +183,19 @@ public class ScoSocket { private void releaseWakeLock() { if (mWakeLock.isHeld()) { - if (VDBG) log("mWakeLock.release() " + this); + // Keep apps processor awake for a further 2 seconds. + // This is a hack to resolve issue http://b/1616263 - in which + // we are left in a 80 mA power state when remotely terminating a + // call while connected to BT headset "HTC BH S100 " with A2DP and + // HFP profiles. + if (VDBG) log("mWakeLock.release() in 2 sec" + this); + mWakeLock.acquire(2000); + } + } + + private void releaseWakeLockNow() { + if (mWakeLock.isHeld()) { + if (VDBG) log("mWakeLock.release() now" + this); mWakeLock.release(); } } -- GitLab From 61711af0071bb40b005071d4e752a5cb850c245c Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh <> Date: Tue, 24 Mar 2009 18:32:43 -0700 Subject: [PATCH 0015/1408] Automated import from //branches/cupcake/...@141862,141862 --- framework/java/android/bluetooth/HeadsetBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index fd2d2ab8848..f31e7a2c65e 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -105,7 +105,7 @@ public class HeadsetBase { /* Process an incoming AT command line */ - protected synchronized void handleInput(String input) { + protected void handleInput(String input) { acquireWakeLock(); long timestamp; @@ -267,13 +267,13 @@ public class HeadsetBase { } private native boolean sendURCNative(String urc); - private void acquireWakeLock() { + private synchronized void acquireWakeLock() { if (!mWakeLock.isHeld()) { mWakeLock.acquire(); } } - private void releaseWakeLock() { + private synchronized void releaseWakeLock() { if (mWakeLock.isHeld()) { mWakeLock.release(); } -- GitLab From 95db27b834b2062be9c60d1e807d7d09894b1e27 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh <> Date: Tue, 24 Mar 2009 21:02:13 -0700 Subject: [PATCH 0016/1408] Automated import from //branches/donutburger/...@141864,141864 --- framework/java/android/bluetooth/HeadsetBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index fd2d2ab8848..f31e7a2c65e 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -105,7 +105,7 @@ public class HeadsetBase { /* Process an incoming AT command line */ - protected synchronized void handleInput(String input) { + protected void handleInput(String input) { acquireWakeLock(); long timestamp; @@ -267,13 +267,13 @@ public class HeadsetBase { } private native boolean sendURCNative(String urc); - private void acquireWakeLock() { + private synchronized void acquireWakeLock() { if (!mWakeLock.isHeld()) { mWakeLock.acquire(); } } - private void releaseWakeLock() { + private synchronized void releaseWakeLock() { if (mWakeLock.isHeld()) { mWakeLock.release(); } -- GitLab From 4023163f053f5d6de9c466b81cf85d05c76b838b Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh <> Date: Tue, 24 Mar 2009 21:05:50 -0700 Subject: [PATCH 0017/1408] Automated import from //branches/master/...@141865,141865 --- framework/java/android/bluetooth/HeadsetBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index fd2d2ab8848..f31e7a2c65e 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -105,7 +105,7 @@ public class HeadsetBase { /* Process an incoming AT command line */ - protected synchronized void handleInput(String input) { + protected void handleInput(String input) { acquireWakeLock(); long timestamp; @@ -267,13 +267,13 @@ public class HeadsetBase { } private native boolean sendURCNative(String urc); - private void acquireWakeLock() { + private synchronized void acquireWakeLock() { if (!mWakeLock.isHeld()) { mWakeLock.acquire(); } } - private void releaseWakeLock() { + private synchronized void releaseWakeLock() { if (mWakeLock.isHeld()) { mWakeLock.release(); } -- GitLab From e44ce670aff1064fe2a62052af536dc7c956d709 Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Wed, 25 Mar 2009 15:43:42 -0700 Subject: [PATCH 0018/1408] Automated import from //branches/cupcake/...@142761,142761 --- framework/java/android/bluetooth/BluetoothHeadset.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 1dbe0cccda5..e1984353e88 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -82,6 +82,12 @@ public class BluetoothHeadset { /** Default priority for headsets that should not be auto-connected */ public static final int PRIORITY_OFF = 0; + /** The voice dialer 'works' but the user experience is poor. The voice + * recognizer has trouble dealing with the 8kHz SCO signal, and it still + * requires visual confirmation. Disable for cupcake. + */ + public static final boolean DISABLE_BT_VOICE_DIALING = true; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. -- GitLab From 1a3ee65d98c738a860c23b7b29f58494cee32156 Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Wed, 25 Mar 2009 16:25:37 -0700 Subject: [PATCH 0019/1408] Automated import from //branches/master/...@142774,142774 --- framework/java/android/bluetooth/BluetoothHeadset.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 1dbe0cccda5..e1984353e88 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -82,6 +82,12 @@ public class BluetoothHeadset { /** Default priority for headsets that should not be auto-connected */ public static final int PRIORITY_OFF = 0; + /** The voice dialer 'works' but the user experience is poor. The voice + * recognizer has trouble dealing with the 8kHz SCO signal, and it still + * requires visual confirmation. Disable for cupcake. + */ + public static final boolean DISABLE_BT_VOICE_DIALING = true; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. -- GitLab From 1b5300f8495df8522e065c0a31e65eb9a020a6db Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Wed, 25 Mar 2009 17:33:56 -0700 Subject: [PATCH 0020/1408] Automated import from //branches/donutburger/...@142766,142766 --- framework/java/android/bluetooth/BluetoothHeadset.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 1dbe0cccda5..e1984353e88 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -82,6 +82,12 @@ public class BluetoothHeadset { /** Default priority for headsets that should not be auto-connected */ public static final int PRIORITY_OFF = 0; + /** The voice dialer 'works' but the user experience is poor. The voice + * recognizer has trouble dealing with the 8kHz SCO signal, and it still + * requires visual confirmation. Disable for cupcake. + */ + public static final boolean DISABLE_BT_VOICE_DIALING = true; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. -- GitLab From faa54af8e47214adea373c0bbdd9b9771873c98c Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Tue, 31 Mar 2009 12:05:46 -0700 Subject: [PATCH 0021/1408] AI 143740: Don't clear supported profiles in settings app if getRemoteClass returns error. Also clean up the error codes returned by the framework, so that the settings app can properly detect an error. BUG=1748881 Automated import of CL 143740 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index abf08cbfc76..951b4b0ab47 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -485,7 +485,8 @@ public class BluetoothDevice { * Get the major, minor and servics classes of a remote device. * These classes are encoded as a 32-bit integer. See BluetoothClass. * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass. + * @return 32-bit class suitable for use with BluetoothClass, or + * BluetoothClass.ERROR on error */ public int getRemoteClass(String address) { try { -- GitLab From ed269c58996a883ce8b999576a4eb53a66678196 Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Tue, 31 Mar 2009 14:42:33 -0700 Subject: [PATCH 0022/1408] AI 143812: am: CL 143788 am: CL 143740 Don't clear supported profiles in settings app if getRemoteClass returns error. Also clean up the error codes returned by the framework, so that the settings app can properly detect an error. Original author: npelly Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143812 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index abf08cbfc76..951b4b0ab47 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -485,7 +485,8 @@ public class BluetoothDevice { * Get the major, minor and servics classes of a remote device. * These classes are encoded as a 32-bit integer. See BluetoothClass. * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass. + * @return 32-bit class suitable for use with BluetoothClass, or + * BluetoothClass.ERROR on error */ public int getRemoteClass(String address) { try { -- GitLab From 7cfb003c1ae1b640a1af71b3d95f75dea0570416 Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Tue, 31 Mar 2009 14:56:26 -0700 Subject: [PATCH 0023/1408] AI 143788: am: CL 143740 Don't clear supported profiles in settings app if getRemoteClass returns error. Also clean up the error codes returned by the framework, so that the settings app can properly detect an error. Original author: npelly Merged from: //branches/cupcake/... Automated import of CL 143788 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index abf08cbfc76..951b4b0ab47 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -485,7 +485,8 @@ public class BluetoothDevice { * Get the major, minor and servics classes of a remote device. * These classes are encoded as a 32-bit integer. See BluetoothClass. * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass. + * @return 32-bit class suitable for use with BluetoothClass, or + * BluetoothClass.ERROR on error */ public int getRemoteClass(String address) { try { -- GitLab From 08609179ddabac22903f522148bb3eec25c079fe Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 19 May 2009 15:21:03 -0700 Subject: [PATCH 0024/1408] Remove Database.java API. This provided SDP functionality to Java, but is not currently used by any Apps. I will shortly be providing SDP functionality in a new API, but it will be quite different to this one, and in the mean-time keeping this stale code updated with other API changes is a pain. --- .../java/android/bluetooth/Database.java | 200 ------------------ 1 file changed, 200 deletions(-) delete mode 100644 framework/java/android/bluetooth/Database.java diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java deleted file mode 100644 index fef641a5f7c..00000000000 --- a/framework/java/android/bluetooth/Database.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.RfcommSocket; - -import android.util.Log; - -import java.io.*; -import java.util.*; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * A low-level API to the Service Discovery Protocol (SDP) Database. - * - * Allows service records to be added to the local SDP database. Once added, - * these services will be advertised to remote devices when they make SDP - * queries on this device. - * - * Currently this API is a thin wrapper to the bluez SDP Database API. See: - * http://wiki.bluez.org/wiki/Database - * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords - * @hide - */ -public final class Database { - private static Database mInstance; - - private static final String sLogName = "android.bluetooth.Database"; - - /** - * Class load time initialization - */ - static { - classInitNative(); - } - private native static void classInitNative(); - - /** - * Private to enforce singleton property - */ - private Database() { - initializeNativeDataNative(); - } - private native void initializeNativeDataNative(); - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - private native void cleanupNativeDataNative(); - - /** - * Singelton accessor - * @return The singleton instance of Database - */ - public static synchronized Database getInstance() { - if (mInstance == null) { - mInstance = new Database(); - } - return mInstance; - } - - /** - * Advertise a service with an RfcommSocket. - * - * This adds the service the SDP Database with the following attributes - * set: Service Name, Protocol Descriptor List, Service Class ID List - * TODO: Construct a byte[] record directly, rather than via XML. - * @param socket The rfcomm socket to advertise (by channel). - * @param serviceName A short name for this service - * @param uuid - * Unique identifier for this service, by which clients - * can search for your service - * @return Handle to the new service record - */ - public int advertiseRfcommService(RfcommSocket socket, - String serviceName, - UUID uuid) throws IOException { - String xmlRecord = - "\n" + - "\n" + - " \n" + // ServiceClassIDList - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ProtocolDescriptorList - " \n" + - " \n" + - " \n" + // L2CAP - " \n" + - " \n" + - " \n" + // RFCOMM - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ServiceName - " \n" + - " \n" + - "\n"; - Log.i(sLogName, xmlRecord); - return addServiceRecordFromXml(xmlRecord); - } - - - /** - * Add a new service record. - * @param record The byte[] record - * @return A handle to the new record - */ - public synchronized int addServiceRecord(byte[] record) throws IOException { - int handle = addServiceRecordNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordNative(byte[] record) - throws IOException; - - /** - * Add a new service record, using XML. - * @param record The record as an XML string - * @return A handle to the new record - */ - public synchronized int addServiceRecordFromXml(String record) throws IOException { - int handle = addServiceRecordFromXmlNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordFromXmlNative(String record) - throws IOException; - - /** - * Update an exisiting service record. - * @param handle Handle to exisiting record - * @param record The updated byte[] record - */ - public synchronized void updateServiceRecord(int handle, byte[] record) { - try { - updateServiceRecordNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordNative(int handle, byte[] record) - throws IOException; - - /** - * Update an exisiting record, using XML. - * @param handle Handle to exisiting record - * @param record The record as an XML string. - */ - public synchronized void updateServiceRecordFromXml(int handle, String record) { - try { - updateServiceRecordFromXmlNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordFromXmlNative(int handle, String record) - throws IOException; - - /** - * Remove a service record. - * It is only possible to remove service records that were added by the - * current connection. - * @param handle Handle to exisiting record to be removed - */ - public synchronized void removeServiceRecord(int handle) { - try { - removeServiceRecordNative(handle); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void removeServiceRecordNative(int handle) throws IOException; -} -- GitLab From 53f441bf8d89667e099302d506c6f07ed1201db1 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 26 May 2009 19:13:43 -0700 Subject: [PATCH 0025/1408] New BluetoothSocket API. Modeled on blocking java.net.Socket and java.net.ServerSocket library. Public interface is: public final class BluetoothSocket implements Closeable { public static BluetoothSocket createRfcommSocket(String address, int port) throws IOException; public static BluetoothSocket createInsecureRfcommSocket(String address, int port) throws IOException; public void connect() throws IOException; public void close() throws IOException; public String getAddress(); public InputStream getInputStream() throws IOException; public OutputStream getOutputStream() throws IOException; } public final class BluetoothServerSocket implements Closeable { public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException; public static BluetoothServerSocket listenUsingUnsecureRfcommOn(int port) throws IOException; public BluetoothSocket accept() throws IOException; public BluetoothSocket accept(int timeout) throws IOException; public void close() throws IOException; } --- .../bluetooth/BluetoothInputStream.java | 62 ++ .../bluetooth/BluetoothOutputStream.java | 57 ++ .../bluetooth/BluetoothServerSocket.java | 124 ++++ .../android/bluetooth/BluetoothSocket.java | 176 +++++ .../java/android/bluetooth/RfcommSocket.java | 674 ------------------ 5 files changed, 419 insertions(+), 674 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothInputStream.java create mode 100644 framework/java/android/bluetooth/BluetoothOutputStream.java create mode 100644 framework/java/android/bluetooth/BluetoothServerSocket.java create mode 100644 framework/java/android/bluetooth/BluetoothSocket.java delete mode 100644 framework/java/android/bluetooth/RfcommSocket.java diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java new file mode 100644 index 00000000000..ceae70c586d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.InputStream; + +/** + * BluetoothInputStream. + * + * Used to write to a Bluetooth socket. + * + * TODO: Implement bulk writes (instead of one byte at a time). + * @hide + */ +/*package*/ final class BluetoothInputStream extends InputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothInputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Return number of bytes available before this stream will block. + */ + public int available() throws IOException { + return mSocket.availableNative(); + } + + public void close() throws IOException { + mSocket.close(); + } + + /** + * Reads a single byte from this stream and returns it as an integer in the + * range from 0 to 255. Returns -1 if the end of the stream has been + * reached. Blocks until one byte has been read, the end of the source + * stream is detected or an exception is thrown. + * + * @return the byte read or -1 if the end of stream has been reached. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.0 + */ + public int read() throws IOException { + return mSocket.readNative(); + } +} diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java new file mode 100644 index 00000000000..32e6d17c82a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.OutputStream; + +/** + * BluetoothOutputStream. + * + * Used to read from a Bluetooth socket. + * + * TODO: Implement bulk reads (instead of one byte at a time). + * @hide + */ +/*package*/ final class BluetoothOutputStream extends OutputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothOutputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Close this output stream and the socket associated with it. + */ + public void close() throws IOException { + mSocket.close(); + } + + /** + * Writes a single byte to this stream. Only the least significant byte of + * the integer {@code oneByte} is written to the stream. + * + * @param oneByte + * the byte to be written. + * @throws IOException + * if an error occurs while writing to this stream. + * @since Android 1.0 + */ + public void write(int oneByte) throws IOException { + mSocket.writeNative(oneByte); + } +} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java new file mode 100644 index 00000000000..ca467011c91 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2009 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 java.io.Closeable; +import java.io.IOException; + +/** + * Server (listening) Bluetooth Socket. + * + * Currently only supports RFCOMM sockets. + * + * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is + * also known as the Serial Port Profile (SPP). + * + * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Clean up javadoc grammer and formatting. + * TODO: Remove @hide + * @hide + */ +public final class BluetoothServerSocket implements Closeable { + private final BluetoothSocket mSocket; + + /** + * Construct a listening, secure RFCOMM server socket. + * The remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket(true, true); + try { + socket.mSocket.bindListenNative(port); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct an unencrypted, unauthenticated, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket(false, false); + try { + socket.mSocket.bindListenNative(port); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct a socket for incoming connections. + * @param auth Require the remote device to be authenticated + * @param encrypt Require the connection to be encrypted + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException { + mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1); + } + + /** + * Block until a connection is established. + * Returns a connected #BluetoothSocket. This server socket can be reused + * for subsequent incoming connections by calling #accept repeatedly. + * #close can be used to abort this call from another thread. + * @return A connected #BluetoothSocket + * @throws IOException On error, for example this call was aborted + */ + public BluetoothSocket accept() throws IOException { + return accept(-1); + } + + /** + * Block until a connection is established, with timeout. + * Returns a connected #BluetoothSocket. This server socket can be reused + * for subsequent incoming connections by calling #accept repeatedly. + * #close can be used to abort this call from another thread. + * @return A connected #BluetoothSocket + * @throws IOException On error, for example this call was aborted, or + * timeout + */ + public BluetoothSocket accept(int timeout) throws IOException { + return mSocket.acceptNative(timeout); + } + + /** + * Closes this socket. + * This will cause other blocking calls on this socket to immediately + * throw an IOException. + */ + public void close() throws IOException { + mSocket.closeNative(); + } +} diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java new file mode 100644 index 00000000000..fd8885ece99 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2009 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 java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Represents a connected or connecting Bluetooth Socket. + * + * Currently only supports RFCOMM sockets. + * + * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is + * also known as the Serial Port Profile (SPP). + * + * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Clean up javadoc grammer and formatting. + * TODO: Remove @hide + * @hide + */ +public final class BluetoothSocket implements Closeable { + private final int mPort; + private final String mAddress; /* remote address */ + private final boolean mAuth; + private final boolean mEncrypt; + private final BluetoothInputStream mInputStream; + private final BluetoothOutputStream mOutputStream; + + private int mSocketData; /* used by native code only */ + + /** + * Construct a secure RFCOMM socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will be authenticated and communication on this socket + * will be encrypted. + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @return an RFCOMM BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothSocket createRfcommSocket(String address, int port) + throws IOException { + return new BluetoothSocket(-1, true, true, address, port); + } + + /** + * Construct an insecure RFCOMM socket ready to start an outgoing + * connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will not be authenticated and communication on this + * socket will not be encrypted. + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @return An RFCOMM BluetoothSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothSocket createInsecureRfcommSocket(String address, int port) + throws IOException { + return new BluetoothSocket(-1, false, false, address, port); + } + + /** + * Construct a Bluetooth. + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port) + throws IOException { + mAuth = auth; + mEncrypt = encrypt; + mAddress = address; + mPort = port; + if (fd == -1) { + initSocketNative(); + } else { + initSocketFromFdNative(fd); + } + mInputStream = new BluetoothInputStream(this); + mOutputStream = new BluetoothOutputStream(this); + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Attempt to connect to a remote device. + * This method will block until a connection is made or the connection + * fails. If this method returns without an exception then this socket + * is now connected. #close can be used to abort this call from another + * thread. + * @throws IOException On error, for example connection failure + */ + public void connect() throws IOException { + connectNative(mAddress, mPort, -1); + } + + /** + * Closes this socket. + * This will cause other blocking calls on this socket to immediately + * throw an IOException. + */ + public void close() throws IOException { + closeNative(); + } + + /** + * Return the address we are connecting, or connected, to. + * @return Bluetooth address, or null if this socket has not yet attempted + * or established a connection. + */ + public String getAddress() { + return mAddress; + } + + /** + * Get the input stream associated with this socket. + * The input stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return InputStream + */ + public InputStream getInputStream() throws IOException { + return mInputStream; + } + + /** + * Get the output stream associated with this socket. + * The output stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return OutputStream + */ + public OutputStream getOutputStream() throws IOException { + return mOutputStream; + } + + private native void initSocketNative(); + private native void initSocketFromFdNative(int fd); + private native void connectNative(String address, int port, int timeout); + /*package*/ native void bindListenNative(int port) throws IOException; + /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; + /*package*/ native int availableNative(); + /*package*/ native int readNative(); + /*package*/ native void writeNative(int data); + /*package*/ native void closeNative(); + private native void destroyNative(); +} diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java deleted file mode 100644 index a33263f5261..00000000000 --- a/framework/java/android/bluetooth/RfcommSocket.java +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (C) 2007 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 java.io.IOException; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.FileDescriptor; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket - * is similar to a normal socket in that it takes an address and a port number. - * The difference is of course that the address is a Bluetooth-device address, - * and the port number is an RFCOMM channel. The API allows for the - * establishment of listening sockets via methods - * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and - * {@link #accept(RfcommSocket, int) accept}, as well as for the making of - * outgoing connections with {@link #connect(String, int) connect}, - * {@link #connectAsync(String, int) connectAsync}, and - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * After constructing a socket, you need to {@link #create() create} it and then - * {@link #destroy() destroy} it when you are done using it. Both - * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return - * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. - * Alternatively, you may call {@link #getInputStream() getInputStream} and - * {@link #getOutputStream() getOutputStream} to retrieve the respective streams - * without going through the FileDescriptor. - * - * @hide - */ -public class RfcommSocket { - - /** - * Used by the native implementation of the class. - */ - private int mNativeData; - - /** - * Used by the native implementation of the class. - */ - private int mPort; - - /** - * Used by the native implementation of the class. - */ - private String mAddress; - - /** - * We save the return value of {@link #create() create} and - * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to - * retrieve the I/O streams. - */ - private FileDescriptor mFd; - - /** - * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, - * if the return value is zero, then, the the remaining time left to wait is - * written into this variable (by the native implementation). It is possible - * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before - * the user-specified timeout expires, which is why we save the remaining - * time in this member variable for the user to retrieve by calling method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. - */ - private int mTimeoutRemainingMs; - - /** - * Set to true when an asynchronous (nonblocking) connect is in progress. - * {@see #connectAsync(String,int)}. - */ - private boolean mIsConnecting; - - /** - * Set to true after a successful call to {@link #bind(String,int) bind} and - * used for error checking in {@link #listen(int) listen}. Reset to false - * on {@link #destroy() destroy}. - */ - private boolean mIsBound = false; - - /** - * Set to true after a successful call to {@link #listen(int) listen} and - * used for error checking in {@link #accept(RfcommSocket,int) accept}. - * Reset to false on {@link #destroy() destroy}. - */ - private boolean mIsListening = false; - - /** - * Used to store the remaining time after an accept with a non-negative - * timeout returns unsuccessfully. It is possible that a blocking - * {@link #accept(int) accept} may wait for less than the time specified by - * the user, which is why we store the remainder in this member variable for - * it to be retrieved with method - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. - */ - private int mAcceptTimeoutRemainingMs; - - /** - * Maintained by {@link #getInputStream() getInputStream}. - */ - protected FileInputStream mInputStream; - - /** - * Maintained by {@link #getOutputStream() getOutputStream}. - */ - protected FileOutputStream mOutputStream; - - private native void initializeNativeDataNative(); - - /** - * Constructor. - */ - public RfcommSocket() { - initializeNativeDataNative(); - } - - private native void cleanupNativeDataNative(); - - /** - * Called by the GC to clean up the native data that we set up when we - * construct the object. - */ - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private native static void classInitNative(); - - static { - classInitNative(); - } - - /** - * Creates a socket. You need to call this method before performing any - * other operation on a socket. - * - * @return FileDescriptor for the data stream. - * @throws IOException - * @see #destroy() - */ - public FileDescriptor create() throws IOException { - if (mFd == null) { - mFd = createNative(); - } - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - private native FileDescriptor createNative(); - - /** - * Destroys a socket created by {@link #create() create}. Call this - * function when you no longer use the socket in order to release the - * underlying OS resources. - * - * @see #create() - */ - public void destroy() { - synchronized (this) { - destroyNative(); - mFd = null; - mIsBound = false; - mIsListening = false; - } - } - - private native void destroyNative(); - - /** - * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. - * - * @return the FileDescriptor - * @throws IOException - * when the socket has not been {@link #create() created}. - */ - public FileDescriptor getFileDescriptor() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - /** - * Retrieves the input stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return InputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public InputStream getInputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mInputStream == null) { - mInputStream = new FileInputStream(mFd); - } - - return mInputStream; - } - } - - /** - * Retrieves the output stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return OutputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public OutputStream getOutputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mOutputStream == null) { - mOutputStream = new FileOutputStream(mFd); - } - - return mOutputStream; - } - } - - /** - * Starts a blocking connect to a remote RFCOMM socket. It takes the address - * of a device and the RFCOMM channel (port) to which to connect. - * - * @param address - * is the Bluetooth address of the remote device. - * @param port - * is the RFCOMM channel - * @return true on success, false on failure - * @throws IOException - * if {@link #create() create} has not been called. - * @see #connectAsync(String, int) - */ - public boolean connect(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return connectNative(address, port); - } - } - - private native boolean connectNative(String address, int port); - - /** - * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. - * It takes the address of the device to connect to, as well as the RFCOMM - * channel (port). On successful return (return value is true), you need to - * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to - * block for up to a specified number of milliseconds while waiting for the - * asyncronous connect to complete. - * - * @param address - * of remote device - * @param port - * the RFCOMM channel - * @return true when the asynchronous connect has successfully started, - * false if there was an error. - * @throws IOException - * is you have not called {@link #create() create} - * @see #waitForAsyncConnect(int) - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connect(String, int) - */ - public boolean connectAsync(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsConnecting = connectAsyncNative(address, port); - return mIsConnecting; - } - } - - private native boolean connectAsyncNative(String address, int port); - - /** - * Interrupts an asynchronous connect in progress. This method does nothing - * when there is no asynchronous connect in progress. - * - * @throws IOException - * if you have not called {@link #create() create}. - * @see #connectAsync(String, int) - */ - public void interruptAsyncConnect() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsConnecting) { - mIsConnecting = !interruptAsyncConnectNative(); - } - } - } - - private native boolean interruptAsyncConnectNative(); - - /** - * Tells you whether there is an asynchronous connect in progress. This - * method returns an undefined value when there is a synchronous connect in - * progress. - * - * @return true if there is an asyc connect in progress, false otherwise - * @see #connectAsync(String, int) - */ - public boolean isConnecting() { - return mIsConnecting; - } - - /** - * Blocks for a specified amount of milliseconds while waiting for an - * asynchronous connect to complete. Returns an integer value to indicate - * one of the following: the connect succeeded, the connect is still in - * progress, or the connect failed. It is possible for this method to block - * for less than the time specified by the user, and still return zero - * (i.e., async connect is still in progress.) For this reason, if the - * return value is zero, you need to call method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} - * to retrieve the remaining time. - * - * @param timeoutMs - * the time to block while waiting for the async connect to - * complete. - * @return a positive value if the connect succeeds; zero, if the connect is - * still in progress, and a negative value if the connect failed. - * - * @throws IOException - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connectAsync(String, int) - */ - public int waitForAsyncConnect(int timeoutMs) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - int ret = waitForAsyncConnectNative(timeoutMs); - if (ret != 0) { - mIsConnecting = false; - } - return ret; - } - } - - private native int waitForAsyncConnectNative(int timeoutMs); - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * It is possible that waitForAsyncConnect() waits for less than the time - * specified by the user, and still returns zero (i.e., async connect is - * still in progress.) For this reason, if the return value is zero, you - * need to call this method to retrieve the remaining time before you call - * waitForAsyncConnect again. - * - * @return the remaining timeout in milliseconds. - * @see #waitForAsyncConnect(int) - * @see #connectAsync(String, int) - */ - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /** - * Shuts down both directions on a socket. - * - * @return true on success, false on failure; if the return value is false, - * the socket might be left in a patially shut-down state (i.e. one - * direction is shut down, but the other is still open.) In this - * case, you should {@link #destroy() destroy} and then - * {@link #create() create} the socket again. - * @throws IOException - * is you have not caled {@link #create() create}. - * @see #shutdownInput() - * @see #shutdownOutput() - */ - public boolean shutdown() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (shutdownNative(true)) { - return shutdownNative(false); - } - - return false; - } - } - - /** - * Shuts down the input stream of the socket, but leaves the output stream - * in its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownOutput() - */ - public boolean shutdownInput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(true); - } - } - - /** - * Shut down the output stream of the socket, but leaves the input stream in - * its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownInput() - */ - public boolean shutdownOutput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(false); - } - } - - private native boolean shutdownNative(boolean shutdownInput); - - /** - * Tells you whether a socket is connected to another socket. This could be - * for input or output or both. - * - * @return true if connected, false otherwise. - * @see #isInputConnected() - * @see #isOutputConnected() - */ - public boolean isConnected() { - return isConnectedNative() > 0; - } - - /** - * Determines whether input is connected (i.e., whether you can receive data - * on this socket.) - * - * @return true if input is connected, false otherwise. - * @see #isConnected() - * @see #isOutputConnected() - */ - public boolean isInputConnected() { - return (isConnectedNative() & 1) != 0; - } - - /** - * Determines whether output is connected (i.e., whether you can send data - * on this socket.) - * - * @return true if output is connected, false otherwise. - * @see #isConnected() - * @see #isInputConnected() - */ - public boolean isOutputConnected() { - return (isConnectedNative() & 2) != 0; - } - - private native int isConnectedNative(); - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. The port is automatically selected as the first - * available port in the range 12 to 30. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * TODO: Use bind(0) in native code to have the kernel select an unused - * port. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - for (int port = 12; port <= 30; port++) { - if (bindNative(device, port)) { - mIsBound = true; - return true; - } - } - mIsBound = false; - return false; - } - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @param port - * RFCOMM channel to bind socket to. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device, int port) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsBound = bindNative(device, port); - return mIsBound; - } - - private native boolean bindNative(String device, int port); - - /** - * Starts listening for incoming connections on this socket, after it has - * been bound to an address and RFCOMM channel with - * {@link #bind(String,int) bind}. - * - * @param backlog - * the number of pending incoming connections to queue for - * {@link #accept(RfcommSocket, int) accept}. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} or if the - * socket has not been bound to a device and RFCOMM channel. - */ - public boolean listen(int backlog) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsBound) { - throw new IOException("socket not bound"); - } - mIsListening = listenNative(backlog); - return mIsListening; - } - - private native boolean listenNative(int backlog); - - /** - * Accepts incoming-connection requests for a listening socket bound to an - * RFCOMM channel. The user may provide a time to wait for an incoming - * connection. - * - * Note that this method may return null (i.e., no incoming connection) - * before the user-specified timeout expires. For this reason, on a null - * return value, you need to call - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} - * in order to see how much time is left to wait, before you call this - * method again. - * - * @param newSock - * is set to the new socket that is created as a result of a - * successful accept. - * @param timeoutMs - * time (in milliseconds) to block while waiting to an - * incoming-connection request. A negative value is an infinite - * wait. - * @return FileDescriptor of newSock on success, null on failure. Failure - * occurs if the timeout expires without a successful connect. - * @throws IOException - * if the socket has not been {@link #create() create}ed, is - * not bound, or is not a listening socket. - * @see #bind(String, int) - * @see #listen(int) - * @see #getRemainingAcceptWaitingTimeMs() - */ - public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) - throws IOException { - synchronized (newSock) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsListening == false) { - throw new IOException("not listening on socket"); - } - newSock.mFd = acceptNative(newSock, timeoutMs); - return newSock.mFd; - } - } - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #accept(RfcommSocket, int) accept}. - * - * Since accept() may return null (i.e., no incoming connection) before the - * user-specified timeout expires, you need to call this method in order to - * see how much time is left to wait, and wait for that amount of time - * before you call accept again. - * - * @return the remaining time, in milliseconds. - */ - public int getRemainingAcceptWaitingTimeMs() { - return mAcceptTimeoutRemainingMs; - } - - private native FileDescriptor acceptNative(RfcommSocket newSock, - int timeoutMs); - - /** - * Get the port (rfcomm channel) associated with this socket. - * - * This is only valid if the port has been set via a successful call to - * {@link #bind(String, int)}, {@link #connect(String, int)} - * or {@link #connectAsync(String, int)}. This can be checked - * with {@link #isListening()} and {@link #isConnected()}. - * @return Port (rfcomm channel) - */ - public int getPort() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsListening && !isConnected()) { - throw new IOException("not listening or connected on socket"); - } - return mPort; - } - - /** - * Return true if this socket is listening ({@link #listen(int)} - * has been called successfully). - */ - public boolean isListening() { - return mIsListening; - } -} -- GitLab From 62c384ed909178ce5409551702166dbed48f8691 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 19 May 2009 15:21:03 -0700 Subject: [PATCH 0026/1408] Remove Database.java API. This provided SDP functionality to Java, but is not currently used by any Apps. I will shortly be providing SDP functionality in a new API, but it will be quite different to this one, and in the mean-time keeping this stale code updated with other API changes is a pain. --- .../java/android/bluetooth/Database.java | 200 ------------------ 1 file changed, 200 deletions(-) delete mode 100644 framework/java/android/bluetooth/Database.java diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java deleted file mode 100644 index fef641a5f7c..00000000000 --- a/framework/java/android/bluetooth/Database.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.RfcommSocket; - -import android.util.Log; - -import java.io.*; -import java.util.*; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * A low-level API to the Service Discovery Protocol (SDP) Database. - * - * Allows service records to be added to the local SDP database. Once added, - * these services will be advertised to remote devices when they make SDP - * queries on this device. - * - * Currently this API is a thin wrapper to the bluez SDP Database API. See: - * http://wiki.bluez.org/wiki/Database - * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords - * @hide - */ -public final class Database { - private static Database mInstance; - - private static final String sLogName = "android.bluetooth.Database"; - - /** - * Class load time initialization - */ - static { - classInitNative(); - } - private native static void classInitNative(); - - /** - * Private to enforce singleton property - */ - private Database() { - initializeNativeDataNative(); - } - private native void initializeNativeDataNative(); - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - private native void cleanupNativeDataNative(); - - /** - * Singelton accessor - * @return The singleton instance of Database - */ - public static synchronized Database getInstance() { - if (mInstance == null) { - mInstance = new Database(); - } - return mInstance; - } - - /** - * Advertise a service with an RfcommSocket. - * - * This adds the service the SDP Database with the following attributes - * set: Service Name, Protocol Descriptor List, Service Class ID List - * TODO: Construct a byte[] record directly, rather than via XML. - * @param socket The rfcomm socket to advertise (by channel). - * @param serviceName A short name for this service - * @param uuid - * Unique identifier for this service, by which clients - * can search for your service - * @return Handle to the new service record - */ - public int advertiseRfcommService(RfcommSocket socket, - String serviceName, - UUID uuid) throws IOException { - String xmlRecord = - "\n" + - "\n" + - " \n" + // ServiceClassIDList - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ProtocolDescriptorList - " \n" + - " \n" + - " \n" + // L2CAP - " \n" + - " \n" + - " \n" + // RFCOMM - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ServiceName - " \n" + - " \n" + - "\n"; - Log.i(sLogName, xmlRecord); - return addServiceRecordFromXml(xmlRecord); - } - - - /** - * Add a new service record. - * @param record The byte[] record - * @return A handle to the new record - */ - public synchronized int addServiceRecord(byte[] record) throws IOException { - int handle = addServiceRecordNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordNative(byte[] record) - throws IOException; - - /** - * Add a new service record, using XML. - * @param record The record as an XML string - * @return A handle to the new record - */ - public synchronized int addServiceRecordFromXml(String record) throws IOException { - int handle = addServiceRecordFromXmlNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordFromXmlNative(String record) - throws IOException; - - /** - * Update an exisiting service record. - * @param handle Handle to exisiting record - * @param record The updated byte[] record - */ - public synchronized void updateServiceRecord(int handle, byte[] record) { - try { - updateServiceRecordNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordNative(int handle, byte[] record) - throws IOException; - - /** - * Update an exisiting record, using XML. - * @param handle Handle to exisiting record - * @param record The record as an XML string. - */ - public synchronized void updateServiceRecordFromXml(int handle, String record) { - try { - updateServiceRecordFromXmlNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordFromXmlNative(int handle, String record) - throws IOException; - - /** - * Remove a service record. - * It is only possible to remove service records that were added by the - * current connection. - * @param handle Handle to exisiting record to be removed - */ - public synchronized void removeServiceRecord(int handle) { - try { - removeServiceRecordNative(handle); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void removeServiceRecordNative(int handle) throws IOException; -} -- GitLab From 53ccaa4c296c58493dd6e7e5278d80d120bad0e0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 26 May 2009 19:13:43 -0700 Subject: [PATCH 0027/1408] New BluetoothSocket API. Modeled on blocking java.net.Socket and java.net.ServerSocket library. Public interface is: public final class BluetoothSocket implements Closeable { public static BluetoothSocket createRfcommSocket(String address, int port) throws IOException; public static BluetoothSocket createInsecureRfcommSocket(String address, int port) throws IOException; public void connect() throws IOException; public void close() throws IOException; public String getAddress(); public InputStream getInputStream() throws IOException; public OutputStream getOutputStream() throws IOException; } public final class BluetoothServerSocket implements Closeable { public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException; public static BluetoothServerSocket listenUsingUnsecureRfcommOn(int port) throws IOException; public BluetoothSocket accept() throws IOException; public BluetoothSocket accept(int timeout) throws IOException; public void close() throws IOException; } --- .../bluetooth/BluetoothInputStream.java | 62 ++ .../bluetooth/BluetoothOutputStream.java | 57 ++ .../bluetooth/BluetoothServerSocket.java | 124 ++++ .../android/bluetooth/BluetoothSocket.java | 176 +++++ .../java/android/bluetooth/RfcommSocket.java | 674 ------------------ 5 files changed, 419 insertions(+), 674 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothInputStream.java create mode 100644 framework/java/android/bluetooth/BluetoothOutputStream.java create mode 100644 framework/java/android/bluetooth/BluetoothServerSocket.java create mode 100644 framework/java/android/bluetooth/BluetoothSocket.java delete mode 100644 framework/java/android/bluetooth/RfcommSocket.java diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java new file mode 100644 index 00000000000..ceae70c586d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.InputStream; + +/** + * BluetoothInputStream. + * + * Used to write to a Bluetooth socket. + * + * TODO: Implement bulk writes (instead of one byte at a time). + * @hide + */ +/*package*/ final class BluetoothInputStream extends InputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothInputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Return number of bytes available before this stream will block. + */ + public int available() throws IOException { + return mSocket.availableNative(); + } + + public void close() throws IOException { + mSocket.close(); + } + + /** + * Reads a single byte from this stream and returns it as an integer in the + * range from 0 to 255. Returns -1 if the end of the stream has been + * reached. Blocks until one byte has been read, the end of the source + * stream is detected or an exception is thrown. + * + * @return the byte read or -1 if the end of stream has been reached. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.0 + */ + public int read() throws IOException { + return mSocket.readNative(); + } +} diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java new file mode 100644 index 00000000000..32e6d17c82a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.OutputStream; + +/** + * BluetoothOutputStream. + * + * Used to read from a Bluetooth socket. + * + * TODO: Implement bulk reads (instead of one byte at a time). + * @hide + */ +/*package*/ final class BluetoothOutputStream extends OutputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothOutputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Close this output stream and the socket associated with it. + */ + public void close() throws IOException { + mSocket.close(); + } + + /** + * Writes a single byte to this stream. Only the least significant byte of + * the integer {@code oneByte} is written to the stream. + * + * @param oneByte + * the byte to be written. + * @throws IOException + * if an error occurs while writing to this stream. + * @since Android 1.0 + */ + public void write(int oneByte) throws IOException { + mSocket.writeNative(oneByte); + } +} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java new file mode 100644 index 00000000000..ca467011c91 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2009 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 java.io.Closeable; +import java.io.IOException; + +/** + * Server (listening) Bluetooth Socket. + * + * Currently only supports RFCOMM sockets. + * + * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is + * also known as the Serial Port Profile (SPP). + * + * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Clean up javadoc grammer and formatting. + * TODO: Remove @hide + * @hide + */ +public final class BluetoothServerSocket implements Closeable { + private final BluetoothSocket mSocket; + + /** + * Construct a listening, secure RFCOMM server socket. + * The remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket(true, true); + try { + socket.mSocket.bindListenNative(port); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct an unencrypted, unauthenticated, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket(false, false); + try { + socket.mSocket.bindListenNative(port); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct a socket for incoming connections. + * @param auth Require the remote device to be authenticated + * @param encrypt Require the connection to be encrypted + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException { + mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1); + } + + /** + * Block until a connection is established. + * Returns a connected #BluetoothSocket. This server socket can be reused + * for subsequent incoming connections by calling #accept repeatedly. + * #close can be used to abort this call from another thread. + * @return A connected #BluetoothSocket + * @throws IOException On error, for example this call was aborted + */ + public BluetoothSocket accept() throws IOException { + return accept(-1); + } + + /** + * Block until a connection is established, with timeout. + * Returns a connected #BluetoothSocket. This server socket can be reused + * for subsequent incoming connections by calling #accept repeatedly. + * #close can be used to abort this call from another thread. + * @return A connected #BluetoothSocket + * @throws IOException On error, for example this call was aborted, or + * timeout + */ + public BluetoothSocket accept(int timeout) throws IOException { + return mSocket.acceptNative(timeout); + } + + /** + * Closes this socket. + * This will cause other blocking calls on this socket to immediately + * throw an IOException. + */ + public void close() throws IOException { + mSocket.closeNative(); + } +} diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java new file mode 100644 index 00000000000..fd8885ece99 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2009 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 java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Represents a connected or connecting Bluetooth Socket. + * + * Currently only supports RFCOMM sockets. + * + * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is + * also known as the Serial Port Profile (SPP). + * + * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Clean up javadoc grammer and formatting. + * TODO: Remove @hide + * @hide + */ +public final class BluetoothSocket implements Closeable { + private final int mPort; + private final String mAddress; /* remote address */ + private final boolean mAuth; + private final boolean mEncrypt; + private final BluetoothInputStream mInputStream; + private final BluetoothOutputStream mOutputStream; + + private int mSocketData; /* used by native code only */ + + /** + * Construct a secure RFCOMM socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will be authenticated and communication on this socket + * will be encrypted. + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @return an RFCOMM BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothSocket createRfcommSocket(String address, int port) + throws IOException { + return new BluetoothSocket(-1, true, true, address, port); + } + + /** + * Construct an insecure RFCOMM socket ready to start an outgoing + * connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will not be authenticated and communication on this + * socket will not be encrypted. + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @return An RFCOMM BluetoothSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothSocket createInsecureRfcommSocket(String address, int port) + throws IOException { + return new BluetoothSocket(-1, false, false, address, port); + } + + /** + * Construct a Bluetooth. + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param address remote Bluetooth address that this socket can connect to + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port) + throws IOException { + mAuth = auth; + mEncrypt = encrypt; + mAddress = address; + mPort = port; + if (fd == -1) { + initSocketNative(); + } else { + initSocketFromFdNative(fd); + } + mInputStream = new BluetoothInputStream(this); + mOutputStream = new BluetoothOutputStream(this); + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Attempt to connect to a remote device. + * This method will block until a connection is made or the connection + * fails. If this method returns without an exception then this socket + * is now connected. #close can be used to abort this call from another + * thread. + * @throws IOException On error, for example connection failure + */ + public void connect() throws IOException { + connectNative(mAddress, mPort, -1); + } + + /** + * Closes this socket. + * This will cause other blocking calls on this socket to immediately + * throw an IOException. + */ + public void close() throws IOException { + closeNative(); + } + + /** + * Return the address we are connecting, or connected, to. + * @return Bluetooth address, or null if this socket has not yet attempted + * or established a connection. + */ + public String getAddress() { + return mAddress; + } + + /** + * Get the input stream associated with this socket. + * The input stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return InputStream + */ + public InputStream getInputStream() throws IOException { + return mInputStream; + } + + /** + * Get the output stream associated with this socket. + * The output stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return OutputStream + */ + public OutputStream getOutputStream() throws IOException { + return mOutputStream; + } + + private native void initSocketNative(); + private native void initSocketFromFdNative(int fd); + private native void connectNative(String address, int port, int timeout); + /*package*/ native void bindListenNative(int port) throws IOException; + /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; + /*package*/ native int availableNative(); + /*package*/ native int readNative(); + /*package*/ native void writeNative(int data); + /*package*/ native void closeNative(); + private native void destroyNative(); +} diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java deleted file mode 100644 index a33263f5261..00000000000 --- a/framework/java/android/bluetooth/RfcommSocket.java +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (C) 2007 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 java.io.IOException; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.FileDescriptor; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket - * is similar to a normal socket in that it takes an address and a port number. - * The difference is of course that the address is a Bluetooth-device address, - * and the port number is an RFCOMM channel. The API allows for the - * establishment of listening sockets via methods - * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and - * {@link #accept(RfcommSocket, int) accept}, as well as for the making of - * outgoing connections with {@link #connect(String, int) connect}, - * {@link #connectAsync(String, int) connectAsync}, and - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * After constructing a socket, you need to {@link #create() create} it and then - * {@link #destroy() destroy} it when you are done using it. Both - * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return - * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. - * Alternatively, you may call {@link #getInputStream() getInputStream} and - * {@link #getOutputStream() getOutputStream} to retrieve the respective streams - * without going through the FileDescriptor. - * - * @hide - */ -public class RfcommSocket { - - /** - * Used by the native implementation of the class. - */ - private int mNativeData; - - /** - * Used by the native implementation of the class. - */ - private int mPort; - - /** - * Used by the native implementation of the class. - */ - private String mAddress; - - /** - * We save the return value of {@link #create() create} and - * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to - * retrieve the I/O streams. - */ - private FileDescriptor mFd; - - /** - * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, - * if the return value is zero, then, the the remaining time left to wait is - * written into this variable (by the native implementation). It is possible - * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before - * the user-specified timeout expires, which is why we save the remaining - * time in this member variable for the user to retrieve by calling method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. - */ - private int mTimeoutRemainingMs; - - /** - * Set to true when an asynchronous (nonblocking) connect is in progress. - * {@see #connectAsync(String,int)}. - */ - private boolean mIsConnecting; - - /** - * Set to true after a successful call to {@link #bind(String,int) bind} and - * used for error checking in {@link #listen(int) listen}. Reset to false - * on {@link #destroy() destroy}. - */ - private boolean mIsBound = false; - - /** - * Set to true after a successful call to {@link #listen(int) listen} and - * used for error checking in {@link #accept(RfcommSocket,int) accept}. - * Reset to false on {@link #destroy() destroy}. - */ - private boolean mIsListening = false; - - /** - * Used to store the remaining time after an accept with a non-negative - * timeout returns unsuccessfully. It is possible that a blocking - * {@link #accept(int) accept} may wait for less than the time specified by - * the user, which is why we store the remainder in this member variable for - * it to be retrieved with method - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. - */ - private int mAcceptTimeoutRemainingMs; - - /** - * Maintained by {@link #getInputStream() getInputStream}. - */ - protected FileInputStream mInputStream; - - /** - * Maintained by {@link #getOutputStream() getOutputStream}. - */ - protected FileOutputStream mOutputStream; - - private native void initializeNativeDataNative(); - - /** - * Constructor. - */ - public RfcommSocket() { - initializeNativeDataNative(); - } - - private native void cleanupNativeDataNative(); - - /** - * Called by the GC to clean up the native data that we set up when we - * construct the object. - */ - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private native static void classInitNative(); - - static { - classInitNative(); - } - - /** - * Creates a socket. You need to call this method before performing any - * other operation on a socket. - * - * @return FileDescriptor for the data stream. - * @throws IOException - * @see #destroy() - */ - public FileDescriptor create() throws IOException { - if (mFd == null) { - mFd = createNative(); - } - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - private native FileDescriptor createNative(); - - /** - * Destroys a socket created by {@link #create() create}. Call this - * function when you no longer use the socket in order to release the - * underlying OS resources. - * - * @see #create() - */ - public void destroy() { - synchronized (this) { - destroyNative(); - mFd = null; - mIsBound = false; - mIsListening = false; - } - } - - private native void destroyNative(); - - /** - * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. - * - * @return the FileDescriptor - * @throws IOException - * when the socket has not been {@link #create() created}. - */ - public FileDescriptor getFileDescriptor() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - /** - * Retrieves the input stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return InputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public InputStream getInputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mInputStream == null) { - mInputStream = new FileInputStream(mFd); - } - - return mInputStream; - } - } - - /** - * Retrieves the output stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return OutputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public OutputStream getOutputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mOutputStream == null) { - mOutputStream = new FileOutputStream(mFd); - } - - return mOutputStream; - } - } - - /** - * Starts a blocking connect to a remote RFCOMM socket. It takes the address - * of a device and the RFCOMM channel (port) to which to connect. - * - * @param address - * is the Bluetooth address of the remote device. - * @param port - * is the RFCOMM channel - * @return true on success, false on failure - * @throws IOException - * if {@link #create() create} has not been called. - * @see #connectAsync(String, int) - */ - public boolean connect(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return connectNative(address, port); - } - } - - private native boolean connectNative(String address, int port); - - /** - * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. - * It takes the address of the device to connect to, as well as the RFCOMM - * channel (port). On successful return (return value is true), you need to - * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to - * block for up to a specified number of milliseconds while waiting for the - * asyncronous connect to complete. - * - * @param address - * of remote device - * @param port - * the RFCOMM channel - * @return true when the asynchronous connect has successfully started, - * false if there was an error. - * @throws IOException - * is you have not called {@link #create() create} - * @see #waitForAsyncConnect(int) - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connect(String, int) - */ - public boolean connectAsync(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsConnecting = connectAsyncNative(address, port); - return mIsConnecting; - } - } - - private native boolean connectAsyncNative(String address, int port); - - /** - * Interrupts an asynchronous connect in progress. This method does nothing - * when there is no asynchronous connect in progress. - * - * @throws IOException - * if you have not called {@link #create() create}. - * @see #connectAsync(String, int) - */ - public void interruptAsyncConnect() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsConnecting) { - mIsConnecting = !interruptAsyncConnectNative(); - } - } - } - - private native boolean interruptAsyncConnectNative(); - - /** - * Tells you whether there is an asynchronous connect in progress. This - * method returns an undefined value when there is a synchronous connect in - * progress. - * - * @return true if there is an asyc connect in progress, false otherwise - * @see #connectAsync(String, int) - */ - public boolean isConnecting() { - return mIsConnecting; - } - - /** - * Blocks for a specified amount of milliseconds while waiting for an - * asynchronous connect to complete. Returns an integer value to indicate - * one of the following: the connect succeeded, the connect is still in - * progress, or the connect failed. It is possible for this method to block - * for less than the time specified by the user, and still return zero - * (i.e., async connect is still in progress.) For this reason, if the - * return value is zero, you need to call method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} - * to retrieve the remaining time. - * - * @param timeoutMs - * the time to block while waiting for the async connect to - * complete. - * @return a positive value if the connect succeeds; zero, if the connect is - * still in progress, and a negative value if the connect failed. - * - * @throws IOException - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connectAsync(String, int) - */ - public int waitForAsyncConnect(int timeoutMs) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - int ret = waitForAsyncConnectNative(timeoutMs); - if (ret != 0) { - mIsConnecting = false; - } - return ret; - } - } - - private native int waitForAsyncConnectNative(int timeoutMs); - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * It is possible that waitForAsyncConnect() waits for less than the time - * specified by the user, and still returns zero (i.e., async connect is - * still in progress.) For this reason, if the return value is zero, you - * need to call this method to retrieve the remaining time before you call - * waitForAsyncConnect again. - * - * @return the remaining timeout in milliseconds. - * @see #waitForAsyncConnect(int) - * @see #connectAsync(String, int) - */ - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /** - * Shuts down both directions on a socket. - * - * @return true on success, false on failure; if the return value is false, - * the socket might be left in a patially shut-down state (i.e. one - * direction is shut down, but the other is still open.) In this - * case, you should {@link #destroy() destroy} and then - * {@link #create() create} the socket again. - * @throws IOException - * is you have not caled {@link #create() create}. - * @see #shutdownInput() - * @see #shutdownOutput() - */ - public boolean shutdown() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (shutdownNative(true)) { - return shutdownNative(false); - } - - return false; - } - } - - /** - * Shuts down the input stream of the socket, but leaves the output stream - * in its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownOutput() - */ - public boolean shutdownInput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(true); - } - } - - /** - * Shut down the output stream of the socket, but leaves the input stream in - * its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownInput() - */ - public boolean shutdownOutput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(false); - } - } - - private native boolean shutdownNative(boolean shutdownInput); - - /** - * Tells you whether a socket is connected to another socket. This could be - * for input or output or both. - * - * @return true if connected, false otherwise. - * @see #isInputConnected() - * @see #isOutputConnected() - */ - public boolean isConnected() { - return isConnectedNative() > 0; - } - - /** - * Determines whether input is connected (i.e., whether you can receive data - * on this socket.) - * - * @return true if input is connected, false otherwise. - * @see #isConnected() - * @see #isOutputConnected() - */ - public boolean isInputConnected() { - return (isConnectedNative() & 1) != 0; - } - - /** - * Determines whether output is connected (i.e., whether you can send data - * on this socket.) - * - * @return true if output is connected, false otherwise. - * @see #isConnected() - * @see #isInputConnected() - */ - public boolean isOutputConnected() { - return (isConnectedNative() & 2) != 0; - } - - private native int isConnectedNative(); - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. The port is automatically selected as the first - * available port in the range 12 to 30. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * TODO: Use bind(0) in native code to have the kernel select an unused - * port. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - for (int port = 12; port <= 30; port++) { - if (bindNative(device, port)) { - mIsBound = true; - return true; - } - } - mIsBound = false; - return false; - } - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @param port - * RFCOMM channel to bind socket to. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device, int port) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsBound = bindNative(device, port); - return mIsBound; - } - - private native boolean bindNative(String device, int port); - - /** - * Starts listening for incoming connections on this socket, after it has - * been bound to an address and RFCOMM channel with - * {@link #bind(String,int) bind}. - * - * @param backlog - * the number of pending incoming connections to queue for - * {@link #accept(RfcommSocket, int) accept}. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} or if the - * socket has not been bound to a device and RFCOMM channel. - */ - public boolean listen(int backlog) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsBound) { - throw new IOException("socket not bound"); - } - mIsListening = listenNative(backlog); - return mIsListening; - } - - private native boolean listenNative(int backlog); - - /** - * Accepts incoming-connection requests for a listening socket bound to an - * RFCOMM channel. The user may provide a time to wait for an incoming - * connection. - * - * Note that this method may return null (i.e., no incoming connection) - * before the user-specified timeout expires. For this reason, on a null - * return value, you need to call - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} - * in order to see how much time is left to wait, before you call this - * method again. - * - * @param newSock - * is set to the new socket that is created as a result of a - * successful accept. - * @param timeoutMs - * time (in milliseconds) to block while waiting to an - * incoming-connection request. A negative value is an infinite - * wait. - * @return FileDescriptor of newSock on success, null on failure. Failure - * occurs if the timeout expires without a successful connect. - * @throws IOException - * if the socket has not been {@link #create() create}ed, is - * not bound, or is not a listening socket. - * @see #bind(String, int) - * @see #listen(int) - * @see #getRemainingAcceptWaitingTimeMs() - */ - public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) - throws IOException { - synchronized (newSock) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsListening == false) { - throw new IOException("not listening on socket"); - } - newSock.mFd = acceptNative(newSock, timeoutMs); - return newSock.mFd; - } - } - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #accept(RfcommSocket, int) accept}. - * - * Since accept() may return null (i.e., no incoming connection) before the - * user-specified timeout expires, you need to call this method in order to - * see how much time is left to wait, and wait for that amount of time - * before you call accept again. - * - * @return the remaining time, in milliseconds. - */ - public int getRemainingAcceptWaitingTimeMs() { - return mAcceptTimeoutRemainingMs; - } - - private native FileDescriptor acceptNative(RfcommSocket newSock, - int timeoutMs); - - /** - * Get the port (rfcomm channel) associated with this socket. - * - * This is only valid if the port has been set via a successful call to - * {@link #bind(String, int)}, {@link #connect(String, int)} - * or {@link #connectAsync(String, int)}. This can be checked - * with {@link #isListening()} and {@link #isConnected()}. - * @return Port (rfcomm channel) - */ - public int getPort() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsListening && !isConnected()) { - throw new IOException("not listening or connected on socket"); - } - return mPort; - } - - /** - * Return true if this socket is listening ({@link #listen(int)} - * has been called successfully). - */ - public boolean isListening() { - return mIsListening; - } -} -- GitLab From 731bcb12e10d3527cf6c15d5e0da6161482787c0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 1 Jun 2009 19:09:37 -0700 Subject: [PATCH 0028/1408] Implement bulk read and writes for Bluetooth sockets. Before: 0.1 kB/s After: 100 kB/s (in my java BT speed test app) --- .../bluetooth/BluetoothInputStream.java | 42 +++++++++++++++++-- .../bluetooth/BluetoothOutputStream.java | 34 ++++++++++++++- .../android/bluetooth/BluetoothSocket.java | 4 +- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index ceae70c586d..e6f501c9b69 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -24,7 +24,6 @@ import java.io.InputStream; * * Used to write to a Bluetooth socket. * - * TODO: Implement bulk writes (instead of one byte at a time). * @hide */ /*package*/ final class BluetoothInputStream extends InputStream { @@ -54,9 +53,46 @@ import java.io.InputStream; * @return the byte read or -1 if the end of stream has been reached. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 + * @since Android 1.5 */ public int read() throws IOException { - return mSocket.readNative(); + byte b[] = new byte[1]; + int ret = mSocket.readNative(b, 0, 1); + if (ret == 1) { + return (int)b[0]; + } else { + return -1; + } + } + + /** + * Reads at most {@code length} bytes from this stream and stores them in + * the byte array {@code b} starting at {@code offset}. + * + * @param b + * the byte array in which to store the bytes read. + * @param offset + * the initial position in {@code buffer} to store the bytes + * read from this stream. + * @param length + * the maximum number of bytes to store in {@code b}. + * @return the number of bytes actually read or -1 if the end of the stream + * has been reached. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code length < 0}, or if + * {@code offset + length} is greater than the length of + * {@code b}. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.5 + */ + public int read(byte[] b, int offset, int length) throws IOException { + if (b == null) { + throw new NullPointerException("byte array is null"); + } + if ((offset | length) < 0 || length > b.length - offset) { + throw new ArrayIndexOutOfBoundsException("invalid offset or length"); + } + return mSocket.readNative(b, offset, length); } } diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index 32e6d17c82a..7e2ead478af 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -24,7 +24,6 @@ import java.io.OutputStream; * * Used to read from a Bluetooth socket. * - * TODO: Implement bulk reads (instead of one byte at a time). * @hide */ /*package*/ final class BluetoothOutputStream extends OutputStream { @@ -52,6 +51,37 @@ import java.io.OutputStream; * @since Android 1.0 */ public void write(int oneByte) throws IOException { - mSocket.writeNative(oneByte); + byte b[] = new byte[1]; + b[0] = (byte)oneByte; + mSocket.writeNative(b, 0, 1); + } + + /** + * Writes {@code count} bytes from the byte array {@code buffer} starting + * at position {@code offset} to this stream. + * + * @param b + * the buffer to be written. + * @param offset + * the start position in {@code buffer} from where to get bytes. + * @param count + * the number of bytes from {@code buffer} to write to this + * stream. + * @throws IOException + * if an error occurs while writing to this stream. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code count < 0}, or if + * {@code offset + count} is bigger than the length of + * {@code buffer}. + * @since Android 1.0 + */ + public void write(byte[] b, int offset, int count) throws IOException { + if (b == null) { + throw new NullPointerException("buffer is null"); + } + if ((offset | count) < 0 || count > b.length - offset) { + throw new IndexOutOfBoundsException("invalid offset or length"); + } + mSocket.writeNative(b, offset, count); } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index fd8885ece99..670146b245e 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -169,8 +169,8 @@ public final class BluetoothSocket implements Closeable { /*package*/ native void bindListenNative(int port) throws IOException; /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; /*package*/ native int availableNative(); - /*package*/ native int readNative(); - /*package*/ native void writeNative(int data); + /*package*/ native int readNative(byte[] b, int offset, int length); + /*package*/ native int writeNative(byte[] b, int offset, int length); /*package*/ native void closeNative(); private native void destroyNative(); } -- GitLab From cb32d7c30a5ae45bc6e8a81ae59b1e75bdff0c74 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 2 Jun 2009 15:57:18 -0700 Subject: [PATCH 0029/1408] Implement and expose SCO socket support in BluetoothSocket.java. Implement L2CAP socket support, but do not expose it (untested). NEXT: Switch to Builder style constructor instead of factory method. --- .../bluetooth/BluetoothServerSocket.java | 44 +++++++++++---- .../android/bluetooth/BluetoothSocket.java | 53 +++++++++++++------ 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index ca467011c91..f3baeab18de 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -27,7 +27,7 @@ import java.io.IOException; * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is * also known as the Serial Port Profile (SPP). * - * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Consider exposing L2CAP sockets. * TODO: Clean up javadoc grammer and formatting. * TODO: Remove @hide * @hide @@ -45,9 +45,10 @@ public final class BluetoothServerSocket implements Closeable { * insufficient permissions. */ public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket(true, true); + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, port); try { - socket.mSocket.bindListenNative(port); + socket.mSocket.bindListenNative(); } catch (IOException e) { try { socket.close(); @@ -65,9 +66,31 @@ public final class BluetoothServerSocket implements Closeable { * insufficient permissions. */ public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket(false, false); + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, false, false, port); try { - socket.mSocket.bindListenNative(port); + socket.mSocket.bindListenNative(); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct a SCO server socket. + * Call #accept to retrieve connections to this socket. + * @return A SCO BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingScoOn() throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_SCO, false, false, -1); + try { + socket.mSocket.bindListenNative(); } catch (IOException e) { try { socket.close(); @@ -79,13 +102,16 @@ public final class BluetoothServerSocket implements Closeable { /** * Construct a socket for incoming connections. - * @param auth Require the remote device to be authenticated - * @param encrypt Require the connection to be encrypted + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port * @throws IOException On error, for example Bluetooth not available, or * insufficient priveleges */ - private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException { - mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1); + private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) + throws IOException { + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port); } /** diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 670146b245e..de1f32601db 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -29,13 +29,19 @@ import java.io.OutputStream; * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is * also known as the Serial Port Profile (SPP). * - * TODO: Consider implementing SCO and L2CAP sockets. + * TODO: Consider exposing L2CAP sockets. * TODO: Clean up javadoc grammer and formatting. * TODO: Remove @hide * @hide */ public final class BluetoothSocket implements Closeable { - private final int mPort; + /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */ + /*package*/ static final int TYPE_RFCOMM = 1; + /*package*/ static final int TYPE_SCO = 2; + /*package*/ static final int TYPE_L2CAP = 3; + + private final int mType; /* one of TYPE_RFCOMM etc */ + private final int mPort; /* RFCOMM channel or L2CAP psm */ private final String mAddress; /* remote address */ private final boolean mAuth; private final boolean mEncrypt; @@ -57,7 +63,7 @@ public final class BluetoothSocket implements Closeable { */ public static BluetoothSocket createRfcommSocket(String address, int port) throws IOException { - return new BluetoothSocket(-1, true, true, address, port); + return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port); } /** @@ -74,11 +80,25 @@ public final class BluetoothSocket implements Closeable { */ public static BluetoothSocket createInsecureRfcommSocket(String address, int port) throws IOException { - return new BluetoothSocket(-1, false, false, address, port); + return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port); + } + + /** + * Construct a SCO socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * @param address remote Bluetooth address that this socket can connect to + * @return a SCO BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothSocket createScoSocket(String address, int port) + throws IOException { + return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port); } /** * Construct a Bluetooth. + * @param type type of socket * @param fd fd to use for connected socket, or -1 for a new socket * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted @@ -87,8 +107,9 @@ public final class BluetoothSocket implements Closeable { * @throws IOException On error, for example Bluetooth not available, or * insufficient priveleges */ - /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port) - throws IOException { + /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, + int port) throws IOException { + mType = type; mAuth = auth; mEncrypt = encrypt; mAddress = address; @@ -120,7 +141,7 @@ public final class BluetoothSocket implements Closeable { * @throws IOException On error, for example connection failure */ public void connect() throws IOException { - connectNative(mAddress, mPort, -1); + connectNative(); } /** @@ -163,14 +184,14 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } - private native void initSocketNative(); - private native void initSocketFromFdNative(int fd); - private native void connectNative(String address, int port, int timeout); - /*package*/ native void bindListenNative(int port) throws IOException; + private native void initSocketNative() throws IOException; + private native void initSocketFromFdNative(int fd) throws IOException; + private native void connectNative() throws IOException; + /*package*/ native void bindListenNative() throws IOException; /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; - /*package*/ native int availableNative(); - /*package*/ native int readNative(byte[] b, int offset, int length); - /*package*/ native int writeNative(byte[] b, int offset, int length); - /*package*/ native void closeNative(); - private native void destroyNative(); + /*package*/ native int availableNative() throws IOException; + /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException; + /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException; + /*package*/ native void closeNative() throws IOException; + private native void destroyNative() throws IOException; } -- GitLab From 879bf5b24a4d7346bf750d18b3a0efa64c0e78c0 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 5 May 2009 22:26:12 -0700 Subject: [PATCH 0030/1408] Framework changes for bluez4. Changes in the Bluetooth JNI calls and framework functions for Bluez4. --- .../android/bluetooth/BluetoothDevice.java | 192 ++---------------- .../java/android/bluetooth/BluetoothUuid.java | 62 ++++++ .../android/bluetooth/IBluetoothDevice.aidl | 26 +-- .../bluetooth/IBluetoothDeviceCallback.aidl | 25 --- 4 files changed, 80 insertions(+), 225 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothUuid.java delete mode 100644 framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 951b4b0ab47..c942a27e8cd 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -75,7 +75,7 @@ public class BluetoothDevice { public static final int UNBOND_REASON_REMOVED = 6; private static final String TAG = "BluetoothDevice"; - + private final IBluetoothDevice mService; /** * @hide - hide this because it takes a parameter of type @@ -180,31 +180,6 @@ public class BluetoothDevice { return false; } - public String getVersion() { - try { - return mService.getVersion(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRevision() { - try { - return mService.getRevision(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getManufacturer() { - try { - return mService.getManufacturer(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getCompany() { - try { - return mService.getCompany(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - /** * Get the current scan mode. * Used to determine if the local device is connectable and/or discoverable @@ -241,11 +216,8 @@ public class BluetoothDevice { } public boolean startDiscovery() { - return startDiscovery(true); - } - public boolean startDiscovery(boolean resolveNames) { try { - return mService.startDiscovery(resolveNames); + return mService.startDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -263,88 +235,17 @@ public class BluetoothDevice { return false; } - public boolean startPeriodicDiscovery() { - try { - return mService.startPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean stopPeriodicDiscovery() { - try { - return mService.stopPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean isPeriodicDiscovery() { - try { - return mService.isPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public String[] listRemoteDevices() { - try { - return mService.listRemoteDevices(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * List remote devices that have a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @return bluetooth hardware addresses of remote devices with a current - * ACL connection. Array size is 0 if no devices have a - * connection. Null on error. - */ - public String[] listAclConnections() { - try { - return mService.listAclConnections(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Check if a specified remote device has a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. - * - * @param address the Bluetooth hardware address you want to check. - * @return true if there is an ACL connection, false otherwise and on - * error. - */ - public boolean isAclConnected(String address) { - try { - return mService.isAclConnected(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - /** - * Perform a low level (ACL) disconnection of a remote device. - * - * This forcably disconnects the ACL layer connection to a remote device, - * which will cause all RFCOMM, SDP and L2CAP connections to this remote - * device to close. + * Removes the remote device and the pairing information associated + * with it. * * @param address the Bluetooth hardware address you want to disconnect. * @return true if the device was disconnected, false otherwise and on * error. */ - public boolean disconnectRemoteDeviceAcl(String address) { + public boolean removeBond(String address) { try { - return mService.disconnectRemoteDeviceAcl(address); + return mService.removeBond(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -376,16 +277,6 @@ public class BluetoothDevice { return false; } - /** - * Remove an already exisiting bonding (delete the link key). - */ - public boolean removeBond(String address) { - try { - return mService.removeBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - /** * List remote devices that are bonded (paired) to the local device. * @@ -440,78 +331,25 @@ public class BluetoothDevice { return null; } - public String getRemoteVersion(String address) { - try { - return mService.getRemoteVersion(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteRevision(String address) { - try { - return mService.getRemoteRevision(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteManufacturer(String address) { - try { - return mService.getRemoteManufacturer(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRemoteCompany(String address) { - try { - return mService.getRemoteCompany(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - /** - * Returns the RFCOMM channel associated with the 16-byte UUID on - * the remote Bluetooth address. - * - * Performs a SDP ServiceSearchAttributeRequest transaction. The provided - * uuid is verified in the returned record. If there was a problem, or the - * specified uuid does not exist, -1 is returned. - */ - public boolean getRemoteServiceChannel(String address, short uuid16, - IBluetoothDeviceCallback callback) { - try { - return mService.getRemoteServiceChannel(address, uuid16, callback); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Get the major, minor and servics classes of a remote device. - * These classes are encoded as a 32-bit integer. See BluetoothClass. - * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass, or - * BluetoothClass.ERROR on error - */ public int getRemoteClass(String address) { try { return mService.getRemoteClass(address); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothClass.ERROR; + return BluetoothError.ERROR_IPC; } - public byte[] getRemoteFeatures(String address) { + public String[] getRemoteUuids(String address) { try { - return mService.getRemoteFeatures(address); + return mService.getRemoteUuids(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String lastSeen(String address) { - try { - return mService.lastSeen(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastUsed(String address) { - try { - return mService.lastUsed(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + + public int getRemoteServiceChannel(String address, String uuid) { + try { + return mService.getRemoteServiceChannel(address, uuid); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; } public boolean setPin(String address, byte[] pin) { diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java new file mode 100644 index 00000000000..96b93f9b18e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 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 java.util.UUID; + +/** +* Static helper methods and constants to decode the UUID of remote devices. +* @hide +*/ +public final class BluetoothUuid { + + /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs + * for the various services. + * + * The following 128 bit values are calculated as: + * uuid * 2^96 + BASE_UUID + */ + public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB"); + public static final UUID Handsfree = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB"); + public static final UUID HandsfreeAudioGateway + = UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB"); + + public static boolean isAudioSource(UUID uuid) { + return uuid.equals(AudioSource); + } + + public static boolean isAudioSink(UUID uuid) { + return uuid.equals(AudioSink); + } + + public static boolean isAdvAudioDist(UUID uuid) { + return uuid.equals(AdvAudioDist); + } + + public static boolean isHandsfree(UUID uuid) { + return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway); + } + + public static boolean isHeadset(UUID uuid) { + return uuid.equals(HSP) || uuid.equals(HeadsetHS); + } +} + diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index 6cd792e26a5..c249c817a46 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.IBluetoothDeviceCallback; - /** * System private API for talking with the Bluetooth service. * @@ -33,10 +31,6 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getVersion(); - String getRevision(); - String getManufacturer(); - String getCompany(); int getScanMode(); boolean setScanMode(int mode); @@ -44,17 +38,9 @@ interface IBluetoothDevice int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); - boolean startDiscovery(boolean resolveNames); + boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); - boolean startPeriodicDiscovery(); - boolean stopPeriodicDiscovery(); - boolean isPeriodicDiscovery(); - String[] listRemoteDevices(); - - String[] listAclConnections(); - boolean isAclConnected(in String address); - boolean disconnectRemoteDeviceAcl(in String address); boolean createBond(in String address); boolean cancelBondProcess(in String address); @@ -63,15 +49,9 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteVersion(in String address); - String getRemoteRevision(in String address); int getRemoteClass(in String address); - String getRemoteManufacturer(in String address); - String getRemoteCompany(in String address); - boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); - byte[] getRemoteFeatures(in String adddress); - String lastSeen(in String address); - String lastUsed(in String address); + String[] getRemoteUuids(in String address); + int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); boolean cancelPin(in String address); diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl deleted file mode 100644 index d05709330b7..00000000000 --- a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2008, 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; - -/** - * {@hide} - */ -oneway interface IBluetoothDeviceCallback -{ - void onGetRemoteServiceChannelResult(in String address, int channel); -} -- GitLab From c232d9a813a65d22787b37c0b866759a9020de80 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 18 Jun 2009 16:23:15 -0700 Subject: [PATCH 0031/1408] Fix bug in BluetoothInputStream.read(). InputStream.read() must return values in range [0, 255]. But the previous code would sign extend when casting to int so return [-128, 127]. Bitwise AND with 0xff to remove sign extension. --- framework/java/android/bluetooth/BluetoothInputStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index e6f501c9b69..c060f3263ee 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -59,7 +59,7 @@ import java.io.InputStream; byte b[] = new byte[1]; int ret = mSocket.readNative(b, 0, 1); if (ret == 1) { - return (int)b[0]; + return (int)b[0] & 0xff; } else { return -1; } -- GitLab From 312cd3a838b216b894c600a18a0208ad6fc3478b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 19 Jun 2009 10:08:09 -0700 Subject: [PATCH 0032/1408] Add getBatteryUsageHint() to BluetoothHeadset for power monitoring. This is a monotonically increasing integer. Wraps to 0 at Integer.MAX_INT, and at boot. Current implementation returns the number of AT commands handled since boot. This is a good indicator for spammy headset/handsfree units that can keep the device awake by polling for cellular status updates. As a rule of thumb, each AT command prevents the CPU from sleeping for 500 ms --- .../android/bluetooth/BluetoothHeadset.java | 25 +++++++++++++++++++ .../java/android/bluetooth/HeadsetBase.java | 16 +++++++++++- .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e1984353e88..fe1e09af984 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -331,6 +331,31 @@ public class BluetoothHeadset { return -1; } + /** + * Get battery usage hint for Bluetooth Headset service. + * This is a monotonically increasing integer. Wraps to 0 at + * Integer.MAX_INT, and at boot. + * Current implementation returns the number of AT commands handled since + * boot. This is a good indicator for spammy headset/handsfree units that + * can keep the device awake by polling for cellular status updates. As a + * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms + * @return monotonically increasing battery usage hint, or a negative error + * code on error + * @hide + */ + public int getBatteryUsageHint() { + if (DBG) log("getBatteryUsageHint()"); + if (mService != null) { + try { + return mService.getBatteryUsageHint(); + } 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 -1; + } + /** * Check class bits for possible HSP or HFP support. * This is a simple heuristic that tries to guess if a device with the diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index f31e7a2c65e..f987ffdd440 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -40,6 +40,8 @@ public class HeadsetBase { public static final int DIRECTION_INCOMING = 1; public static final int DIRECTION_OUTGOING = 2; + private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ + private final BluetoothDevice mBluetooth; private final String mAddress; private final int mRfcommChannel; @@ -109,6 +111,14 @@ public class HeadsetBase { acquireWakeLock(); long timestamp; + synchronized(HeadsetBase.class) { + if (sAtInputCount == Integer.MAX_VALUE) { + sAtInputCount = 0; + } else { + sAtInputCount++; + } + } + if (DBG) timestamp = System.currentTimeMillis(); AtCommandResult result = mAtParser.process(input); if (DBG) Log.d(TAG, "Processing " + input + " took " + @@ -279,7 +289,11 @@ public class HeadsetBase { } } - private void log(String msg) { + public static int getAtInputCount() { + return sAtInputCount; + } + + private static void log(String msg) { Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 582d4e340ee..5f42fd6521b 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -31,4 +31,5 @@ interface IBluetoothHeadset { boolean stopVoiceRecognition(); boolean setPriority(in String address, int priority); int getPriority(in String address); + int getBatteryUsageHint(); } -- GitLab From 4eb5ccc6539e8b56f24347fd546dc4a052cf6c1f Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 14 Jul 2009 12:21:26 -0700 Subject: [PATCH 0033/1408] Reconnect to A2DP device only if it supports that profile, fix HF AG UUID. --- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 96b93f9b18e..f8316a5bac9 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -52,7 +52,7 @@ public final class BluetoothUuid { } public static boolean isHandsfree(UUID uuid) { - return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway); + return uuid.equals(Handsfree); } public static boolean isHeadset(UUID uuid) { -- GitLab From d1a0bd8fd2e21d0cceb55872571edd3b6cc332ad Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 16 Jul 2009 18:26:28 -0700 Subject: [PATCH 0034/1408] Initial support of 2.1 pairing. Note: Some cases have not been tested yet, as we would need to get proper UI support. --- .../android/bluetooth/BluetoothDevice.java | 27 +++++++++++++++++-- .../android/bluetooth/BluetoothIntent.java | 4 +++ .../android/bluetooth/IBluetoothDevice.aidl | 5 +++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c942a27e8cd..a64c6d72d43 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -74,6 +74,14 @@ public class BluetoothDevice { /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 6; + /* The user will be prompted to enter a pin */ + public static final int PAIRING_VARIANT_PIN = 0; + /* The user will be prompted to enter a passkey */ + public static final int PAIRING_VARIANT_PASSKEY = 1; + /* The user will be prompted to confirm the passkey displayed on the screen */ + public static final int PAIRING_VARIANT_CONFIRMATION = 2; + + private static final String TAG = "BluetoothDevice"; private final IBluetoothDevice mService; @@ -358,9 +366,24 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean cancelPin(String address) { + + public boolean setPasskey(String address, int passkey) { + try { + return mService.setPasskey(address, passkey); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean setPairingConfirmation(String address, boolean confirm) { + try { + return mService.setPairingConfirmation(address, confirm); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean cancelPairingUserInput(String address) { try { - return mService.cancelPin(address); + return mService.cancelPairingUserInput(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 344601b0b89..d6c79b40490 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -57,6 +57,10 @@ public interface BluetoothIntent { "android.bluetooth.intent.BOND_PREVIOUS_STATE"; public static final String REASON = "android.bluetooth.intent.REASON"; + public static final String PAIRING_VARIANT = + "android.bluetooth.intent.PAIRING_VARIANT"; + public static final String PASSKEY = + "android.bluetooth.intent.PASSKEY"; /** Broadcast when the local Bluetooth device state changes, for example * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index c249c817a46..a78752bff5e 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -54,5 +54,8 @@ interface IBluetoothDevice int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); - boolean cancelPin(in String address); + boolean setPasskey(in String address, int passkey); + boolean setPairingConfirmation(in String address, boolean confirm); + boolean cancelPairingUserInput(in String address); + } -- GitLab From 76d5a71ae16c0c17d97b969e2a23974472898ed5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 17 Jul 2009 14:45:22 -0700 Subject: [PATCH 0035/1408] Revert "Initial support of 2.1 pairing." This reverts commit d1a0bd8fd2e21d0cceb55872571edd3b6cc332ad. --- .../android/bluetooth/BluetoothDevice.java | 27 ++----------------- .../android/bluetooth/BluetoothIntent.java | 4 --- .../android/bluetooth/IBluetoothDevice.aidl | 5 +--- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a64c6d72d43..c942a27e8cd 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -74,14 +74,6 @@ public class BluetoothDevice { /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 6; - /* The user will be prompted to enter a pin */ - public static final int PAIRING_VARIANT_PIN = 0; - /* The user will be prompted to enter a passkey */ - public static final int PAIRING_VARIANT_PASSKEY = 1; - /* The user will be prompted to confirm the passkey displayed on the screen */ - public static final int PAIRING_VARIANT_CONFIRMATION = 2; - - private static final String TAG = "BluetoothDevice"; private final IBluetoothDevice mService; @@ -366,24 +358,9 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - - public boolean setPasskey(String address, int passkey) { - try { - return mService.setPasskey(address, passkey); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public boolean setPairingConfirmation(String address, boolean confirm) { - try { - return mService.setPairingConfirmation(address, confirm); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public boolean cancelPairingUserInput(String address) { + public boolean cancelPin(String address) { try { - return mService.cancelPairingUserInput(address); + return mService.cancelPin(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index d6c79b40490..344601b0b89 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -57,10 +57,6 @@ public interface BluetoothIntent { "android.bluetooth.intent.BOND_PREVIOUS_STATE"; public static final String REASON = "android.bluetooth.intent.REASON"; - public static final String PAIRING_VARIANT = - "android.bluetooth.intent.PAIRING_VARIANT"; - public static final String PASSKEY = - "android.bluetooth.intent.PASSKEY"; /** Broadcast when the local Bluetooth device state changes, for example * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index a78752bff5e..c249c817a46 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -54,8 +54,5 @@ interface IBluetoothDevice int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); - boolean setPasskey(in String address, int passkey); - boolean setPairingConfirmation(in String address, boolean confirm); - boolean cancelPairingUserInput(in String address); - + boolean cancelPin(in String address); } -- GitLab From 541330b82b7e541ee635df8650bd7077caf965dd Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 16 Jul 2009 18:26:28 -0700 Subject: [PATCH 0036/1408] Initial support of 2.1 pairing. Note: Some cases have not been tested yet, as we would need to get proper UI support. --- .../android/bluetooth/BluetoothDevice.java | 27 +++++++++++++++++-- .../android/bluetooth/BluetoothIntent.java | 4 +++ .../android/bluetooth/IBluetoothDevice.aidl | 5 +++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c942a27e8cd..a64c6d72d43 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -74,6 +74,14 @@ public class BluetoothDevice { /** An existing bond was explicitly revoked */ public static final int UNBOND_REASON_REMOVED = 6; + /* The user will be prompted to enter a pin */ + public static final int PAIRING_VARIANT_PIN = 0; + /* The user will be prompted to enter a passkey */ + public static final int PAIRING_VARIANT_PASSKEY = 1; + /* The user will be prompted to confirm the passkey displayed on the screen */ + public static final int PAIRING_VARIANT_CONFIRMATION = 2; + + private static final String TAG = "BluetoothDevice"; private final IBluetoothDevice mService; @@ -358,9 +366,24 @@ public class BluetoothDevice { } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean cancelPin(String address) { + + public boolean setPasskey(String address, int passkey) { + try { + return mService.setPasskey(address, passkey); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean setPairingConfirmation(String address, boolean confirm) { + try { + return mService.setPairingConfirmation(address, confirm); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public boolean cancelPairingUserInput(String address) { try { - return mService.cancelPin(address); + return mService.cancelPairingUserInput(address); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 344601b0b89..d6c79b40490 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -57,6 +57,10 @@ public interface BluetoothIntent { "android.bluetooth.intent.BOND_PREVIOUS_STATE"; public static final String REASON = "android.bluetooth.intent.REASON"; + public static final String PAIRING_VARIANT = + "android.bluetooth.intent.PAIRING_VARIANT"; + public static final String PASSKEY = + "android.bluetooth.intent.PASSKEY"; /** Broadcast when the local Bluetooth device state changes, for example * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetoothDevice.aidl index c249c817a46..a78752bff5e 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothDevice.aidl @@ -54,5 +54,8 @@ interface IBluetoothDevice int getRemoteServiceChannel(in String address, String uuid); boolean setPin(in String address, in byte[] pin); - boolean cancelPin(in String address); + boolean setPasskey(in String address, int passkey); + boolean setPairingConfirmation(in String address, boolean confirm); + boolean cancelPairingUserInput(in String address); + } -- GitLab From 1c170a9cdcd486c5bcca706782125743c4f64bbf Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Tue, 21 Jul 2009 11:16:54 -0700 Subject: [PATCH 0037/1408] donut snapshot --- .../android/bluetooth/BluetoothHeadset.java | 25 +++++++++++++++++++ .../java/android/bluetooth/HeadsetBase.java | 16 +++++++++++- .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e1984353e88..fe1e09af984 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -331,6 +331,31 @@ public class BluetoothHeadset { return -1; } + /** + * Get battery usage hint for Bluetooth Headset service. + * This is a monotonically increasing integer. Wraps to 0 at + * Integer.MAX_INT, and at boot. + * Current implementation returns the number of AT commands handled since + * boot. This is a good indicator for spammy headset/handsfree units that + * can keep the device awake by polling for cellular status updates. As a + * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms + * @return monotonically increasing battery usage hint, or a negative error + * code on error + * @hide + */ + public int getBatteryUsageHint() { + if (DBG) log("getBatteryUsageHint()"); + if (mService != null) { + try { + return mService.getBatteryUsageHint(); + } 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 -1; + } + /** * Check class bits for possible HSP or HFP support. * This is a simple heuristic that tries to guess if a device with the diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index f31e7a2c65e..f987ffdd440 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -40,6 +40,8 @@ public class HeadsetBase { public static final int DIRECTION_INCOMING = 1; public static final int DIRECTION_OUTGOING = 2; + private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ + private final BluetoothDevice mBluetooth; private final String mAddress; private final int mRfcommChannel; @@ -109,6 +111,14 @@ public class HeadsetBase { acquireWakeLock(); long timestamp; + synchronized(HeadsetBase.class) { + if (sAtInputCount == Integer.MAX_VALUE) { + sAtInputCount = 0; + } else { + sAtInputCount++; + } + } + if (DBG) timestamp = System.currentTimeMillis(); AtCommandResult result = mAtParser.process(input); if (DBG) Log.d(TAG, "Processing " + input + " took " + @@ -279,7 +289,11 @@ public class HeadsetBase { } } - private void log(String msg) { + public static int getAtInputCount() { + return sAtInputCount; + } + + private static void log(String msg) { Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 582d4e340ee..5f42fd6521b 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -31,4 +31,5 @@ interface IBluetoothHeadset { boolean stopVoiceRecognition(); boolean setPriority(in String address, int priority); int getPriority(in String address); + int getBatteryUsageHint(); } -- GitLab From 57b0968b1ae7b12c2363388f6228ec63304de056 Mon Sep 17 00:00:00 2001 From: Jiafa Liu Date: Thu, 2 Jul 2009 16:36:02 +0800 Subject: [PATCH 0038/1408] frameworks/base: Add PBAP support - Update according to comments - Add aidl support in frameworks for Settings to retrieve current PBAP transaction status. - Add status bar support for PBAP --- .../java/android/bluetooth/BluetoothPbap.java | 261 ++++++++++++++++++ .../android/bluetooth/IBluetoothPbap.aidl | 30 ++ 2 files changed, 291 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothPbap.java create mode 100644 framework/java/android/bluetooth/IBluetoothPbap.aidl diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java new file mode 100644 index 00000000000..d6044145c2d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Public API for controlling the Bluetooth Pbap Service. This includes + * Bluetooth Phone book Access profile. + * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap + * Service via IPC. + * + * Creating a BluetoothPbap object will create a binding with the + * BluetoothPbap service. Users of this object should call close() when they + * are finished with the BluetoothPbap, so that this proxy object can unbind + * from the service. + * + * This BluetoothPbap object is not immediately bound to the + * BluetoothPbap service. Use the ServiceListener interface to obtain a + * notification when it is bound, this is especially important if you wish to + * immediately call methods on BluetoothPbap after construction. + * + * Android only supports one connected Bluetooth Pce at a time. + * + * @hide + */ +public class BluetoothPbap { + + private static final String TAG = "BluetoothPbap"; + private static final boolean DBG = false; + + /** int extra for PBAP_STATE_CHANGED_ACTION */ + public static final String PBAP_STATE = + "android.bluetooth.pbap.intent.PBAP_STATE"; + /** int extra for PBAP_STATE_CHANGED_ACTION */ + public static final String PBAP_PREVIOUS_STATE = + "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; + + /** Indicates the state of an pbap connection state has changed. + * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. + */ + public static final String PBAP_STATE_CHANGED_ACTION = + "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; + + private IBluetoothPbap mService; + private final Context mContext; + private final ServiceListener mServiceListener; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + /** No headset currently connected */ + public static final int STATE_DISCONNECTED = 0; + /** Connection attempt in progress */ + public static final int STATE_CONNECTING = 1; + /** A headset is currently connected */ + public static final int STATE_CONNECTED = 2; + + 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; + + public static final int PRIORITY_AUTO = 100; + public static final int PRIORITY_OFF = 0; + /** + * An interface for notifying Bluetooth PCE IPC clients when they have + * been connected to the BluetoothHeadset service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothPbap service. Clients must wait for + * this callback before making IPC calls on the BluetoothPbap + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothPbap service. Clients must not + * make IPC calls on the BluetoothPbap service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothPbap service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + /** + * Create a BluetoothHeadset proxy object. + */ + public BluetoothPbap(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service"); + } + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothHeadset will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized void close() { + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } + } + + /** + * Get the current state of the Bluetooth Headset service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Headset service. + */ + public int getState() { + if (DBG) log("getState()"); + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return BluetoothHeadset.STATE_ERROR; + } + + /** + * Get the Bluetooth address of the current headset. + * @return The Bluetooth address, or null if not in connected or connecting + * state, or if this proxy object is not connected to the Headset + * service. + */ + public String getPceAddress() { + if (DBG) log("getPceAddress()"); + if (mService != null) { + try { + return mService.getPceAddress(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return null; + } + + /** + * Returns true if the specified headset is connected (does not include + * connecting). Returns false if not connected, or if this proxy object + * if not currently connected to the headset service. + */ + public boolean isConnected(String address) { + if (DBG) log("isConnected(" + address + ")"); + if (mService != null) { + try { + return mService.isConnected(address); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Disconnects the current headset. Currently this call blocks, it may soon + * be made asynchornous. Returns false if this proxy object is + * not currently connected to the Headset service. + */ + public boolean disconnectPce() { + if (DBG) log("disconnectPce()"); + if (mService != null) { + try { + mService.disconnectPce(); + return true; + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Check class bits for possible PBAP support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support PBAP. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support PBAP. + */ + public static boolean doesClassMatchSink(int btClass) { + // TODO optimize the rule + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return true; + default: + return false; + } + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) log("Proxy object connected"); + mService = IBluetoothPbap.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) log("Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/IBluetoothPbap.aidl b/framework/java/android/bluetooth/IBluetoothPbap.aidl new file mode 100644 index 00000000000..06cdb7b972d --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothPbap.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 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; + +/** + * System private API for Bluetooth pbap service + * + * {@hide} + */ +interface IBluetoothPbap { + int getState(); + String getPceAddress(); + boolean connectPce(in String address); + void disconnectPce(); + boolean isConnected(in String address); +} -- GitLab From dd2c000b0956aabab9e20ab5332ac1ea0cc5b5a0 Mon Sep 17 00:00:00 2001 From: Jackson Fan Date: Tue, 28 Jul 2009 12:15:49 +0800 Subject: [PATCH 0039/1408] Modify BluetoothPbap for a few wrong wordings --- .../java/android/bluetooth/BluetoothPbap.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d6044145c2d..57826444665 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -16,8 +16,6 @@ package android.bluetooth; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -75,11 +73,11 @@ public class BluetoothPbap { /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; - /** No headset currently connected */ + /** No Pce currently connected */ public static final int STATE_DISCONNECTED = 0; /** Connection attempt in progress */ public static final int STATE_CONNECTING = 1; - /** A headset is currently connected */ + /** A Pce is currently connected */ public static final int STATE_CONNECTED = 2; public static final int RESULT_FAILURE = 0; @@ -87,11 +85,9 @@ public class BluetoothPbap { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - public static final int PRIORITY_AUTO = 100; - public static final int PRIORITY_OFF = 0; /** * An interface for notifying Bluetooth PCE IPC clients when they have - * been connected to the BluetoothHeadset service. + * been connected to the BluetoothPbap service. */ public interface ServiceListener { /** @@ -113,7 +109,7 @@ public class BluetoothPbap { } /** - * Create a BluetoothHeadset proxy object. + * Create a BluetoothPbap proxy object. */ public BluetoothPbap(Context context, ServiceListener l) { mContext = context; @@ -133,7 +129,7 @@ public class BluetoothPbap { /** * Close the connection to the backing service. - * Other public functions of BluetoothHeadset will return default error + * Other public functions of BluetoothPbap will return default error * results once close() has been called. Multiple invocations of close() * are ok. */ @@ -145,9 +141,9 @@ public class BluetoothPbap { } /** - * Get the current state of the Bluetooth Headset service. + * Get the current state of the BluetoothPbap service. * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Headset service. + * object is currently not connected to the Pbap service. */ public int getState() { if (DBG) log("getState()"); @@ -159,13 +155,13 @@ public class BluetoothPbap { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } - return BluetoothHeadset.STATE_ERROR; + return BluetoothPbap.STATE_ERROR; } /** - * Get the Bluetooth address of the current headset. + * Get the Bluetooth address of the current Pce. * @return The Bluetooth address, or null if not in connected or connecting - * state, or if this proxy object is not connected to the Headset + * state, or if this proxy object is not connected to the Pbap * service. */ public String getPceAddress() { @@ -182,9 +178,9 @@ public class BluetoothPbap { } /** - * Returns true if the specified headset is connected (does not include + * Returns true if the specified Pcs is connected (does not include * connecting). Returns false if not connected, or if this proxy object - * if not currently connected to the headset service. + * if not currently connected to the Pbap service. */ public boolean isConnected(String address) { if (DBG) log("isConnected(" + address + ")"); @@ -200,9 +196,9 @@ public class BluetoothPbap { } /** - * Disconnects the current headset. Currently this call blocks, it may soon + * Disconnects the current Pce. Currently this call blocks, it may soon * be made asynchornous. Returns false if this proxy object is - * not currently connected to the Headset service. + * not currently connected to the Pbap service. */ public boolean disconnectPce() { if (DBG) log("disconnectPce()"); -- GitLab From 671065d6585c7f7029fde056b21c3fa7c087b9e9 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 30 Jul 2009 13:32:25 -0700 Subject: [PATCH 0040/1408] Fix UUID typo and allow incoming AVRCP connections. --- .../java/android/bluetooth/BluetoothUuid.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index f8316a5bac9..1ec7fb38b44 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -30,14 +30,13 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - public static final UUID AudioSink = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - public static final UUID AudioSource = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final UUID AudioSink = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final UUID AudioSource = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); - public static final UUID HeadsetHS = UUID.fromString("00001131-0000-1000-8000-00805F9B34FB"); - public static final UUID Handsfree = UUID.fromString("0000111e-0000-1000-8000-00805F9B34FB"); - public static final UUID HandsfreeAudioGateway - = UUID.fromString("0000111f-0000-1000-8000-00805F9B34FB"); + public static final UUID Handsfree = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB"); + public static final UUID AvrcpController = + UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB"); public static boolean isAudioSource(UUID uuid) { return uuid.equals(AudioSource); @@ -56,7 +55,10 @@ public final class BluetoothUuid { } public static boolean isHeadset(UUID uuid) { - return uuid.equals(HSP) || uuid.equals(HeadsetHS); + return uuid.equals(HSP); } -} + public static boolean isAvrcpController(UUID uuid) { + return uuid.equals(AvrcpController); + } +} -- GitLab From 2d66488e7d7aeb87e4f7385dfe1a6c800c08391b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 14 Aug 2009 18:33:38 -0700 Subject: [PATCH 0041/1408] Bluetooth: API change. Split BluetoothDevice into BluetoothDevice and BluetoothAdapter. BluetoothAdapter: Represents the local BT adapter. Operations on the local adapter (start a scan, etc). BluetoothDevice: Represents a remote BT device. Operations on remote devices (pair, connect, etc). IBluetoothDevice.aidl -> Bluetooth.aidl BluetoothDeviceService.java -> BluetoothDeviceService.java TODO: Javadoc --- .../java/android/bluetooth/BluetoothA2dp.java | 66 ++-- .../android/bluetooth/BluetoothAdapter.java | 331 ++++++++++++++++ .../bluetooth/BluetoothAudioGateway.java | 60 ++- .../android/bluetooth/BluetoothDevice.aidl | 19 + .../android/bluetooth/BluetoothDevice.java | 357 +++++++----------- .../android/bluetooth/BluetoothHeadset.java | 56 +-- .../android/bluetooth/BluetoothIntent.java | 4 +- .../java/android/bluetooth/BluetoothPbap.java | 40 +- .../bluetooth/BluetoothServerSocket.java | 69 +--- .../android/bluetooth/BluetoothSocket.java | 86 ++--- .../java/android/bluetooth/HeadsetBase.java | 33 +- ...{IBluetoothDevice.aidl => IBluetooth.aidl} | 2 +- .../android/bluetooth/IBluetoothA2dp.aidl | 14 +- .../android/bluetooth/IBluetoothHeadset.aidl | 12 +- .../android/bluetooth/IBluetoothPbap.aidl | 10 +- 15 files changed, 670 insertions(+), 489 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdapter.java create mode 100644 framework/java/android/bluetooth/BluetoothDevice.aidl rename framework/java/android/bluetooth/{IBluetoothDevice.aidl => IBluetooth.aidl} (98%) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 2ea45d567f6..6e48b66baa1 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -25,7 +25,10 @@ import android.os.RemoteException; import android.os.IBinder; import android.util.Log; -import java.util.List; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; /** * Public API for controlling the Bluetooth A2DP Profile Service. @@ -47,7 +50,7 @@ import java.util.List; * * @hide */ -public class BluetoothA2dp { +public final class BluetoothA2dp { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = false; @@ -79,6 +82,7 @@ public class BluetoothA2dp { /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; + private final IBluetoothA2dp mService; private final Context mContext; @@ -89,6 +93,7 @@ public class BluetoothA2dp { */ public BluetoothA2dp(Context c) { mContext = c; + IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); if (b == null) { throw new RuntimeException("Bluetooth A2DP service not available!"); @@ -99,14 +104,14 @@ public class BluetoothA2dp { /** Initiate a connection to an A2DP sink. * Listen for SINK_STATE_CHANGED_ACTION to find out when the * connection is completed. - * @param address Remote BT address. + * @param device Remote BT device. * @return Result code, negative indicates an immediate error. * @hide */ - public int connectSink(String address) { - if (DBG) log("connectSink(" + address + ")"); + public int connectSink(BluetoothDevice device) { + if (DBG) log("connectSink(" + device + ")"); try { - return mService.connectSink(address); + return mService.connectSink(device); } catch (RemoteException e) { Log.w(TAG, "", e); return BluetoothError.ERROR_IPC; @@ -116,14 +121,14 @@ public class BluetoothA2dp { /** Initiate disconnect from an A2DP sink. * Listen for SINK_STATE_CHANGED_ACTION to find out when * disconnect is completed. - * @param address Remote BT address. + * @param device Remote BT device. * @return Result code, negative indicates an immediate error. * @hide */ - public int disconnectSink(String address) { - if (DBG) log("disconnectSink(" + address + ")"); + public int disconnectSink(BluetoothDevice device) { + if (DBG) log("disconnectSink(" + device + ")"); try { - return mService.disconnectSink(address); + return mService.disconnectSink(device); } catch (RemoteException e) { Log.w(TAG, "", e); return BluetoothError.ERROR_IPC; @@ -131,24 +136,25 @@ public class BluetoothA2dp { } /** Check if a specified A2DP sink is connected. - * @param address Remote BT address. + * @param device Remote BT device. * @return True if connected (or playing), false otherwise and on error. * @hide */ - public boolean isSinkConnected(String address) { - if (DBG) log("isSinkConnected(" + address + ")"); - int state = getSinkState(address); + public boolean isSinkConnected(BluetoothDevice device) { + if (DBG) log("isSinkConnected(" + device + ")"); + int state = getSinkState(device); return state == STATE_CONNECTED || state == STATE_PLAYING; } /** Check if any A2DP sink is connected. - * @return a List of connected A2DP sinks, or null on error. + * @return a unmodifiable set of connected A2DP sinks, or null on error. * @hide */ - public List listConnectedSinks() { - if (DBG) log("listConnectedSinks()"); + public Set getConnectedSinks() { + if (DBG) log("getConnectedSinks()"); try { - return mService.listConnectedSinks(); + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(mService.getConnectedSinks()))); } catch (RemoteException e) { Log.w(TAG, "", e); return null; @@ -156,14 +162,14 @@ public class BluetoothA2dp { } /** Get the state of an A2DP sink - * @param address Remote BT address. + * @param device Remote BT device. * @return State code, or negative on error * @hide */ - public int getSinkState(String address) { - if (DBG) log("getSinkState(" + address + ")"); + public int getSinkState(BluetoothDevice device) { + if (DBG) log("getSinkState(" + device + ")"); try { - return mService.getSinkState(address); + return mService.getSinkState(device); } catch (RemoteException e) { Log.w(TAG, "", e); return BluetoothError.ERROR_IPC; @@ -177,15 +183,15 @@ public class BluetoothA2dp { * Sinks with priority greater than zero will accept incoming connections * (if no sink is currently connected). * Priority for unpaired sink must be PRIORITY_NONE. - * @param address Paired sink + * @param device Paired sink * @param priority Integer priority, for example PRIORITY_AUTO or * PRIORITY_NONE * @return Result code, negative indicates an error */ - public int setSinkPriority(String address, int priority) { - if (DBG) log("setSinkPriority(" + address + ", " + priority + ")"); + public int setSinkPriority(BluetoothDevice device, int priority) { + if (DBG) log("setSinkPriority(" + device + ", " + priority + ")"); try { - return mService.setSinkPriority(address, priority); + return mService.setSinkPriority(device, priority); } catch (RemoteException e) { Log.w(TAG, "", e); return BluetoothError.ERROR_IPC; @@ -194,13 +200,13 @@ public class BluetoothA2dp { /** * Get priority of a2dp sink. - * @param address Sink + * @param device Sink * @return non-negative priority, or negative error code on error. */ - public int getSinkPriority(String address) { - if (DBG) log("getSinkPriority(" + address + ")"); + public int getSinkPriority(BluetoothDevice device) { + if (DBG) log("getSinkPriority(" + device + ")"); try { - return mService.getSinkPriority(address); + return mService.getSinkPriority(device); } catch (RemoteException e) { Log.w(TAG, "", e); return BluetoothError.ERROR_IPC; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java new file mode 100644 index 00000000000..d2075407a4a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2009 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.os.RemoteException; +import android.util.Log; + +import java.io.IOException; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; + +/** + * Represents the local Bluetooth adapter. + * + * @hide + */ +public final class BluetoothAdapter { + private static final String TAG = "BluetoothAdapter"; + + public static final int BLUETOOTH_STATE_OFF = 0; + public static final int BLUETOOTH_STATE_TURNING_ON = 1; + public static final int BLUETOOTH_STATE_ON = 2; + public static final int BLUETOOTH_STATE_TURNING_OFF = 3; + + /** Inquiry scan and page scan are both off. + * Device is neither discoverable nor connectable */ + public static final int SCAN_MODE_NONE = 0; + /** Page scan is on, inquiry scan is off. + * Device is connectable, but not discoverable */ + public static final int SCAN_MODE_CONNECTABLE = 1; + /** Page scan and inquiry scan are on. + * Device is connectable and discoverable */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; + + public static final int RESULT_FAILURE = -1; + public static final int RESULT_SUCCESS = 0; + + /* The user will be prompted to enter a pin */ + public static final int PAIRING_VARIANT_PIN = 0; + /* The user will be prompted to enter a passkey */ + public static final int PAIRING_VARIANT_PASSKEY = 1; + /* The user will be prompted to confirm the passkey displayed on the screen */ + public static final int PAIRING_VARIANT_CONFIRMATION = 2; + + private final IBluetooth mService; + + /** + * Do not use this constructor. Use Context.getSystemService() instead. + * @hide + */ + public BluetoothAdapter(IBluetooth service) { + if (service == null) { + throw new IllegalArgumentException("service is null"); + } + mService = service; + } + + /** + * Get the remote BluetoothDevice associated with the given MAC address. + * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB". + * @param address valid Bluetooth MAC address + */ + public BluetoothDevice getRemoteDevice(String address) { + return new BluetoothDevice(address); + } + + /** + * Is Bluetooth currently turned on. + * + * @return true if Bluetooth enabled, false otherwise. + */ + public boolean isEnabled() { + try { + return mService.isEnabled(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the current state of Bluetooth. + * + * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. + */ + public int getBluetoothState() { + try { + return mService.getBluetoothState(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR; + } + + /** + * Enable the Bluetooth device. + * Turn on the underlying hardware. + * This is an asynchronous call, + * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if + * and when the device is sucessfully enabled. + * @return false if we cannot enable the Bluetooth device. True does not + * imply the device was enabled, it only implies that so far there were no + * problems. + */ + public boolean enable() { + try { + return mService.enable(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Disable the Bluetooth device. + * This turns off the underlying hardware. + * + * @return true if successful, false otherwise. + */ + public boolean disable() { + try { + return mService.disable(true); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public String getAddress() { + try { + return mService.getAddress(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. Currently it is only + * possible to retrieve the Bluetooth name when Bluetooth is enabled. + * + * @return the Bluetooth name, or null if there was a problem. + */ + public String getName() { + try { + return mService.getName(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Set the friendly Bluetooth name of this device. + * + * This name is visible to remote Bluetooth devices. The Bluetooth Service + * is responsible for persisting this name. + * + * @param name the name to set + * @return true, if the name was successfully set. False otherwise. + */ + public boolean setName(String name) { + try { + return mService.setName(name); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the current scan mode. + * Used to determine if the local device is connectable and/or discoverable + * @return Scan mode, one of SCAN_MODE_* or an error code + */ + public int getScanMode() { + try { + return mService.getScanMode(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothError.ERROR_IPC; + } + + /** + * Set the current scan mode. + * Used to make the local device connectable and/or discoverable + * @param scanMode One of SCAN_MODE_* + */ + public void setScanMode(int scanMode) { + try { + mService.setScanMode(scanMode); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public int getDiscoverableTimeout() { + try { + return mService.getDiscoverableTimeout(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return -1; + } + + public void setDiscoverableTimeout(int timeout) { + try { + mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean startDiscovery() { + try { + return mService.startDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + public void cancelDiscovery() { + try { + mService.cancelDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + public boolean isDiscovering() { + try { + return mService.isDiscovering(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * List remote devices that are bonded (paired) to the local adapter. + * + * Bonding (pairing) is the process by which the user enters a pin code for + * the device, which generates a shared link key, allowing for + * authentication and encryption of future connections. In Android we + * require bonding before RFCOMM or SCO connections can be made to a remote + * device. + * + * This function lists which remote devices we have a link key for. It does + * not cause any RF transmission, and does not check if the remote device + * still has it's link key with us. If the other side no longer has its + * link key then the RFCOMM or SCO connection attempt will result in an + * error. + * + * This function does not check if the remote device is in range. + * + * Remote devices that have an in-progress bonding attempt are not + * returned. + * + * @return unmodifiable set of bonded devices, or null on error + */ + public Set getBondedDevices() { + try { + return toDeviceSet(mService.listBonds()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Construct a listening, secure RFCOMM server socket. + * The remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, port); + try { + socket.mSocket.bindListenNative(); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct an unencrypted, unauthenticated, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, false, false, port); + try { + socket.mSocket.bindListenNative(); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + /** + * Construct a SCO server socket. + * Call #accept to retrieve connections to this socket. + * @return A SCO BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public static BluetoothServerSocket listenUsingScoOn() throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_SCO, false, false, -1); + try { + socket.mSocket.bindListenNative(); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { } + throw e; + } + return socket; + } + + private Set toDeviceSet(String[] addresses) { + Set devices = new HashSet(addresses.length); + for (int i = 0; i < addresses.length; i++) { + devices.add(getRemoteDevice(addresses[i])); + } + return Collections.unmodifiableSet(devices); + } +} diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java index f3afd2a4a0b..abd7723c57c 100644 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -9,24 +9,22 @@ import android.util.Log; /** * Listen's for incoming RFCOMM connection for the headset / handsfree service. * - * This class is planned for deletion, in favor of a generic Rfcomm class. + * TODO: Use the new generic BluetoothSocket class instead of this legacy code * * @hide */ -public class BluetoothAudioGateway { +public final class BluetoothAudioGateway { private static final String TAG = "BT Audio Gateway"; private static final boolean DBG = false; private int mNativeData; static { classInitNative(); } - private BluetoothDevice mBluetooth; - /* in */ private int mHandsfreeAgRfcommChannel = -1; private int mHeadsetAgRfcommChannel = -1; - /* out */ + /* out - written by native code */ private String mConnectingHeadsetAddress; private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ private int mConnectingHeadsetSocketFd; @@ -35,17 +33,18 @@ public class BluetoothAudioGateway { private int mConnectingHandsfreeSocketFd; private int mTimeoutRemainingMs; /* in/out */ + private final BluetoothAdapter mAdapter; + public static final int DEFAULT_HF_AG_CHANNEL = 10; public static final int DEFAULT_HS_AG_CHANNEL = 11; - public BluetoothAudioGateway(BluetoothDevice bluetooth) { - this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); + public BluetoothAudioGateway(BluetoothAdapter adapter) { + this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); } - public BluetoothAudioGateway(BluetoothDevice bluetooth, - int handsfreeAgRfcommChannel, - int headsetAgRfcommChannel) { - mBluetooth = bluetooth; + public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel, + int headsetAgRfcommChannel) { + mAdapter = adapter; mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; mHeadsetAgRfcommChannel = headsetAgRfcommChannel; initializeNativeDataNative(); @@ -58,18 +57,17 @@ public class BluetoothAudioGateway { private Handler mCallback; public class IncomingConnectionInfo { - IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChan) { - mBluetooth = bluetooth; - mAddress = address; + public BluetoothAdapter mAdapter; + public BluetoothDevice mRemoteDevice; + public int mSocketFd; + public int mRfcommChan; + IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice, + int socketFd, int rfcommChan) { + mAdapter = adapter; + mRemoteDevice = remoteDevice; mSocketFd = socketFd; mRfcommChan = rfcommChan; } - - public BluetoothDevice mBluetooth; - public String mAddress; - public int mSocketFd; - public int mRfcommChan; } public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; @@ -111,12 +109,11 @@ public class BluetoothAudioGateway { mConnectingHeadsetRfcommChannel); Message msg = Message.obtain(mCallback); msg.what = MSG_INCOMING_HEADSET_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHeadsetAddress, - mConnectingHeadsetSocketFd, - mConnectingHeadsetRfcommChannel); + msg.obj = new IncomingConnectionInfo( + mAdapter, + mAdapter.getRemoteDevice(mConnectingHeadsetAddress), + mConnectingHeadsetSocketFd, + mConnectingHeadsetRfcommChannel); msg.sendToTarget(); } if (mConnectingHandsfreeRfcommChannel >= 0) { @@ -126,12 +123,11 @@ public class BluetoothAudioGateway { Message msg = Message.obtain(); msg.setTarget(mCallback); msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHandsfreeAddress, - mConnectingHandsfreeSocketFd, - mConnectingHandsfreeRfcommChannel); + msg.obj = new IncomingConnectionInfo( + mAdapter, + mAdapter.getRemoteDevice(mConnectingHandsfreeAddress), + mConnectingHandsfreeSocketFd, + mConnectingHandsfreeRfcommChannel); msg.sendToTarget(); } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.aidl b/framework/java/android/bluetooth/BluetoothDevice.aidl new file mode 100644 index 00000000000..daae74d52c4 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009 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; + +parcelable BluetoothDevice; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a64c6d72d43..27b284913c6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 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. @@ -16,39 +16,25 @@ package android.bluetooth; +import android.content.Context; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; +import java.io.IOException; import java.io.UnsupportedEncodingException; /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. + * Represents a remote Bluetooth device. * + * TODO: unhide * @hide */ -public class BluetoothDevice { - - public static final int BLUETOOTH_STATE_OFF = 0; - public static final int BLUETOOTH_STATE_TURNING_ON = 1; - public static final int BLUETOOTH_STATE_ON = 2; - public static final int BLUETOOTH_STATE_TURNING_OFF = 3; - - /** Inquiry scan and page scan are both off. - * Device is neither discoverable nor connectable */ - public static final int SCAN_MODE_NONE = 0; - /** Page scan is on, inquiry scan is off. - * Device is connectable, but not discoverable */ - public static final int SCAN_MODE_CONNECTABLE = 1; - /** Page scan and inquiry scan are on. - * Device is connectable and discoverable */ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; - - public static final int RESULT_FAILURE = -1; - public static final int RESULT_SUCCESS = 0; +public final class BluetoothDevice implements Parcelable { + private static final String TAG = "BluetoothDevice"; /** We do not have a link key for the remote device, and are therefore not * bonded */ @@ -81,84 +67,81 @@ public class BluetoothDevice { /* The user will be prompted to confirm the passkey displayed on the screen */ public static final int PAIRING_VARIANT_CONFIRMATION = 2; + private static final int ADDRESS_LENGTH = 17; - private static final String TAG = "BluetoothDevice"; + private static IBluetooth sService; /* Guarenteed constant after first object constructed */ + + private final String mAddress; - private final IBluetoothDevice mService; /** - * @hide - hide this because it takes a parameter of type - * IBluetoothDevice, which is a System private class. - * Also note that Context.getSystemService is a factory that - * returns a BlueToothDevice. That is the right way to get - * a BluetoothDevice. + * Create a new BluetoothDevice + * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", + * and is validated in this constructor. + * @param address valid Bluetooth MAC address + * @throws RuntimeException Bluetooth is not available on this platform + * @throws IllegalArgumentException address is invalid + * @hide */ - public BluetoothDevice(IBluetoothDevice service) { - mService = service; + /*package*/ BluetoothDevice(String address) { + synchronized (BluetoothDevice.class) { + if (sService == null) { + IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); + if (b == null) { + throw new RuntimeException("Bluetooth service not available"); + } + sService = IBluetooth.Stub.asInterface(b); + } + } + + if (!checkBluetoothAddress(address)) { + throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); + } + + mAddress = address; } - /** - * Is Bluetooth currently turned on. - * - * @return true if Bluetooth enabled, false otherwise. - */ - public boolean isEnabled() { - try { - return mService.isEnabled(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothDevice) { + return mAddress.equals(((BluetoothDevice)o).getAddress()); + } return false; } - /** - * Get the current state of Bluetooth. - * - * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. - */ - public int getBluetoothState() { - try { - return mService.getBluetoothState(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR; + @Override + public int hashCode() { + return mAddress.hashCode(); } - /** - * Enable the Bluetooth device. - * Turn on the underlying hardware. - * This is an asynchronous call, - * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if - * and when the device is sucessfully enabled. - * @return false if we cannot enable the Bluetooth device. True does not - * imply the device was enabled, it only implies that so far there were no - * problems. - */ - public boolean enable() { - try { - return mService.enable(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + @Override + public String toString() { + return mAddress; } - /** - * Disable the Bluetooth device. - * This turns off the underlying hardware. - * - * @return true if successful, false otherwise. - */ - public boolean disable() { - try { - return mService.disable(true); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothDevice createFromParcel(Parcel in) { + return new BluetoothDevice(in.readString()); + } + public BluetoothDevice[] newArray(int size) { + return new BluetoothDevice[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeString(mAddress); } public String getAddress() { - try { - return mService.getAddress(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return mAddress; } /** - * Get the friendly Bluetooth name of this device. + * Get the friendly Bluetooth name of this remote device. * * This name is visible to remote Bluetooth devices. Currently it is only * possible to retrieve the Bluetooth name when Bluetooth is enabled. @@ -167,97 +150,11 @@ public class BluetoothDevice { */ public String getName() { try { - return mService.getName(); + return sService.getRemoteName(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - /** - * Set the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. The Bluetooth Service - * is responsible for persisting this name. - * - * @param name the name to set - * @return true, if the name was successfully set. False otherwise. - */ - public boolean setName(String name) { - try { - return mService.setName(name); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Get the current scan mode. - * Used to determine if the local device is connectable and/or discoverable - * @return Scan mode, one of SCAN_MODE_* or an error code - */ - public int getScanMode() { - try { - return mService.getScanMode(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; - } - - /** - * Set the current scan mode. - * Used to make the local device connectable and/or discoverable - * @param scanMode One of SCAN_MODE_* - */ - public void setScanMode(int scanMode) { - try { - mService.setScanMode(scanMode); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public int getDiscoverableTimeout() { - try { - return mService.getDiscoverableTimeout(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return -1; - } - public void setDiscoverableTimeout(int timeout) { - try { - mService.setDiscoverableTimeout(timeout); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public boolean startDiscovery() { - try { - return mService.startDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - public void cancelDiscovery() { - try { - mService.cancelDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - - public boolean isDiscovering() { - try { - return mService.isDiscovering(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - - /** - * Removes the remote device and the pairing information associated - * with it. - * - * @param address the Bluetooth hardware address you want to disconnect. - * @return true if the device was disconnected, false otherwise and on - * error. - */ - public boolean removeBond(String address) { - try { - return mService.removeBond(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - /** * Create a bonding with a remote bluetooth device. * @@ -268,9 +165,9 @@ public class BluetoothDevice { * @return false If there was an immediate problem creating the bonding, * true otherwise. */ - public boolean createBond(String address) { + public boolean createBond() { try { - return mService.createBond(address); + return sService.createBond(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -278,41 +175,25 @@ public class BluetoothDevice { /** * Cancel an in-progress bonding request started with createBond. */ - public boolean cancelBondProcess(String address) { + public boolean cancelBondProcess() { try { - return mService.cancelBondProcess(address); + return sService.cancelBondProcess(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** - * List remote devices that are bonded (paired) to the local device. - * - * Bonding (pairing) is the process by which the user enters a pin code for - * the device, which generates a shared link key, allowing for - * authentication and encryption of future connections. In Android we - * require bonding before RFCOMM or SCO connections can be made to a remote - * device. - * - * This function lists which remote devices we have a link key for. It does - * not cause any RF transmission, and does not check if the remote device - * still has it's link key with us. If the other side no longer has its - * link key then the RFCOMM or SCO connection attempt will result in an - * error. - * - * This function does not check if the remote device is in range. - * - * Remote devices that have an in-progress bonding attempt are not - * returned. + * Removes the remote device and the pairing information associated + * with it. * - * @return bluetooth hardware addresses of remote devices that are - * bonded. Array size is 0 if no devices are bonded. Null on error. + * @return true if the device was disconnected, false otherwise and on + * error. */ - public String[] listBonds() { + public boolean removeBond() { try { - return mService.listBonds(); + return sService.removeBond(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return false; } /** @@ -325,69 +206,102 @@ public class BluetoothDevice { * @param address Bluetooth hardware address of the remote device to check. * @return Result code */ - public int getBondState(String address) { + public int getBondState() { try { - return mService.getBondState(address); + return sService.getBondState(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothError.ERROR_IPC; } - public String getRemoteName(String address) { + public int getBluetoothClass() { try { - return mService.getRemoteName(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - - public int getRemoteClass(String address) { - try { - return mService.getRemoteClass(address); + return sService.getRemoteClass(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothError.ERROR_IPC; } - public String[] getRemoteUuids(String address) { + public String[] getUuids() { try { - return mService.getRemoteUuids(address); + return sService.getRemoteUuids(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public int getRemoteServiceChannel(String address, String uuid) { + public int getServiceChannel(String uuid) { try { - return mService.getRemoteServiceChannel(address, uuid); + return sService.getRemoteServiceChannel(mAddress, uuid); } catch (RemoteException e) {Log.e(TAG, "", e);} return BluetoothError.ERROR_IPC; } - public boolean setPin(String address, byte[] pin) { + public boolean setPin(byte[] pin) { try { - return mService.setPin(address, pin); + return sService.setPin(mAddress, pin); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean setPasskey(String address, int passkey) { + public boolean setPasskey(int passkey) { try { - return mService.setPasskey(address, passkey); + return sService.setPasskey(mAddress, passkey); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean setPairingConfirmation(String address, boolean confirm) { + public boolean setPairingConfirmation(boolean confirm) { try { - return mService.setPairingConfirmation(address, confirm); + return sService.setPairingConfirmation(mAddress, confirm); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } - public boolean cancelPairingUserInput(String address) { + public boolean cancelPairingUserInput() { try { - return mService.cancelPairingUserInput(address); + return sService.cancelPairingUserInput(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } + /** + * Construct a secure RFCOMM socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will be authenticated and communication on this socket + * will be encrypted. + * @param port remote port + * @return an RFCOMM BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + */ + public BluetoothSocket createRfcommSocket(int port) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, port); + } + + /** + * Construct an insecure RFCOMM socket ready to start an outgoing + * connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will not be authenticated and communication on this + * socket will not be encrypted. + * @param port remote port + * @return An RFCOMM BluetoothSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + */ + public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); + } + + /** + * Construct a SCO socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * @return a SCO BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + */ + public BluetoothSocket createScoSocket() throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); + } + /** * Check that a pin is valid and convert to byte array. * @@ -413,7 +327,6 @@ public class BluetoothDevice { return pinBytes; } - private static final int ADDRESS_LENGTH = 17; /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index fe1e09af984..0e3d2bbb192 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -49,7 +49,7 @@ import android.util.Log; * * @hide */ -public class BluetoothHeadset { +public final class BluetoothHeadset { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = false; @@ -163,16 +163,16 @@ public class BluetoothHeadset { } /** - * Get the Bluetooth address of the current headset. - * @return The Bluetooth address, or null if not in connected or connecting + * Get the BluetoothDevice for the current headset. + * @return current headset, or null if not in connected or connecting * state, or if this proxy object is not connected to the Headset * service. */ - public String getHeadsetAddress() { - if (DBG) log("getHeadsetAddress()"); + public BluetoothDevice getCurrentHeadset() { + if (DBG) log("getCurrentHeadset()"); if (mService != null) { try { - return mService.getHeadsetAddress(); + return mService.getCurrentHeadset(); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -185,19 +185,19 @@ public class BluetoothHeadset { * Request to initiate a connection to a headset. * This call does not block. Fails if a headset is already connecting * or connected. - * Initiates auto-connection if address is null. Tries to connect to all + * Initiates auto-connection if device is null. Tries to connect to all * devices with priority greater than PRIORITY_AUTO in descending order. - * @param address The Bluetooth Address to connect to, or null to - * auto-connect to the last connected headset. - * @return False if there was a problem initiating the connection - * procedure, and no further HEADSET_STATE_CHANGED intents - * will be expected. + * @param device device to connect to, or null to auto-connect last connected + * headset + * @return false if there was a problem initiating the connection + * procedure, and no further HEADSET_STATE_CHANGED intents + * will be expected. */ - public boolean connectHeadset(String address) { - if (DBG) log("connectHeadset(" + address + ")"); + public boolean connectHeadset(BluetoothDevice device) { + if (DBG) log("connectHeadset(" + device + ")"); if (mService != null) { try { - if (mService.connectHeadset(address)) { + if (mService.connectHeadset(device)) { return true; } } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -213,11 +213,11 @@ public class BluetoothHeadset { * connecting). Returns false if not connected, or if this proxy object * if not currently connected to the headset service. */ - public boolean isConnected(String address) { - if (DBG) log("isConnected(" + address + ")"); + public boolean isConnected(BluetoothDevice device) { + if (DBG) log("isConnected(" + device + ")"); if (mService != null) { try { - return mService.isConnected(address); + return mService.isConnected(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -295,16 +295,16 @@ public class BluetoothHeadset { * auto-connected. * Incoming connections are ignored regardless of priority if there is * already a headset connected. - * @param address Paired headset + * @param device paired headset * @param priority Integer priority, for example PRIORITY_AUTO or * PRIORITY_NONE - * @return True if successful, false if there was some error. + * @return true if successful, false if there was some error */ - public boolean setPriority(String address, int priority) { - if (DBG) log("setPriority(" + address + ", " + priority + ")"); + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null) { try { - return mService.setPriority(address, priority); + return mService.setPriority(device, priority); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -315,14 +315,14 @@ public class BluetoothHeadset { /** * Get priority of headset. - * @param address Headset - * @return non-negative priority, or negative error code on error. + * @param device headset + * @return non-negative priority, or negative error code on error */ - public int getPriority(String address) { - if (DBG) log("getPriority(" + address + ")"); + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); if (mService != null) { try { - return mService.getPriority(address); + return mService.getPriority(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index d6c79b40490..2a0de61f8c3 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -31,8 +31,8 @@ import android.annotation.SdkConstant.SdkConstantType; public interface BluetoothIntent { public static final String SCAN_MODE = "android.bluetooth.intent.SCAN_MODE"; - public static final String ADDRESS = - "android.bluetooth.intent.ADDRESS"; + public static final String DEVICE = + "android.bluetooth.intent.DEVICE"; public static final String NAME = "android.bluetooth.intent.NAME"; public static final String ALIAS = diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 57826444665..645e24145c5 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -73,11 +73,11 @@ public class BluetoothPbap { /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; - /** No Pce currently connected */ + /** No client currently connected */ public static final int STATE_DISCONNECTED = 0; /** Connection attempt in progress */ public static final int STATE_CONNECTING = 1; - /** A Pce is currently connected */ + /** Client is currently connected */ public static final int STATE_CONNECTED = 2; public static final int RESULT_FAILURE = 0; @@ -159,16 +159,16 @@ public class BluetoothPbap { } /** - * Get the Bluetooth address of the current Pce. - * @return The Bluetooth address, or null if not in connected or connecting - * state, or if this proxy object is not connected to the Pbap - * service. + * Get the currently connected remote Bluetooth device (PCE). + * @return The remote Bluetooth device, or null if not in connected or + * connecting state, or if this proxy object is not connected to + * the Pbap service. */ - public String getPceAddress() { - if (DBG) log("getPceAddress()"); + public BluetoothDevice getClient() { + if (DBG) log("getClient()"); if (mService != null) { try { - return mService.getPceAddress(); + return mService.getClient(); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -178,15 +178,15 @@ public class BluetoothPbap { } /** - * Returns true if the specified Pcs is connected (does not include - * connecting). Returns false if not connected, or if this proxy object - * if not currently connected to the Pbap service. + * Returns true if the specified Bluetooth device is connected (does not + * include connecting). Returns false if not connected, or if this proxy + * object is not currently connected to the Pbap service. */ - public boolean isConnected(String address) { - if (DBG) log("isConnected(" + address + ")"); + public boolean isConnected(BluetoothDevice device) { + if (DBG) log("isConnected(" + device + ")"); if (mService != null) { try { - return mService.isConnected(address); + return mService.isConnected(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -196,15 +196,15 @@ public class BluetoothPbap { } /** - * Disconnects the current Pce. Currently this call blocks, it may soon - * be made asynchornous. Returns false if this proxy object is + * Disconnects the current Pbap client (PCE). Currently this call blocks, + * it may soon be made asynchornous. Returns false if this proxy object is * not currently connected to the Pbap service. */ - public boolean disconnectPce() { - if (DBG) log("disconnectPce()"); + public boolean disconnect() { + if (DBG) log("disconnect()"); if (mService != null) { try { - mService.disconnectPce(); + mService.disconnect(); return true; } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index f3baeab18de..8be300b0418 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -33,72 +33,7 @@ import java.io.IOException; * @hide */ public final class BluetoothServerSocket implements Closeable { - private final BluetoothSocket mSocket; - - /** - * Construct a listening, secure RFCOMM server socket. - * The remote device connecting to this socket will be authenticated and - * communication on this socket will be encrypted. - * Call #accept to retrieve connections to this socket. - * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, port); - try { - socket.mSocket.bindListenNative(); - } catch (IOException e) { - try { - socket.close(); - } catch (IOException e2) { } - throw e; - } - return socket; - } - - /** - * Construct an unencrypted, unauthenticated, RFCOMM server socket. - * Call #accept to retrieve connections to this socket. - * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, false, false, port); - try { - socket.mSocket.bindListenNative(); - } catch (IOException e) { - try { - socket.close(); - } catch (IOException e2) { } - throw e; - } - return socket; - } - - /** - * Construct a SCO server socket. - * Call #accept to retrieve connections to this socket. - * @return A SCO BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothServerSocket listenUsingScoOn() throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_SCO, false, false, -1); - try { - socket.mSocket.bindListenNative(); - } catch (IOException e) { - try { - socket.close(); - } catch (IOException e2) { } - throw e; - } - return socket; - } + /*package*/ final BluetoothSocket mSocket; /** * Construct a socket for incoming connections. @@ -109,7 +44,7 @@ public final class BluetoothServerSocket implements Closeable { * @throws IOException On error, for example Bluetooth not available, or * insufficient priveleges */ - private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) + /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index de1f32601db..dda2cef99ed 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -42,6 +42,7 @@ public final class BluetoothSocket implements Closeable { private final int mType; /* one of TYPE_RFCOMM etc */ private final int mPort; /* RFCOMM channel or L2CAP psm */ + private final BluetoothDevice mDevice; /* remote device */ private final String mAddress; /* remote address */ private final boolean mAuth; private final boolean mEncrypt; @@ -51,68 +52,27 @@ public final class BluetoothSocket implements Closeable { private int mSocketData; /* used by native code only */ /** - * Construct a secure RFCOMM socket ready to start an outgoing connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * The remote device will be authenticated and communication on this socket - * will be encrypted. - * @param address remote Bluetooth address that this socket can connect to - * @param port remote port - * @return an RFCOMM BluetoothSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothSocket createRfcommSocket(String address, int port) - throws IOException { - return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port); - } - - /** - * Construct an insecure RFCOMM socket ready to start an outgoing - * connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * The remote device will not be authenticated and communication on this - * socket will not be encrypted. - * @param address remote Bluetooth address that this socket can connect to - * @param port remote port - * @return An RFCOMM BluetoothSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothSocket createInsecureRfcommSocket(String address, int port) - throws IOException { - return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port); - } - - /** - * Construct a SCO socket ready to start an outgoing connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * @param address remote Bluetooth address that this socket can connect to - * @return a SCO BluetoothSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions. - */ - public static BluetoothSocket createScoSocket(String address, int port) - throws IOException { - return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port); - } - - /** - * Construct a Bluetooth. + * Construct a BluetoothSocket. * @param type type of socket * @param fd fd to use for connected socket, or -1 for a new socket * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param address remote Bluetooth address that this socket can connect to + * @param device remote device that this socket can connect to * @param port remote port * @throws IOException On error, for example Bluetooth not available, or * insufficient priveleges */ - /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, - int port) throws IOException { + /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, + BluetoothDevice device, int port) throws IOException { mType = type; mAuth = auth; mEncrypt = encrypt; - mAddress = address; + mDevice = device; + if (device == null) { + mAddress = null; + } else { + mAddress = device.getAddress(); + } mPort = port; if (fd == -1) { initSocketNative(); @@ -123,6 +83,22 @@ public final class BluetoothSocket implements Closeable { mOutputStream = new BluetoothOutputStream(this); } + /** + * Construct a BluetoothSocket from address. + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param address remote device that this socket can connect to + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, + int port) throws IOException { + this(type, fd, auth, encrypt, new BluetoothDevice(address), port); + } + @Override protected void finalize() throws Throwable { try { @@ -154,12 +130,12 @@ public final class BluetoothSocket implements Closeable { } /** - * Return the address we are connecting, or connected, to. - * @return Bluetooth address, or null if this socket has not yet attempted + * Return the remote device we are connecting, or connected, to. + * @return remote device, or null if this socket has not yet attempted * or established a connection. */ - public String getAddress() { - return mAddress; + public BluetoothDevice getRemoteDevice() { + return mDevice; } /** diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index f987ffdd440..29cf41da931 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -31,7 +31,7 @@ import android.util.Log; * * @hide */ -public class HeadsetBase { +public final class HeadsetBase { private static final String TAG = "Bluetooth HeadsetBase"; private static final boolean DBG = false; @@ -42,8 +42,9 @@ public class HeadsetBase { private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ - private final BluetoothDevice mBluetooth; - private final String mAddress; + private final BluetoothAdapter mAdapter; + private final BluetoothDevice mRemoteDevice; + private final String mAddress; // for native code private final int mRfcommChannel; private int mNativeData; private Thread mEventThread; @@ -73,12 +74,13 @@ public class HeadsetBase { private native void cleanupNativeDataNative(); - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, - int rfcommChannel) { + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, + int rfcommChannel) { mDirection = DIRECTION_OUTGOING; mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; + mAdapter = adapter; + mRemoteDevice = device; + mAddress = device.getAddress(); mRfcommChannel = rfcommChannel; mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); mWakeLock.setReferenceCounted(false); @@ -88,12 +90,13 @@ public class HeadsetBase { } /* Create from an already exisiting rfcomm connection */ - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, + int socketFd, int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; + mAdapter = adapter; + mRemoteDevice = device; + mAddress = device.getAddress(); mRfcommChannel = rfcommChannel; mEventThreadHandler = handler; mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); @@ -252,12 +255,8 @@ public class HeadsetBase { return mEventThread != null; } - public String getAddress() { - return mAddress; - } - - public String getName() { - return mBluetooth.getRemoteName(mAddress); + public BluetoothDevice getRemoteDevice() { + return mRemoteDevice; } public int getDirection() { diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetooth.aidl similarity index 98% rename from framework/java/android/bluetooth/IBluetoothDevice.aidl rename to framework/java/android/bluetooth/IBluetooth.aidl index a78752bff5e..9e05a871d41 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -21,7 +21,7 @@ package android.bluetooth; * * {@hide} */ -interface IBluetoothDevice +interface IBluetooth { boolean isEnabled(); int getBluetoothState(); diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 55ff27f97ec..e6c6be2594f 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -16,16 +16,18 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** * System private API for Bluetooth A2DP service * * {@hide} */ interface IBluetoothA2dp { - int connectSink(in String address); - int disconnectSink(in String address); - List listConnectedSinks(); - int getSinkState(in String address); - int setSinkPriority(in String address, int priority); - int getSinkPriority(in String address); + int connectSink(in BluetoothDevice device); + int disconnectSink(in BluetoothDevice device); + BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports + int getSinkState(in BluetoothDevice device); + int setSinkPriority(in BluetoothDevice device, int priority); + int getSinkPriority(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 5f42fd6521b..6cccd506e26 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** * System private API for Bluetooth Headset service * @@ -23,13 +25,13 @@ package android.bluetooth; */ interface IBluetoothHeadset { int getState(); - String getHeadsetAddress(); - boolean connectHeadset(in String address); + BluetoothDevice getCurrentHeadset(); + boolean connectHeadset(in BluetoothDevice device); void disconnectHeadset(); - boolean isConnected(in String address); + boolean isConnected(in BluetoothDevice device); boolean startVoiceRecognition(); boolean stopVoiceRecognition(); - boolean setPriority(in String address, int priority); - int getPriority(in String address); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); int getBatteryUsageHint(); } diff --git a/framework/java/android/bluetooth/IBluetoothPbap.aidl b/framework/java/android/bluetooth/IBluetoothPbap.aidl index 06cdb7b972d..7cc77d110e9 100644 --- a/framework/java/android/bluetooth/IBluetoothPbap.aidl +++ b/framework/java/android/bluetooth/IBluetoothPbap.aidl @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** * System private API for Bluetooth pbap service * @@ -23,8 +25,8 @@ package android.bluetooth; */ interface IBluetoothPbap { int getState(); - String getPceAddress(); - boolean connectPce(in String address); - void disconnectPce(); - boolean isConnected(in String address); + BluetoothDevice getClient(); + boolean connect(in BluetoothDevice device); + void disconnect(); + boolean isConnected(in BluetoothDevice device); } -- GitLab From 753da539da89aac6762ef2183680205f8fd4e95a Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 19 Aug 2009 11:00:00 -0700 Subject: [PATCH 0042/1408] API CHANGE Javadoc, and unhide the first pieces of the Bluetooth API. With this commit there is enough public API to connect and use an RFCOMM connection between Bluetooth devices. --- .../android/bluetooth/BluetoothAdapter.java | 79 ++++++++++--- .../android/bluetooth/BluetoothDevice.java | 111 +++++++++++++----- .../bluetooth/BluetoothServerSocket.java | 57 +++++---- .../android/bluetooth/BluetoothSocket.java | 54 +++++---- 4 files changed, 217 insertions(+), 84 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d2075407a4a..6bd2a5abc3a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -27,34 +27,54 @@ import java.util.HashSet; /** * Represents the local Bluetooth adapter. * - * @hide + *

Use {@link android.content.Context#getSystemService} with {@link + * android.content.Context#BLUETOOTH_SERVICE} to get the default local + * Bluetooth adapter. On most Android devices there is only one local + * Bluetotoh adapter. + * + *

Use the {@link BluetoothDevice} class for operations on remote Bluetooth + * devices. + * + *

TODO: unhide more of this class */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + /** @hide */ public static final int BLUETOOTH_STATE_OFF = 0; + /** @hide */ public static final int BLUETOOTH_STATE_TURNING_ON = 1; + /** @hide */ public static final int BLUETOOTH_STATE_ON = 2; + /** @hide */ public static final int BLUETOOTH_STATE_TURNING_OFF = 3; /** Inquiry scan and page scan are both off. - * Device is neither discoverable nor connectable */ + * Device is neither discoverable nor connectable + * @hide */ public static final int SCAN_MODE_NONE = 0; /** Page scan is on, inquiry scan is off. - * Device is connectable, but not discoverable */ + * Device is connectable, but not discoverable + * @hide*/ public static final int SCAN_MODE_CONNECTABLE = 1; /** Page scan and inquiry scan are on. - * Device is connectable and discoverable */ + * Device is connectable and discoverable + * @hide*/ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; + /** @hide */ public static final int RESULT_FAILURE = -1; + /** @hide */ public static final int RESULT_SUCCESS = 0; - /* The user will be prompted to enter a pin */ + /** The user will be prompted to enter a pin + * @hide */ public static final int PAIRING_VARIANT_PIN = 0; - /* The user will be prompted to enter a passkey */ + /** The user will be prompted to enter a passkey + * @hide */ public static final int PAIRING_VARIANT_PASSKEY = 1; - /* The user will be prompted to confirm the passkey displayed on the screen */ + /** The user will be prompted to confirm the passkey displayed on the screen + * @hide */ public static final int PAIRING_VARIANT_CONFIRMATION = 2; private final IBluetooth mService; @@ -71,9 +91,14 @@ public final class BluetoothAdapter { } /** - * Get the remote BluetoothDevice associated with the given MAC address. - * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB". + * Get a {@link BluetoothDevice} object for the given Bluetooth hardware + * address. + *

Valid Bluetooth hardware addresses must be upper case, in a format + * such as "00:11:22:33:AA:BB". + *

A {@link BluetoothDevice} will always be returned for a valid + * hardware address, even if this adapter has never seen that device. * @param address valid Bluetooth MAC address + * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(String address) { return new BluetoothDevice(address); @@ -83,6 +108,7 @@ public final class BluetoothAdapter { * Is Bluetooth currently turned on. * * @return true if Bluetooth enabled, false otherwise. + * @hide */ public boolean isEnabled() { try { @@ -95,6 +121,7 @@ public final class BluetoothAdapter { * Get the current state of Bluetooth. * * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. + * @hide */ public int getBluetoothState() { try { @@ -112,6 +139,7 @@ public final class BluetoothAdapter { * @return false if we cannot enable the Bluetooth device. True does not * imply the device was enabled, it only implies that so far there were no * problems. + * @hide */ public boolean enable() { try { @@ -125,6 +153,7 @@ public final class BluetoothAdapter { * This turns off the underlying hardware. * * @return true if successful, false otherwise. + * @hide */ public boolean disable() { try { @@ -133,6 +162,7 @@ public final class BluetoothAdapter { return false; } + /** @hide */ public String getAddress() { try { return mService.getAddress(); @@ -147,6 +177,7 @@ public final class BluetoothAdapter { * possible to retrieve the Bluetooth name when Bluetooth is enabled. * * @return the Bluetooth name, or null if there was a problem. + * @hide */ public String getName() { try { @@ -163,6 +194,7 @@ public final class BluetoothAdapter { * * @param name the name to set * @return true, if the name was successfully set. False otherwise. + * @hide */ public boolean setName(String name) { try { @@ -175,6 +207,7 @@ public final class BluetoothAdapter { * Get the current scan mode. * Used to determine if the local device is connectable and/or discoverable * @return Scan mode, one of SCAN_MODE_* or an error code + * @hide */ public int getScanMode() { try { @@ -187,6 +220,7 @@ public final class BluetoothAdapter { * Set the current scan mode. * Used to make the local device connectable and/or discoverable * @param scanMode One of SCAN_MODE_* + * @hide */ public void setScanMode(int scanMode) { try { @@ -194,6 +228,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} } + /** @hide */ public int getDiscoverableTimeout() { try { return mService.getDiscoverableTimeout(); @@ -201,12 +236,14 @@ public final class BluetoothAdapter { return -1; } + /** @hide */ public void setDiscoverableTimeout(int timeout) { try { mService.setDiscoverableTimeout(timeout); } catch (RemoteException e) {Log.e(TAG, "", e);} } + /** @hide */ public boolean startDiscovery() { try { return mService.startDiscovery(); @@ -214,12 +251,14 @@ public final class BluetoothAdapter { return false; } + /** @hide */ public void cancelDiscovery() { try { mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} } + /** @hide */ public boolean isDiscovering() { try { return mService.isDiscovering(); @@ -248,6 +287,7 @@ public final class BluetoothAdapter { * returned. * * @return unmodifiable set of bonded devices, or null on error + * @hide */ public Set getBondedDevices() { try { @@ -257,17 +297,20 @@ public final class BluetoothAdapter { } /** - * Construct a listening, secure RFCOMM server socket. - * The remote device connecting to this socket will be authenticated and + * Create a listening, secure RFCOMM Bluetooth socket. + *

A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. - * Call #accept to retrieve connections to this socket. - * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections to listening {@link BluetoothServerSocket}. + *

Valid RFCOMM channels are in range 1 to 30. + * @param channel RFCOMM channel to listen on + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. */ - public BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException { + public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, port); + BluetoothSocket.TYPE_RFCOMM, true, true, channel); try { socket.mSocket.bindListenNative(); } catch (IOException e) { @@ -285,6 +328,7 @@ public final class BluetoothAdapter { * @return An RFCOMM BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. + * @hide */ public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( @@ -306,6 +350,7 @@ public final class BluetoothAdapter { * @return A SCO BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. + * @hide */ public static BluetoothServerSocket listenUsingScoOn() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 27b284913c6..1a97924e1e4 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -30,41 +30,62 @@ import java.io.UnsupportedEncodingException; /** * Represents a remote Bluetooth device. * - * TODO: unhide - * @hide + *

Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link + * BluetoothDevice}. + * + *

This class is really just a thin wrapper for a Bluetooth hardware + * address. Objects of this class are immutable. Operations on this class + * are performed on the remote Bluetooth hardware address, using the + * {@link BluetoothAdapter} that was used to create this {@link + * BluetoothDevice}. + * + * TODO: unhide more of this class */ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; /** We do not have a link key for the remote device, and are therefore not - * bonded */ + * bonded + * @hide*/ public static final int BOND_NOT_BONDED = 0; - /** We have a link key for the remote device, and are probably bonded. */ + /** We have a link key for the remote device, and are probably bonded. + * @hide */ public static final int BOND_BONDED = 1; - /** We are currently attempting bonding */ + /** We are currently attempting bonding + * @hide */ public static final int BOND_BONDING = 2; //TODO: Unify these result codes in BluetoothResult or BluetoothError /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time */ + * not respond to pin request in time + * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; /** A bond attempt failed because the other side explicilty rejected - * bonding */ + * bonding + * @hide */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we canceled the bonding process */ + /** A bond attempt failed because we canceled the bonding process + * @hide */ public static final int UNBOND_REASON_AUTH_CANCELED = 3; - /** A bond attempt failed because we could not contact the remote device */ + /** A bond attempt failed because we could not contact the remote device + * @hide */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** A bond attempt failed because a discovery is in progress */ + /** A bond attempt failed because a discovery is in progress + * @hide */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; - /** An existing bond was explicitly revoked */ + /** An existing bond was explicitly revoked + * @hide */ public static final int UNBOND_REASON_REMOVED = 6; - /* The user will be prompted to enter a pin */ + //TODO: Remove duplicates between here and BluetoothAdapter + /** The user will be prompted to enter a pin + * @hide */ public static final int PAIRING_VARIANT_PIN = 0; - /* The user will be prompted to enter a passkey */ + /** The user will be prompted to enter a passkey + * @hide */ public static final int PAIRING_VARIANT_PASSKEY = 1; - /* The user will be prompted to confirm the passkey displayed on the screen */ + /** The user will be prompted to confirm the passkey displayed on the screen + * @hide */ public static final int PAIRING_VARIANT_CONFIRMATION = 2; private static final int ADDRESS_LENGTH = 17; @@ -113,15 +134,25 @@ public final class BluetoothDevice implements Parcelable { return mAddress.hashCode(); } + /** + * Returns a string representation of this BluetoothDevice. + *

Currently this is the Bluetooth hardware address, for example + * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} + * if you explicitly require the Bluetooth hardware address in case the + * {@link #toString} representation changes in the future. + * @return string representation of this BluetoothDevice + */ @Override public String toString() { return mAddress; } + /** @hide */ public int describeContents() { return 0; } + /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothDevice createFromParcel(Parcel in) { @@ -132,21 +163,29 @@ public final class BluetoothDevice implements Parcelable { } }; + /** @hide */ public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); } + /** + * Returns the hardware address of this BluetoothDevice. + *

For example, "00:11:22:AA:BB:CC". + * @return Bluetooth hardware address as string + */ public String getAddress() { return mAddress; } /** - * Get the friendly Bluetooth name of this remote device. + * Get the friendly Bluetooth name of the remote device. * - * This name is visible to remote Bluetooth devices. Currently it is only - * possible to retrieve the Bluetooth name when Bluetooth is enabled. + *

The local adapter will automatically retrieve remote names when + * performing a device scan, and will cache them. This method just returns + * the name for this device from the cache. * * @return the Bluetooth name, or null if there was a problem. + * @hide */ public String getName() { try { @@ -164,6 +203,7 @@ public final class BluetoothDevice implements Parcelable { * @param address the remote device Bluetooth address. * @return false If there was an immediate problem creating the bonding, * true otherwise. + * @hide */ public boolean createBond() { try { @@ -174,6 +214,7 @@ public final class BluetoothDevice implements Parcelable { /** * Cancel an in-progress bonding request started with createBond. + * @hide */ public boolean cancelBondProcess() { try { @@ -188,6 +229,7 @@ public final class BluetoothDevice implements Parcelable { * * @return true if the device was disconnected, false otherwise and on * error. + * @hide */ public boolean removeBond() { try { @@ -205,6 +247,7 @@ public final class BluetoothDevice implements Parcelable { * * @param address Bluetooth hardware address of the remote device to check. * @return Result code + * @hide */ public int getBondState() { try { @@ -213,6 +256,7 @@ public final class BluetoothDevice implements Parcelable { return BluetoothError.ERROR_IPC; } + /** @hide */ public int getBluetoothClass() { try { return sService.getRemoteClass(mAddress); @@ -220,6 +264,7 @@ public final class BluetoothDevice implements Parcelable { return BluetoothError.ERROR_IPC; } + /** @hide */ public String[] getUuids() { try { return sService.getRemoteUuids(mAddress); @@ -227,6 +272,7 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** @hide */ public int getServiceChannel(String uuid) { try { return sService.getRemoteServiceChannel(mAddress, uuid); @@ -234,6 +280,7 @@ public final class BluetoothDevice implements Parcelable { return BluetoothError.ERROR_IPC; } + /** @hide */ public boolean setPin(byte[] pin) { try { return sService.setPin(mAddress, pin); @@ -241,6 +288,7 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ public boolean setPasskey(int passkey) { try { return sService.setPasskey(mAddress, passkey); @@ -248,6 +296,7 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ public boolean setPairingConfirmation(boolean confirm) { try { return sService.setPairingConfirmation(mAddress, confirm); @@ -255,6 +304,7 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ public boolean cancelPairingUserInput() { try { return sService.cancelPairingUserInput(mAddress); @@ -263,17 +313,20 @@ public final class BluetoothDevice implements Parcelable { } /** - * Construct a secure RFCOMM socket ready to start an outgoing connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * The remote device will be authenticated and communication on this socket - * will be encrypted. - * @param port remote port - * @return an RFCOMM BluetoothSocket + * Create an RFCOMM {@link BluetoothSocket} ready to start a secure + * outgoing connection to this remote device. + *

The remote device will be authenticated and communication on this + * socket will be encrypted. + *

Use {@link BluetoothSocket#connect} to intiate the outgoing + * connection. + *

Valid RFCOMM channels are in range 1 to 30. + * @param channel RFCOMM channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions. + * insufficient permissions */ - public BluetoothSocket createRfcommSocket(int port) throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, port); + public BluetoothSocket createRfcommSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel); } /** @@ -286,6 +339,7 @@ public final class BluetoothDevice implements Parcelable { * @return An RFCOMM BluetoothSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. + * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); @@ -297,6 +351,7 @@ public final class BluetoothDevice implements Parcelable { * @return a SCO BluetoothSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions. + * @hide */ public BluetoothSocket createScoSocket() throws IOException { return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); @@ -309,6 +364,7 @@ public final class BluetoothDevice implements Parcelable { * @param pin pin as java String * @return the pin code as a UTF8 byte array, or null if it is an invalid * Bluetooth pin. + * @hide */ public static byte[] convertPinToBytes(String pin) { if (pin == null) { @@ -327,7 +383,8 @@ public final class BluetoothDevice implements Parcelable { return pinBytes; } - /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ + /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" + * @hide */ public static boolean checkBluetoothAddress(String address) { if (address == null || address.length() != ADDRESS_LENGTH) { return false; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 8be300b0418..e653c23295f 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -20,17 +20,31 @@ import java.io.Closeable; import java.io.IOException; /** - * Server (listening) Bluetooth Socket. + * A listening Bluetooth socket. * - * Currently only supports RFCOMM sockets. + *

The interface for Bluetooth Sockets is similar to that of TCP sockets: + * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server + * side, use a {@link BluetoothServerSocket} to create a listening server + * socket. It will return a new, connected {@link BluetoothSocket} on an + * accepted connection. On the client side, use the same + * {@link BluetoothSocket} object to both intiate the outgoing connection, + * and to manage the connected socket. * - * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is - * also known as the Serial Port Profile (SPP). + *

The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a + * connection orientated, streaming transport over Bluetooth. It is also known + * as the Serial Port Profile (SPP). * - * TODO: Consider exposing L2CAP sockets. - * TODO: Clean up javadoc grammer and formatting. - * TODO: Remove @hide - * @hide + *

Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link + * BluetoothSocket} ready for an outgoing connection to a remote + * {@link BluetoothDevice}. + * + *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + * {@link BluetoothServerSocket} ready for incoming connections to the local + * {@link BluetoothAdapter}. + * + *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + * safe. In particular, {@link #close} will always immediately abort ongoing + * operations and close the socket. */ public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; @@ -51,11 +65,13 @@ public final class BluetoothServerSocket implements Closeable { /** * Block until a connection is established. - * Returns a connected #BluetoothSocket. This server socket can be reused - * for subsequent incoming connections by calling #accept repeatedly. - * #close can be used to abort this call from another thread. - * @return A connected #BluetoothSocket - * @throws IOException On error, for example this call was aborted + *

Returns a connected {@link BluetoothSocket} on successful connection. + *

Once this call returns, it can be called again to accept subsequent + * incoming connections. + *

{@link #close} can be used to abort this call from another thread. + * @return a connected {@link BluetoothSocket} + * @throws IOException on error, for example this call was aborted, or + * timeout */ public BluetoothSocket accept() throws IOException { return accept(-1); @@ -63,11 +79,12 @@ public final class BluetoothServerSocket implements Closeable { /** * Block until a connection is established, with timeout. - * Returns a connected #BluetoothSocket. This server socket can be reused - * for subsequent incoming connections by calling #accept repeatedly. - * #close can be used to abort this call from another thread. - * @return A connected #BluetoothSocket - * @throws IOException On error, for example this call was aborted, or + *

Returns a connected {@link BluetoothSocket} on successful connection. + *

Once this call returns, it can be called again to accept subsequent + * incoming connections. + *

{@link #close} can be used to abort this call from another thread. + * @return a connected {@link BluetoothSocket} + * @throws IOException on error, for example this call was aborted, or * timeout */ public BluetoothSocket accept(int timeout) throws IOException { @@ -75,8 +92,8 @@ public final class BluetoothServerSocket implements Closeable { } /** - * Closes this socket. - * This will cause other blocking calls on this socket to immediately + * Immediately close this socket, and release all associated resources. + *

Causes blocked calls on this socket in other threads to immediately * throw an IOException. */ public void close() throws IOException { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index dda2cef99ed..eae0f3716e9 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -22,20 +22,34 @@ import java.io.InputStream; import java.io.OutputStream; /** - * Represents a connected or connecting Bluetooth Socket. + * A connected or connecting Bluetooth socket. * - * Currently only supports RFCOMM sockets. + *

The interface for Bluetooth Sockets is similar to that of TCP sockets: + * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server + * side, use a {@link BluetoothServerSocket} to create a listening server + * socket. It will return a new, connected {@link BluetoothSocket} on an + * accepted connection. On the client side, use the same + * {@link BluetoothSocket} object to both intiate the outgoing connection, + * and to manage the connected socket. * - * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is - * also known as the Serial Port Profile (SPP). + *

The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a + * connection orientated, streaming transport over Bluetooth. It is also known + * as the Serial Port Profile (SPP). * - * TODO: Consider exposing L2CAP sockets. - * TODO: Clean up javadoc grammer and formatting. - * TODO: Remove @hide - * @hide + *

Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link + * BluetoothSocket} ready for an outgoing connection to a remote + * {@link BluetoothDevice}. + * + *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + * {@link BluetoothServerSocket} ready for incoming connections to the local + * {@link BluetoothAdapter}. + * + *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + * safe. In particular, {@link #close} will always immediately abort ongoing + * operations and close the socket. */ public final class BluetoothSocket implements Closeable { - /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */ + /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ /*package*/ static final int TYPE_RFCOMM = 1; /*package*/ static final int TYPE_SCO = 2; /*package*/ static final int TYPE_L2CAP = 3; @@ -99,6 +113,7 @@ public final class BluetoothSocket implements Closeable { this(type, fd, auth, encrypt, new BluetoothDevice(address), port); } + /** @hide */ @Override protected void finalize() throws Throwable { try { @@ -110,19 +125,19 @@ public final class BluetoothSocket implements Closeable { /** * Attempt to connect to a remote device. - * This method will block until a connection is made or the connection + *

This method will block until a connection is made or the connection * fails. If this method returns without an exception then this socket - * is now connected. #close can be used to abort this call from another - * thread. - * @throws IOException On error, for example connection failure + * is now connected. + *

{@link #close} can be used to abort this call from another thread. + * @throws IOException on error, for example connection failure */ public void connect() throws IOException { connectNative(); } /** - * Closes this socket. - * This will cause other blocking calls on this socket to immediately + * Immediately close this socket, and release all associated resources. + *

Causes blocked calls on this socket in other threads to immediately * throw an IOException. */ public void close() throws IOException { @@ -130,9 +145,8 @@ public final class BluetoothSocket implements Closeable { } /** - * Return the remote device we are connecting, or connected, to. - * @return remote device, or null if this socket has not yet attempted - * or established a connection. + * Get the remote device this socket is connecting, or connected, to. + * @return remote device */ public BluetoothDevice getRemoteDevice() { return mDevice; @@ -140,7 +154,7 @@ public final class BluetoothSocket implements Closeable { /** * Get the input stream associated with this socket. - * The input stream will be returned even if the socket is not yet + *

The input stream will be returned even if the socket is not yet * connected, but operations on that stream will throw IOException until * the associated socket is connected. * @return InputStream @@ -151,7 +165,7 @@ public final class BluetoothSocket implements Closeable { /** * Get the output stream associated with this socket. - * The output stream will be returned even if the socket is not yet + *

The output stream will be returned even if the socket is not yet * connected, but operations on that stream will throw IOException until * the associated socket is connected. * @return OutputStream -- GitLab From efc6e07d40fd0505d9c8bf14f84ef8262bf1f0bb Mon Sep 17 00:00:00 2001 From: Yue Lixin Date: Thu, 9 Jul 2009 16:56:43 +0800 Subject: [PATCH 0043/1408] Add Bluetooth device picker support - add Intent and Extra definition - move profile filter into BluetoothClass --- .../java/android/bluetooth/BluetoothA2dp.java | 25 ------- .../android/bluetooth/BluetoothClass.java | 73 +++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 10 +++ .../android/bluetooth/BluetoothHeadset.java | 24 ------ .../android/bluetooth/BluetoothIntent.java | 29 ++++++++ 5 files changed, 112 insertions(+), 49 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 6e48b66baa1..b531a5038cb 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -213,31 +213,6 @@ public final class BluetoothA2dp { } } - /** - * Check class bits for possible A2DP Sink support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might be a A2DP Sink. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might be a A2DP sink - */ - public static boolean doesClassMatchSink(int btClass) { - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // By the A2DP spec, sinks must indicate the RENDER service. - // However we found some that do not (Chordette). So lets also - // match on some other class bits. - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: - case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } - /** Helper for converting a state to a string. * For debug use only - strings are not internationalized. * @hide diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 88ce18b4577..0061f10d453 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -46,6 +46,10 @@ public class BluetoothClass { /** Indicates the Bluetooth API could not retrieve the class */ public static final int ERROR = 0xFF000000; + public static final int PROFILE_HEADSET = 0; + public static final int PROFILE_A2DP = 1; + public static final int PROFILE_OPP = 2; + /** Every Bluetooth device has zero or more service classes */ public static class Service { public static final int BITMASK = 0xFFE000; @@ -187,5 +191,74 @@ public class BluetoothClass { return (btClass & Device.BITMASK); } } + + /** + * Check class bits for possible bluetooth profile support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support specified profile. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @param btClass The class + * @param profile The profile to be checked + * @return True if this device might support specified profile. + */ + public static boolean doesClassMatch(int btClass, int profile) { + if (profile == PROFILE_A2DP) { + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // By the A2DP spec, sinks must indicate the RENDER service. + // However we found some that do not (Chordette). So lets also + // match on some other class bits. + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } else if (profile == PROFILE_HEADSET) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + return true; + } + // Just in case they forgot the render service class + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } else if (profile == PROFILE_OPP) { + if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.OBJECT_TRANSFER)) { + return true; + } + + switch (BluetoothClass.Device.getDevice(btClass)) { + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: + case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: + case BluetoothClass.Device.COMPUTER_WEARABLE: + case BluetoothClass.Device.PHONE_UNCATEGORIZED: + case BluetoothClass.Device.PHONE_CELLULAR: + case BluetoothClass.Device.PHONE_CORDLESS: + case BluetoothClass.Device.PHONE_SMART: + case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: + case BluetoothClass.Device.PHONE_ISDN: + return true; + default: + return false; + } + } else { + return false; + } + } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1a97924e1e4..0a71961992b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -55,6 +55,16 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int BOND_BONDING = 2; + /** Ask device picker to show all kinds of BT devices. + * @hide */ + public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0; + /** Ask device picker to show BT devices that support AUDIO profiles. + * @hide */ + public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1; + /** Ask device picker to show BT devices that support Object Transfer. + * @hide */ + public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2; + //TODO: Unify these result codes in BluetoothResult or BluetoothError /** A bond attempt failed because pins did not match, or remote device did * not respond to pin request in time diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0e3d2bbb192..d31b6aef3c7 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -356,30 +356,6 @@ public final class BluetoothHeadset { return -1; } - /** - * Check class bits for possible HSP or HFP support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support HSP or HFP. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might support HSP or HFP. - */ - public static boolean doesClassMatch(int btClass) { - // The render service class is required by the spec for HFP, so is a - // pretty good signal - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // Just in case they forgot the render service class - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } - private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index 2a0de61f8c3..c39bc3dedfd 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -62,6 +62,35 @@ public interface BluetoothIntent { public static final String PASSKEY = "android.bluetooth.intent.PASSKEY"; + public static final String DEVICE_PICKER_NEED_AUTH = + "android.bluetooth.intent.DEVICE_PICKER_NEED_AUTH"; + public static final String DEVICE_PICKER_FILTER_TYPE = + "android.bluetooth.intent.DEVICE_PICKER_FILTER_TYPE"; + public static final String DEVICE_PICKER_LAUNCH_PACKAGE = + "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_PACKAGE"; + public static final String DEVICE_PICKER_LAUNCH_CLASS = + "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_CLASS"; + + /** + * Broadcast when one BT device is selected from BT device picker screen. + * Selected BT device address is contained in extra string "BluetoothIntent.ADDRESS". + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DEVICE_PICKER_DEVICE_SELECTED = + "android.bluetooth.intent.action.DEVICE_SELECTED"; + + /** + * Broadcast when someone want to select one BT device from devices list. + * This intent contains below extra data: + * - BluetoothIntent.DEVICE_PICKER_NEED_AUTH (boolean): if need authentication + * - BluetoothIntent.DEVICE_PICKER_FILTER_TYPE (int): what kinds of device should be listed + * - BluetoothIntent.DEVICE_PICKER_LAUNCH_PACKAGE (string): where(which package) this intent come from + * - BluetoothIntent.DEVICE_PICKER_LAUNCH_CLASS (string): where(which class) this intent come from + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String DEVICE_PICKER_DEVICE_PICKER = + "android.bluetooth.intent.action.DEVICE_PICKER"; + /** Broadcast when the local Bluetooth device state changes, for example * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and * BLUETOOTH_PREVIOUS_STATE. */ -- GitLab From 0d5387689d2eda5a4002fa228b8c11e931e9de5f Mon Sep 17 00:00:00 2001 From: Jackson Fan Date: Wed, 19 Aug 2009 21:01:29 +0800 Subject: [PATCH 0044/1408] Use correct UUID to authorize AVRCP --- framework/java/android/bluetooth/BluetoothUuid.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 1ec7fb38b44..c15bc20bfb0 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -37,6 +37,7 @@ public final class BluetoothUuid { public static final UUID Handsfree = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB"); public static final UUID AvrcpController = UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB"); + public static final UUID AvrcpTarget = UUID.fromString("0000110C-0000-1000-8000-00805F9B34FB"); public static boolean isAudioSource(UUID uuid) { return uuid.equals(AudioSource); @@ -61,4 +62,8 @@ public final class BluetoothUuid { public static boolean isAvrcpController(UUID uuid) { return uuid.equals(AvrcpController); } + + public static boolean isAvrcpTarget(UUID uuid) { + return uuid.equals(AvrcpTarget); + } } -- GitLab From 3ab1182c39e07407a0531a24f1985216fa7ee74b Mon Sep 17 00:00:00 2001 From: Lixin Yue Date: Mon, 31 Aug 2009 15:55:13 +0800 Subject: [PATCH 0045/1408] Add Bluetooth Device trust --- .../android/bluetooth/BluetoothDevice.java | 27 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 2 ++ 2 files changed, 29 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0a71961992b..0ec3243fd68 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -266,6 +266,33 @@ public final class BluetoothDevice implements Parcelable { return BluetoothError.ERROR_IPC; } + /** + * Get trust state of a remote device. + * @hide + */ + public boolean getTrustState() { + try { + return sService.getTrustState(mAddress); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + + /** + * Set trust state for a remote device. + * @param value the trust state value (true or false) + * @hide + */ + public boolean setTrust(boolean value) { + try { + return sService.setTrust(mAddress, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** @hide */ public int getBluetoothClass() { try { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 9e05a871d41..a11ceac0983 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -58,4 +58,6 @@ interface IBluetooth boolean setPairingConfirmation(in String address, boolean confirm); boolean cancelPairingUserInput(in String address); + boolean setTrust(in String address, in boolean value); + boolean getTrustState(in String address); } -- GitLab From 4cd2cd92746e80798c79161791a4042251ed356a Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 2 Sep 2009 11:51:35 -0700 Subject: [PATCH 0046/1408] Immediately destroy BluetoothSocket's on close(). Unfortunatley, shutdown() on the underlying fd does not actually stop a listening socket from listening. You need to call close() on the fd to do this. There is no way around it. So this means the Java BluetoothSocket code has to call destroyNative() during BluetoothSocket.close(). Since native methods cannot be called after destroyNative(), add a ReadWrite lock and mClosed field to protect access to native methods. This fixes the "resource busy" error when Bluetooth OPP and Bluetooth PBAP tried to resume listening after turning BT off and then on. --- .../android/bluetooth/BluetoothAdapter.java | 6 +- .../bluetooth/BluetoothInputStream.java | 6 +- .../bluetooth/BluetoothOutputStream.java | 4 +- .../bluetooth/BluetoothServerSocket.java | 5 +- .../android/bluetooth/BluetoothSocket.java | 102 ++++++++++++++++-- 5 files changed, 104 insertions(+), 19 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6bd2a5abc3a..8975fe27b7c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -312,7 +312,7 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, true, true, channel); try { - socket.mSocket.bindListenNative(); + socket.mSocket.bindListen(); } catch (IOException e) { try { socket.close(); @@ -334,7 +334,7 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); try { - socket.mSocket.bindListenNative(); + socket.mSocket.bindListen(); } catch (IOException e) { try { socket.close(); @@ -356,7 +356,7 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_SCO, false, false, -1); try { - socket.mSocket.bindListenNative(); + socket.mSocket.bindListen(); } catch (IOException e) { try { socket.close(); diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index c060f3263ee..03af95337c5 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -37,7 +37,7 @@ import java.io.InputStream; * Return number of bytes available before this stream will block. */ public int available() throws IOException { - return mSocket.availableNative(); + return mSocket.available(); } public void close() throws IOException { @@ -57,7 +57,7 @@ import java.io.InputStream; */ public int read() throws IOException { byte b[] = new byte[1]; - int ret = mSocket.readNative(b, 0, 1); + int ret = mSocket.read(b, 0, 1); if (ret == 1) { return (int)b[0] & 0xff; } else { @@ -93,6 +93,6 @@ import java.io.InputStream; if ((offset | length) < 0 || length > b.length - offset) { throw new ArrayIndexOutOfBoundsException("invalid offset or length"); } - return mSocket.readNative(b, offset, length); + return mSocket.read(b, offset, length); } } diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index 7e2ead478af..62242a2672f 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -53,7 +53,7 @@ import java.io.OutputStream; public void write(int oneByte) throws IOException { byte b[] = new byte[1]; b[0] = (byte)oneByte; - mSocket.writeNative(b, 0, 1); + mSocket.write(b, 0, 1); } /** @@ -82,6 +82,6 @@ import java.io.OutputStream; if ((offset | count) < 0 || count > b.length - offset) { throw new IndexOutOfBoundsException("invalid offset or length"); } - mSocket.writeNative(b, offset, count); + mSocket.write(b, offset, count); } } diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index e653c23295f..b65084157eb 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -47,6 +47,7 @@ import java.io.IOException; * operations and close the socket. */ public final class BluetoothServerSocket implements Closeable { + /*package*/ final BluetoothSocket mSocket; /** @@ -88,7 +89,7 @@ public final class BluetoothServerSocket implements Closeable { * timeout */ public BluetoothSocket accept(int timeout) throws IOException { - return mSocket.acceptNative(timeout); + return mSocket.accept(timeout); } /** @@ -97,6 +98,6 @@ public final class BluetoothServerSocket implements Closeable { * throw an IOException. */ public void close() throws IOException { - mSocket.closeNative(); + mSocket.close(); } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index eae0f3716e9..ccbe23e55c7 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.locks.ReentrantReadWriteLock; + /** * A connected or connecting Bluetooth socket. * @@ -63,7 +65,14 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; - private int mSocketData; /* used by native code only */ + /** prevents all native calls after destroyNative() */ + private boolean mClosed; + + /** protects mClosed */ + private final ReentrantReadWriteLock mLock; + + /** used by native code only */ + private int mSocketData; /** * Construct a BluetoothSocket. @@ -95,6 +104,8 @@ public final class BluetoothSocket implements Closeable { } mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); + mClosed = false; + mLock = new ReentrantReadWriteLock(); } /** @@ -132,7 +143,13 @@ public final class BluetoothSocket implements Closeable { * @throws IOException on error, for example connection failure */ public void connect() throws IOException { - connectNative(); + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + connectNative(); + } finally { + mLock.readLock().unlock(); + } } /** @@ -141,7 +158,24 @@ public final class BluetoothSocket implements Closeable { * throw an IOException. */ public void close() throws IOException { - closeNative(); + // abort blocking operations on the socket + mLock.readLock().lock(); + try { + if (mClosed) return; + abortNative(); + } finally { + mLock.readLock().unlock(); + } + + // all native calls are guarenteed to immediately return after + // abortNative(), so this lock should immediatley acquire + mLock.writeLock().lock(); + try { + mClosed = true; + destroyNative(); + } finally { + mLock.writeLock().unlock(); + } } /** @@ -174,14 +208,64 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } + /*package*/ void bindListen() throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + bindListenNative(); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ BluetoothSocket accept(int timeout) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return acceptNative(timeout); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int available() throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return availableNative(); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int read(byte[] b, int offset, int length) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return readNative(b, offset, length); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int write(byte[] b, int offset, int length) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return writeNative(b, offset, length); + } finally { + mLock.readLock().unlock(); + } + } + private native void initSocketNative() throws IOException; private native void initSocketFromFdNative(int fd) throws IOException; private native void connectNative() throws IOException; - /*package*/ native void bindListenNative() throws IOException; - /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException; - /*package*/ native int availableNative() throws IOException; - /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException; - /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException; - /*package*/ native void closeNative() throws IOException; + private native void bindListenNative() throws IOException; + private native BluetoothSocket acceptNative(int timeout) throws IOException; + private native int availableNative() throws IOException; + private native int readNative(byte[] b, int offset, int length) throws IOException; + private native int writeNative(byte[] b, int offset, int length) throws IOException; + private native void abortNative() throws IOException; private native void destroyNative() throws IOException; } -- GitLab From ed6f2ce927782e15c662eab48cd95d95e7ef7841 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 8 Sep 2009 10:12:06 -0700 Subject: [PATCH 0047/1408] Add javadoc to explain which permissions are required for Public BT API's. --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 + framework/java/android/bluetooth/BluetoothDevice.java | 1 + framework/java/android/bluetooth/BluetoothServerSocket.java | 3 +++ framework/java/android/bluetooth/BluetoothSocket.java | 3 +++ 4 files changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8975fe27b7c..5a182f0fb45 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -303,6 +303,7 @@ public final class BluetoothAdapter { *

Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections to listening {@link BluetoothServerSocket}. *

Valid RFCOMM channels are in range 1 to 30. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0ec3243fd68..1b7011cdba3 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -357,6 +357,7 @@ public final class BluetoothDevice implements Parcelable { *

Use {@link BluetoothSocket#connect} to intiate the outgoing * connection. *

Valid RFCOMM channels are in range 1 to 30. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * @param channel RFCOMM channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index b65084157eb..45dc432d332 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -45,6 +45,9 @@ import java.io.IOException; *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread * safe. In particular, {@link #close} will always immediately abort ongoing * operations and close the socket. + * + *

All methods on a {@link BluetoothServerSocket} require + * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothServerSocket implements Closeable { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index ccbe23e55c7..e462ea63831 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -49,6 +49,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread * safe. In particular, {@link #close} will always immediately abort ongoing * operations and close the socket. + * + *

All methods on a {@link BluetoothSocket} require + * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothSocket implements Closeable { /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ -- GitLab From 7482a80ce3d46f9991302cc464bc6a9e5a317451 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 8 Sep 2009 13:15:33 -0700 Subject: [PATCH 0048/1408] API_CHANGE Another round of Bluetooth API clean up, javadoc'ing and unhide'ing. -- Symbols for getting/setting bluetooth state -- BluetoothAdapter.ACTION_STATE_CHANGED BluetoothAdapter.EXTRA_STATE BluetoothAdapter.EXTRA_PREVIOUS_STATE BluetoothAdapter.STATE_OFF BluetoothAdapter.STATE_TURNING_ON BluetoothAdapter.STATE_ON BluetoothAdapter.STATE_TURNING_OFF BluetoothAdapter.isEnabled() BluetoothAdapter.getState() BluetoothAdapter.enable() BluetoothAdapter.disable() -- Symbols for getting/setting scan mode -- BluetoothAdapter.ACTION_SCAN_MODE_CHANGED BluetoothAdapter.EXTRA_SCAN_MODE BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE BluetoothAdapter.SCAN_MODE_NONE BluetoothAdapter.SCAN_MODE_CONNECTABLE BluetoothAdapter.SCAN_MODE_DISCOVERABLE BluetoothAdapter.getScanMode() BluetoothAdapter.setScanMode() -- Symbols for getting address/names -- BluetoothAdapter.getAddress() BluetoothAdapter.getName() BluetoothAdapter.setName() --- .../android/bluetooth/BluetoothAdapter.java | 270 +++++++++++++----- .../android/bluetooth/BluetoothDevice.java | 3 +- .../android/bluetooth/BluetoothError.java | 2 + .../android/bluetooth/BluetoothIntent.java | 28 +- 4 files changed, 205 insertions(+), 98 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5a182f0fb45..1770bc79351 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.os.RemoteException; import android.util.Log; @@ -40,32 +42,109 @@ import java.util.HashSet; public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - /** @hide */ - public static final int BLUETOOTH_STATE_OFF = 0; - /** @hide */ - public static final int BLUETOOTH_STATE_TURNING_ON = 1; - /** @hide */ - public static final int BLUETOOTH_STATE_ON = 2; - /** @hide */ - public static final int BLUETOOTH_STATE_TURNING_OFF = 3; - - /** Inquiry scan and page scan are both off. - * Device is neither discoverable nor connectable - * @hide */ - public static final int SCAN_MODE_NONE = 0; - /** Page scan is on, inquiry scan is off. - * Device is connectable, but not discoverable - * @hide*/ - public static final int SCAN_MODE_CONNECTABLE = 1; - /** Page scan and inquiry scan are on. - * Device is connectable and discoverable - * @hide*/ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; + /** + * Broadcast Action: The state of the local Bluetooth adapter has been + * changed. + *

For example, Bluetooth has been turned on or off. + *

Contains the extra fields {@link #EXTRA_STATE} and {@link + * #EXTRA_PREVIOUS_STATE} containing the new and old states + * respectively. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STATE_CHANGED = + "android.bluetooth.intent.action.STATE_CHANGED"; - /** @hide */ - public static final int RESULT_FAILURE = -1; - /** @hide */ - public static final int RESULT_SUCCESS = 0; + /** + * Used as an int extra field in {@link #ACTION_STATE_CHANGED} + * intents to request the current power state. Possible values are: + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + */ + public static final String EXTRA_STATE = + "android.bluetooth.intent.extra.STATE"; + /** + * Used as an int extra field in {@link #ACTION_STATE_CHANGED} + * intents to request the previous power state. Possible values are: + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + */ + public static final String EXTRA_PREVIOUS_STATE = + "android.bluetooth.intent.extra.PREVIOUS_STATE"; + + /** + * Indicates the local Bluetooth adapter is off. + */ + public static final int STATE_OFF = 40; + /** + * Indicates the local Bluetooth adapter is turning on. However local + * clients should wait for {@link #STATE_ON} before attempting to + * use the adapter. + */ + public static final int STATE_TURNING_ON = 41; + /** + * Indicates the local Bluetooth adapter is on, and ready for use. + */ + public static final int STATE_ON = 42; + /** + * Indicates the local Bluetooth adapter is turning off. Local clients + * should immediately attempt graceful disconnection of any remote links. + */ + public static final int STATE_TURNING_OFF = 43; + + /** + * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter + * has changed. + *

Contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link + * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes + * respectively. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SCAN_MODE_CHANGED = + "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; + + /** + * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} + * intents to request the current scan mode. Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, + */ + public static final String EXTRA_SCAN_MODE = "android.bluetooth.intent.extra.SCAN_MODE"; + /** + * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} + * intents to request the previous scan mode. Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, + */ + public static final String EXTRA_PREVIOUS_SCAN_MODE = + "android.bluetooth.intent.extra.PREVIOUS_SCAN_MODE"; + + /** + * Indicates that both inquiry scan and page scan are disabled on the local + * Bluetooth adapter. Therefore this device is neither discoverable + * nor connectable from remote Bluetooth devices. + */ + public static final int SCAN_MODE_NONE = 50; + /** + * Indicates that inquiry scan is disabled, but page scan is enabled on the + * local Bluetooth adapter. Therefore this device is not discoverable from + * remote Bluetooth devices, but is connectable from remote devices that + * have previously discovered this device. + */ + public static final int SCAN_MODE_CONNECTABLE = 51; + /** + * Indicates that both inquiry scan and page scan are enabled on the local + * Bluetooth adapter. Therefore this device is both discoverable and + * connectable from remote Bluetooth devices. + */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 53; /** The user will be prompted to enter a pin * @hide */ @@ -97,6 +176,7 @@ public final class BluetoothAdapter { * such as "00:11:22:33:AA:BB". *

A {@link BluetoothDevice} will always be returned for a valid * hardware address, even if this adapter has never seen that device. + * * @param address valid Bluetooth MAC address * @throws IllegalArgumentException if address is invalid */ @@ -105,10 +185,12 @@ public final class BluetoothAdapter { } /** - * Is Bluetooth currently turned on. + * Return true if Bluetooth is currently enabled and ready for use. + *

Equivalent to: + * getBluetoothState() == STATE_ON + *

Requires {@link android.Manifest.permission#BLUETOOTH} * - * @return true if Bluetooth enabled, false otherwise. - * @hide + * @return true if the local adapter is turned on */ public boolean isEnabled() { try { @@ -118,28 +200,40 @@ public final class BluetoothAdapter { } /** - * Get the current state of Bluetooth. + * Get the current state of the local Bluetooth adapter. + *

Possible return values are + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * - * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. - * @hide + * @return current state of Bluetooth adapter */ - public int getBluetoothState() { + public int getState() { try { return mService.getBluetoothState(); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR; + return STATE_OFF; } /** - * Enable the Bluetooth device. - * Turn on the underlying hardware. - * This is an asynchronous call, - * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if - * and when the device is sucessfully enabled. - * @return false if we cannot enable the Bluetooth device. True does not - * imply the device was enabled, it only implies that so far there were no - * problems. - * @hide + * Turn on the local Bluetooth adapter. + *

This powers on the underlying Bluetooth hardware, and starts all + * Bluetooth system services. + *

This is an asynchronous call: it will return immediatley, and + * clients should listen for {@link #ACTION_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned on - + * such as Airplane mode, or the adapter is already turned on. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @return true to indicate adapter startup has begun, or false on + * immediate error */ public boolean enable() { try { @@ -149,11 +243,22 @@ public final class BluetoothAdapter { } /** - * Disable the Bluetooth device. - * This turns off the underlying hardware. + * Turn off the local Bluetooth adapter. + *

This gracefully shuts down all Bluetooth connections, stops Bluetooth + * system services, and powers down the underlying Bluetooth hardware. + *

This is an asynchronous call: it will return immediatley, and + * clients should listen for {@link #ACTION_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned off - + * such as the adapter already being turned off. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * - * @return true if successful, false otherwise. - * @hide + * @return true to indicate adapter shutdown has begun, or false on + * immediate error */ public boolean disable() { try { @@ -162,7 +267,13 @@ public final class BluetoothAdapter { return false; } - /** @hide */ + /** + * Returns the hardware address of the local Bluetooth adapter. + *

For example, "00:11:22:AA:BB:CC". + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return Bluetooth hardware address as string + */ public String getAddress() { try { return mService.getAddress(); @@ -171,13 +282,11 @@ public final class BluetoothAdapter { } /** - * Get the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. Currently it is only - * possible to retrieve the Bluetooth name when Bluetooth is enabled. + * Get the friendly Bluetooth name of the local Bluetooth adapter. + *

This name is visible to remote Bluetooth devices. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * - * @return the Bluetooth name, or null if there was a problem. - * @hide + * @return the Bluetooth name, or null on error */ public String getName() { try { @@ -187,14 +296,15 @@ public final class BluetoothAdapter { } /** - * Set the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. The Bluetooth Service - * is responsible for persisting this name. + * Set the friendly Bluetooth name of the local Bluetoth adapter. + *

This name is visible to remote Bluetooth devices. + *

Valid Bluetooth names are a maximum of 248 UTF-8 characters, however + * many remote devices can only display the first 40 characters, and some + * may be limited to just 20. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * - * @param name the name to set - * @return true, if the name was successfully set. False otherwise. - * @hide + * @param name a valid Bluetooth name + * @return true if the name was set, false otherwise */ public boolean setName(String name) { try { @@ -204,28 +314,46 @@ public final class BluetoothAdapter { } /** - * Get the current scan mode. - * Used to determine if the local device is connectable and/or discoverable - * @return Scan mode, one of SCAN_MODE_* or an error code - * @hide + * Get the current Bluetooth scan mode of the local Bluetooth adaper. + *

The Bluetooth scan mode determines if the local adapter is + * connectable and/or discoverable from remote Bluetooth devices. + *

Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return scan mode */ public int getScanMode() { try { return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; + return SCAN_MODE_NONE; } /** - * Set the current scan mode. - * Used to make the local device connectable and/or discoverable - * @param scanMode One of SCAN_MODE_* - * @hide + * Set the Bluetooth scan mode of the local Bluetooth adapter. + *

The Bluetooth scan mode determines if the local adapter is + * connectable and/or discoverable from remote Bluetooth devices. + *

For privacy reasons, it is recommended to limit the duration of time + * that the local adapter remains in a discoverable scan mode. For example, + * 2 minutes is a generous time to allow a remote Bluetooth device to + * initiate and complete its discovery process. + *

Valid scan mode values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param mode valid scan mode + * @return true if the scan mode was set, false otherwise */ - public void setScanMode(int scanMode) { + public boolean setScanMode(int mode) { try { - mService.setScanMode(scanMode); + return mService.setScanMode(mode); } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; } /** @hide */ diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1b7011cdba3..7086557a574 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -193,9 +193,9 @@ public final class BluetoothDevice implements Parcelable { *

The local adapter will automatically retrieve remote names when * performing a device scan, and will cache them. This method just returns * the name for this device from the cache. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * * @return the Bluetooth name, or null if there was a problem. - * @hide */ public String getName() { try { @@ -358,6 +358,7 @@ public final class BluetoothDevice implements Parcelable { * connection. *

Valid RFCOMM channels are in range 1 to 30. *

Requires {@link android.Manifest.permission#BLUETOOTH} + * * @param channel RFCOMM channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/BluetoothError.java index 2554bead0ca..81c1ce9dee7 100644 --- a/framework/java/android/bluetooth/BluetoothError.java +++ b/framework/java/android/bluetooth/BluetoothError.java @@ -21,6 +21,8 @@ package android.bluetooth; * * Errors are always negative. * + * TODO: Deprecate this class. + * * @hide */ public class BluetoothError { diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java index c39bc3dedfd..8de19f5f5dc 100644 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ b/framework/java/android/bluetooth/BluetoothIntent.java @@ -20,17 +20,12 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. + * Bluetooth API constants. * + * TODO: Deprecate this class * @hide */ public interface BluetoothIntent { - public static final String SCAN_MODE = - "android.bluetooth.intent.SCAN_MODE"; public static final String DEVICE = "android.bluetooth.intent.DEVICE"; public static final String NAME = @@ -41,10 +36,6 @@ public interface BluetoothIntent { "android.bluetooth.intent.RSSI"; public static final String CLASS = "android.bluetooth.intent.CLASS"; - public static final String BLUETOOTH_STATE = - "android.bluetooth.intent.BLUETOOTH_STATE"; - public static final String BLUETOOTH_PREVIOUS_STATE = - "android.bluetooth.intent.BLUETOOTH_PREVIOUS_STATE"; public static final String HEADSET_STATE = "android.bluetooth.intent.HEADSET_STATE"; public static final String HEADSET_PREVIOUS_STATE = @@ -91,25 +82,10 @@ public interface BluetoothIntent { public static final String DEVICE_PICKER_DEVICE_PICKER = "android.bluetooth.intent.action.DEVICE_PICKER"; - /** Broadcast when the local Bluetooth device state changes, for example - * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and - * BLUETOOTH_PREVIOUS_STATE. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BLUETOOTH_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.BLUETOOTH_STATE_CHANGED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String NAME_CHANGED_ACTION = "android.bluetooth.intent.action.NAME_CHANGED"; - /** - * Broadcast when the scan mode changes. Always contains an int extra - * named SCAN_MODE that contains the new scan mode. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SCAN_MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DISCOVERY_STARTED_ACTION = "android.bluetooth.intent.action.DISCOVERY_STARTED"; -- GitLab From 8a1cd0cdce892a6bd25bc7292007fe0220b3ae10 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 8 Sep 2009 17:40:43 -0700 Subject: [PATCH 0049/1408] API_CHANGE Deprecate BluetoothError.java. I spent a lot of time experimenting with a class BluetoothError to enumerate the many error codes returned by the Bluetooth API. But at the end of the day they were never used. The vast majority of method calls only really need a true/false error value, and often not even that. Methods which do need more detailed error enumeration (for example, bonding failures) can have there own enumerated error codes. But there is no need for a common set of error codes. Also change the IPC failed warnings in BluetoothA2dp to Log.e. These indicate a very serious error. Introduce BluetoothAdapter.ERROR and BluetoothDevice.ERROR as helper sentinel values. --- .../java/android/bluetooth/BluetoothA2dp.java | 39 ++++++++-------- .../android/bluetooth/BluetoothAdapter.java | 9 ++++ .../android/bluetooth/BluetoothDevice.java | 21 ++++++--- .../android/bluetooth/BluetoothError.java | 44 ------------------- .../android/bluetooth/IBluetoothA2dp.aidl | 6 +-- 5 files changed, 46 insertions(+), 73 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothError.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b531a5038cb..060f20ed664 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -42,9 +42,6 @@ import java.util.HashSet; * * Currently the BluetoothA2dp service runs in the system server and this * proxy object will be immediately bound to the service on construction. - * However this may change in future releases, and error codes such as - * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the - * proxy object is not yet attached. * * Currently this class provides methods to connect to A2DP audio sinks. * @@ -105,16 +102,16 @@ public final class BluetoothA2dp { * Listen for SINK_STATE_CHANGED_ACTION to find out when the * connection is completed. * @param device Remote BT device. - * @return Result code, negative indicates an immediate error. + * @return false on immediate error, true otherwise * @hide */ - public int connectSink(BluetoothDevice device) { + public boolean connectSink(BluetoothDevice device) { if (DBG) log("connectSink(" + device + ")"); try { return mService.connectSink(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } @@ -122,16 +119,16 @@ public final class BluetoothA2dp { * Listen for SINK_STATE_CHANGED_ACTION to find out when * disconnect is completed. * @param device Remote BT device. - * @return Result code, negative indicates an immediate error. + * @return false on immediate error, true otherwise * @hide */ - public int disconnectSink(BluetoothDevice device) { + public boolean disconnectSink(BluetoothDevice device) { if (DBG) log("disconnectSink(" + device + ")"); try { return mService.disconnectSink(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } @@ -156,14 +153,14 @@ public final class BluetoothA2dp { return Collections.unmodifiableSet( new HashSet(Arrays.asList(mService.getConnectedSinks()))); } catch (RemoteException e) { - Log.w(TAG, "", e); + Log.e(TAG, "", e); return null; } } /** Get the state of an A2DP sink * @param device Remote BT device. - * @return State code, or negative on error + * @return State code, one of STATE_ * @hide */ public int getSinkState(BluetoothDevice device) { @@ -171,8 +168,8 @@ public final class BluetoothA2dp { try { return mService.getSinkState(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return BluetoothA2dp.STATE_DISCONNECTED; } } @@ -186,15 +183,15 @@ public final class BluetoothA2dp { * @param device Paired sink * @param priority Integer priority, for example PRIORITY_AUTO or * PRIORITY_NONE - * @return Result code, negative indicates an error + * @return true if priority is set, false on error */ - public int setSinkPriority(BluetoothDevice device, int priority) { + public boolean setSinkPriority(BluetoothDevice device, int priority) { if (DBG) log("setSinkPriority(" + device + ", " + priority + ")"); try { return mService.setSinkPriority(device, priority); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } @@ -208,8 +205,8 @@ public final class BluetoothA2dp { try { return mService.getSinkPriority(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return PRIORITY_OFF; } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1770bc79351..18f6995d26c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -42,6 +42,15 @@ import java.util.HashSet; public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + /** + * Sentinel error value for this class. Guaranteed to not equal any other + * integer constant in this class. Provided as a convenience for functions + * that require a sentinel error value, for example: + *

Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + * BluetoothAdapter.ERROR) + */ + public static final int ERROR = -1; + /** * Broadcast Action: The state of the local Bluetooth adapter has been * changed. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7086557a574..1ab4389b435 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -44,6 +44,15 @@ import java.io.UnsupportedEncodingException; public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; + /** + * Sentinel error value for this class. Guaranteed to not equal any other + * integer constant in this class. Provided as a convenience for functions + * that require a sentinel error value, for example: + *

Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + * BluetoothAdapter.ERROR) + */ + public static final int ERROR = -1; + /** We do not have a link key for the remote device, and are therefore not * bonded * @hide*/ @@ -65,7 +74,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2; - //TODO: Unify these result codes in BluetoothResult or BluetoothError + /** A bond attempt succeeded + * @hide */ + public static final int BOND_SUCCESS = 0; /** A bond attempt failed because pins did not match, or remote device did * not respond to pin request in time * @hide */ @@ -252,8 +263,8 @@ public final class BluetoothDevice implements Parcelable { * Get the bonding state of a remote device. * * Result is one of: - * BluetoothError.* * BOND_* + * ERROR * * @param address Bluetooth hardware address of the remote device to check. * @return Result code @@ -263,7 +274,7 @@ public final class BluetoothDevice implements Parcelable { try { return sService.getBondState(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; + return BluetoothDevice.ERROR; } /** @@ -298,7 +309,7 @@ public final class BluetoothDevice implements Parcelable { try { return sService.getRemoteClass(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; + return BluetoothDevice.ERROR; } /** @hide */ @@ -314,7 +325,7 @@ public final class BluetoothDevice implements Parcelable { try { return sService.getRemoteServiceChannel(mAddress, uuid); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; + return BluetoothDevice.ERROR; } /** @hide */ diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/BluetoothError.java deleted file mode 100644 index 81c1ce9dee7..00000000000 --- a/framework/java/android/bluetooth/BluetoothError.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2008 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; - -/** - * Bluetooth API error codes. - * - * Errors are always negative. - * - * TODO: Deprecate this class. - * - * @hide - */ -public class BluetoothError { - /** No error */ - public static final int SUCCESS = 0; - - /** Generic error */ - public static final int ERROR = -1000; - - /** Bluetooth currently disabled */ - public static final int ERROR_DISABLED = -1001; - - /** IPC is not ready, for example service is not yet bound */ - public static final int ERROR_IPC_NOT_READY = -1011; - - /** Some other IPC error, for example a RemoteException */ - public static final int ERROR_IPC = -1012; - -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index e6c6be2594f..2df7f238552 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -24,10 +24,10 @@ import android.bluetooth.BluetoothDevice; * {@hide} */ interface IBluetoothA2dp { - int connectSink(in BluetoothDevice device); - int disconnectSink(in BluetoothDevice device); + boolean connectSink(in BluetoothDevice device); + boolean disconnectSink(in BluetoothDevice device); BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports int getSinkState(in BluetoothDevice device); - int setSinkPriority(in BluetoothDevice device, int priority); + boolean setSinkPriority(in BluetoothDevice device, int priority); int getSinkPriority(in BluetoothDevice device); } -- GitLab From dac4c0d7c3ac27746fb8feaf8fc74221972ff5f2 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 10 Sep 2009 10:21:56 -0700 Subject: [PATCH 0050/1408] API_CHANGE: Cleanup, javadoc and unhide more Bluetooth API. This is a large batch, and covers: -- Bluetooth Device Discovery -- BluetoothAdapter.ACTION_DISCOVERY_STARTED BluetoothAdapter.ACTION_DISCOVERY_FINISHED BluetoothAdapter.startDiscovery() BluetoothAdapter.cancelDiscovery() BluetoothAdapter.isDiscovering() -- Bluetooth bonding (pairing) -- BluetoothAdapter.getBondedDevices() BluetoothDevice.ACTION_BOND_STATE_CHANGED BluetoothDevice.EXTRA_BOND_STATE BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE BluetoothDevice.BOND_NONE BluetoothDevice.BOND_BONDING BluetoothDevice.BOND_BONDED BluetoothDevice.getBondState() BluetoothDevice.createBond() BluetoothDevice.cancelBondProcess() BluetoothDevice.removeBond() -- BluetoothClass -- BluetoothDevice.ACTION_CLASS_CHANGED BluetoothDevice.EXTRA_CLASS BluetoothDevice.getBluetoothClass() BluetoothClass.Service.* BluetoothClass.Device.Major.* BluetoothClass.Device.* BluetoothClass.getDeviceClass() BluetoothClass.getMajorDeviceClass() BluetoothClass.hasService() -- Misc BluetoothDevice -- BluetoothDevice.ACTION_ACL_CONNECTED BluetoothDevice.ACTION_ACL_DISCONNECTED_REQUESTED BluetoothDevice.ACTION_ACL_DISCONNECTED BluetoothDevice.ACTION_DISCOVERED BluetoothDevice.ACTION_NAME_CHANGED BluetoothDevice.EXTRA_DEVICE BluetoothDevice.EXTRA_NAME BluetoothDevice.EXTRA_RSSI -- Misc BluetoothAdapter -- BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED BluetoothAdapter.EXTRA_LOCAL_NAME BluetoothAdapter.checkBluetoothAddress() I deprecated BluetoothIntent and moved each intent into the class it relates to. Change-Id: I877b1280428ab46278b2bc25668bb44cda22dc36 --- .../java/android/bluetooth/BluetoothA2dp.java | 21 +- .../android/bluetooth/BluetoothAdapter.java | 192 +++++++--- .../android/bluetooth/BluetoothClass.java | 254 ++++++++------ .../android/bluetooth/BluetoothDevice.java | 328 +++++++++++++----- .../bluetooth/BluetoothDevicePicker.java | 66 ++++ .../android/bluetooth/BluetoothHeadset.java | 23 ++ .../android/bluetooth/BluetoothIntent.java | 154 -------- .../java/android/bluetooth/BluetoothPbap.java | 4 +- 8 files changed, 638 insertions(+), 404 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothDevicePicker.java delete mode 100644 framework/java/android/bluetooth/BluetoothIntent.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 060f20ed664..2e9612ac063 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -51,20 +51,21 @@ public final class BluetoothA2dp { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = false; - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_STATE = - "android.bluetooth.a2dp.intent.SINK_STATE"; - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_PREVIOUS_STATE = - "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; + /** int extra for ACTION_SINK_STATE_CHANGED */ + public static final String EXTRA_SINK_STATE = + "android.bluetooth.a2dp.extra.SINK_STATE"; + /** int extra for ACTION_SINK_STATE_CHANGED */ + public static final String EXTRA_PREVIOUS_SINK_STATE = + "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE"; /** Indicates the state of an A2DP audio sink has changed. - * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. + * This intent will always contain EXTRA_SINK_STATE, + * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE + * extras. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SINK_STATE_CHANGED_ACTION = - "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; + public static final String ACTION_SINK_STATE_CHANGED = + "android.bluetooth.a2dp.action.SINK_STATE_CHANGED"; public static final int STATE_DISCONNECTED = 0; public static final int STATE_CONNECTING = 1; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 18f6995d26c..96a927b30cb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,8 +36,6 @@ import java.util.HashSet; * *

Use the {@link BluetoothDevice} class for operations on remote Bluetooth * devices. - * - *

TODO: unhide more of this class */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; @@ -49,20 +47,20 @@ public final class BluetoothAdapter { *

Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, * BluetoothAdapter.ERROR) */ - public static final int ERROR = -1; + public static final int ERROR = Integer.MIN_VALUE; /** * Broadcast Action: The state of the local Bluetooth adapter has been * changed. *

For example, Bluetooth has been turned on or off. - *

Contains the extra fields {@link #EXTRA_STATE} and {@link + *

Always contains the extra fields {@link #EXTRA_STATE} and {@link * #EXTRA_PREVIOUS_STATE} containing the new and old states * respectively. *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_STATE_CHANGED = - "android.bluetooth.intent.action.STATE_CHANGED"; + "android.bluetooth.adapter.action.STATE_CHANGED"; /** * Used as an int extra field in {@link #ACTION_STATE_CHANGED} @@ -73,7 +71,7 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_OFF}, */ public static final String EXTRA_STATE = - "android.bluetooth.intent.extra.STATE"; + "android.bluetooth.adapter.extra.STATE"; /** * Used as an int extra field in {@link #ACTION_STATE_CHANGED} * intents to request the previous power state. Possible values are: @@ -83,39 +81,39 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_OFF}, */ public static final String EXTRA_PREVIOUS_STATE = - "android.bluetooth.intent.extra.PREVIOUS_STATE"; + "android.bluetooth.adapter.extra.PREVIOUS_STATE"; /** * Indicates the local Bluetooth adapter is off. */ - public static final int STATE_OFF = 40; + public static final int STATE_OFF = 10; /** * Indicates the local Bluetooth adapter is turning on. However local * clients should wait for {@link #STATE_ON} before attempting to * use the adapter. */ - public static final int STATE_TURNING_ON = 41; + public static final int STATE_TURNING_ON = 11; /** * Indicates the local Bluetooth adapter is on, and ready for use. */ - public static final int STATE_ON = 42; + public static final int STATE_ON = 12; /** * Indicates the local Bluetooth adapter is turning off. Local clients * should immediately attempt graceful disconnection of any remote links. */ - public static final int STATE_TURNING_OFF = 43; + public static final int STATE_TURNING_OFF = 13; /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. - *

Contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link + *

Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes * respectively. *

Requires {@link android.Manifest.permission#BLUETOOTH} */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCAN_MODE_CHANGED = - "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; + "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} @@ -124,7 +122,7 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, */ - public static final String EXTRA_SCAN_MODE = "android.bluetooth.intent.extra.SCAN_MODE"; + public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE"; /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the previous scan mode. Possible values are: @@ -133,37 +131,73 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, */ public static final String EXTRA_PREVIOUS_SCAN_MODE = - "android.bluetooth.intent.extra.PREVIOUS_SCAN_MODE"; + "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; /** * Indicates that both inquiry scan and page scan are disabled on the local * Bluetooth adapter. Therefore this device is neither discoverable * nor connectable from remote Bluetooth devices. */ - public static final int SCAN_MODE_NONE = 50; + public static final int SCAN_MODE_NONE = 20; /** * Indicates that inquiry scan is disabled, but page scan is enabled on the * local Bluetooth adapter. Therefore this device is not discoverable from * remote Bluetooth devices, but is connectable from remote devices that * have previously discovered this device. */ - public static final int SCAN_MODE_CONNECTABLE = 51; + public static final int SCAN_MODE_CONNECTABLE = 21; /** * Indicates that both inquiry scan and page scan are enabled on the local * Bluetooth adapter. Therefore this device is both discoverable and * connectable from remote Bluetooth devices. */ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 53; + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; + + + /** + * Broadcast Action: The local Bluetooth adapter has started the remote + * device discovery process. + *

This usually involves an inquiry scan of about 12 seconds, followed + * by a page scan of each new device to retrieve its Bluetooth name. + *

Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as + * remote Bluetooth devices are found. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISCOVERY_STARTED = + "android.bluetooth.adapter.action.DISCOVERY_STARTED"; + /** + * Broadcast Action: The local Bluetooth adapter has finished the device + * discovery process. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISCOVERY_FINISHED = + "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; + + /** + * Broadcast Action: The local Bluetooth adapter has changed its friendly + * Bluetooth name. + *

This name is visible to remote Bluetooth devices. + *

Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing + * the name. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LOCAL_NAME_CHANGED = + "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; + /** + * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED} + * intents to request the local Bluetooth name. + */ + public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; - /** The user will be prompted to enter a pin - * @hide */ - public static final int PAIRING_VARIANT_PIN = 0; - /** The user will be prompted to enter a passkey - * @hide */ - public static final int PAIRING_VARIANT_PASSKEY = 1; - /** The user will be prompted to confirm the passkey displayed on the screen - * @hide */ - public static final int PAIRING_VARIANT_CONFIRMATION = 2; + private static final int ADDRESS_LENGTH = 17; private final IBluetooth mService; @@ -182,7 +216,8 @@ public final class BluetoothAdapter { * Get a {@link BluetoothDevice} object for the given Bluetooth hardware * address. *

Valid Bluetooth hardware addresses must be upper case, in a format - * such as "00:11:22:33:AA:BB". + * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is + * available to validate a Bluetooth address. *

A {@link BluetoothDevice} will always be returned for a valid * hardware address, even if this adapter has never seen that device. * @@ -380,7 +415,29 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} } - /** @hide */ + /** + * Start the remote device discovery process. + *

The discovery process usually involves an inquiry scan of about 12 + * seconds, followed by a page scan of each new device to retrieve its + * Bluetooth name. + *

This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_DISCOVERY_STARTED} and {@link + * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the + * discovery starts and completes. Register for {@link + * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices + * are found. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Device discovery will only find remote devices that are currently + * discoverable (inquiry scan enabled). Many Bluetooth devices are + * not discoverable by default, and need to be entered into a special mode. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @return true on success, false on error + */ public boolean startDiscovery() { try { return mService.startDiscovery(); @@ -388,14 +445,33 @@ public final class BluetoothAdapter { return false; } - /** @hide */ - public void cancelDiscovery() { + /** + * Cancel the current device discovery process. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @return true on success, false on error + */ + public boolean cancelDiscovery() { try { mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; } - /** @hide */ + /** + * Return true if the local Bluetooth adapter is currently in the device + * discovery process. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Applications can also register for {@link #ACTION_DISCOVERY_STARTED} + * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery + * starts or completes. + * + * @return true if discovering + */ public boolean isDiscovering() { try { return mService.isDiscovering(); @@ -404,27 +480,10 @@ public final class BluetoothAdapter { } /** - * List remote devices that are bonded (paired) to the local adapter. - * - * Bonding (pairing) is the process by which the user enters a pin code for - * the device, which generates a shared link key, allowing for - * authentication and encryption of future connections. In Android we - * require bonding before RFCOMM or SCO connections can be made to a remote - * device. + * Return the set of {@link BluetoothDevice} objects that are bonded + * (paired) to the local adapter. * - * This function lists which remote devices we have a link key for. It does - * not cause any RF transmission, and does not check if the remote device - * still has it's link key with us. If the other side no longer has its - * link key then the RFCOMM or SCO connection attempt will result in an - * error. - * - * This function does not check if the remote device is in range. - * - * Remote devices that have an in-progress bonding attempt are not - * returned. - * - * @return unmodifiable set of bonded devices, or null on error - * @hide + * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ public Set getBondedDevices() { try { @@ -511,4 +570,33 @@ public final class BluetoothAdapter { } return Collections.unmodifiableSet(devices); } + + /** + * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + * + * @param address Bluetooth address as string + * @return true if the address is valid, false otherwise + */ + public static boolean checkBluetoothAddress(String address) { + if (address == null || address.length() != ADDRESS_LENGTH) { + return false; + } + for (int i = 0; i < ADDRESS_LENGTH; i++) { + char c = address.charAt(i); + switch (i % 3) { + case 0: + case 1: + if (Character.digit(c, 16) != -1) { + break; // hex character, OK + } + return false; + case 2: + if (c == ':') { + break; // OK + } + return false; + } + } + return true; + } } diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 0061f10d453..1fbbf789464 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,43 +16,89 @@ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; + /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Static helper methods and constants to decode the device class bit vector - * returned by the Bluetooth API. + * Represents a Bluetooth class. * - * The Android Bluetooth API returns a 32-bit integer to represent the class. - * The format of these bits is defined at + *

Bluetooth Class is a 32 bit field. The format of these bits is defined at * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class provides static helper methods and constants to - * determine what Service Class(es) and Device Class are encoded in the 32-bit - * class. + * (login required). This class contains that 32 bit field, and provides + * constants and methods to determine which Service Class(es) and Device Class + * are encoded in that field. * - * Devices typically have zero or more service classes, and exactly one device - * class. The device class is encoded as a major and minor device class, the - * minor being a subset of the major. + *

Every Bluetooth Class is composed of zero or more service classes, and + * exactly one device class. The device class is further broken down into major + * and minor device class components. * - * Class is useful to describe a device (for example to show an icon), - * but does not reliably describe what profiles a device supports. To determine - * profile support you usually need to perform SDP queries. + *

Class is useful as a hint to roughly describe a device (for example to + * show an icon in the UI), but does not reliably describe which Bluetooth + * profiles or services are actually supported by a device. Accurate service + * discovery is done through SDP requests. * - * Each of these helper methods takes the 32-bit integer class as an argument. - * - * @hide + *

Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for + * a remote device. */ -public class BluetoothClass { - /** Indicates the Bluetooth API could not retrieve the class */ +public final class BluetoothClass implements Parcelable { + /** + * Legacy error value. Applications should use null instead. + * @hide + */ public static final int ERROR = 0xFF000000; - public static final int PROFILE_HEADSET = 0; - public static final int PROFILE_A2DP = 1; - public static final int PROFILE_OPP = 2; + private final int mClass; + + /** @hide */ + public BluetoothClass(int classInt) { + mClass = classInt; + } - /** Every Bluetooth device has zero or more service classes */ - public static class Service { - public static final int BITMASK = 0xFFE000; + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothClass) { + return mClass == ((BluetoothClass)o).mClass; + } + return false; + } + + @Override + public int hashCode() { + return mClass; + } + + @Override + public String toString() { + return Integer.toHexString(mClass); + } + + /** @hide */ + public int describeContents() { + return 0; + } + + /** @hide */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothClass createFromParcel(Parcel in) { + return new BluetoothClass(in.readInt()); + } + public BluetoothClass[] newArray(int size) { + return new BluetoothClass[size]; + } + }; + + /** @hide */ + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mClass); + } + + /** + * Bluetooth service classes. + *

Each {@link BluetoothClass} encodes zero or more service classes. + */ + public static final class Service { + private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; public static final int POSITIONING = 0x010000; @@ -63,32 +109,35 @@ public class BluetoothClass { public static final int AUDIO = 0x200000; public static final int TELEPHONY = 0x400000; public static final int INFORMATION = 0x800000; + } - /** Returns true if the given class supports the given Service Class. - * A bluetooth device can claim to support zero or more service classes. - * @param btClass The bluetooth class. - * @param serviceClass The service class constant to test for. For - * example, Service.AUDIO. Must be one of the - * Service.FOO constants. - * @return True if the service class is supported. - */ - public static boolean hasService(int btClass, int serviceClass) { - if (btClass == ERROR) { - return false; - } - return ((btClass & Service.BITMASK & serviceClass) != 0); - } + /** + * Return true if the specified service class is supported by this class. + *

Valid service classes are the public constants in + * {@link BluetoothClass.Service}. For example, {@link + * BluetoothClass.Service#AUDIO}. + * + * @param service valid service class + * @return true if the service class is supported + */ + public boolean hasService(int service) { + return ((mClass & Service.BITMASK & service) != 0); } - /** Every Bluetooth device has exactly one device class, comprimised of - * major and minor components. We have not included the minor classes for - * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work - * a little differently. */ + /** + * Bluetooth device classes. + *

Each {@link BluetoothClass} encodes exactly one device class, with + * major and minor components. + *

The constants in {@link + * BluetoothClass.Device} represent a combination of major and minor + * components (the complete device class). The constants in {@link + * BluetoothClass.Device.Major} represent just the major device classes. + */ public static class Device { - public static final int BITMASK = 0x1FFC; + private static final int BITMASK = 0x1FFC; public static class Major { - public static final int BITMASK = 0x1F00; + private static final int BITMASK = 0x1F00; public static final int MISC = 0x0000; public static final int COMPUTER = 0x0100; @@ -101,18 +150,6 @@ public class BluetoothClass { public static final int TOY = 0x0800; public static final int HEALTH = 0x0900; public static final int UNCATEGORIZED = 0x1F00; - - /** Returns the Major Device Class component of a bluetooth class. - * Values returned from this function can be compared with the constants - * Device.Major.FOO. A bluetooth device can only be associated - * with one major class. - */ - public static int getDeviceMajor(int btClass) { - if (btClass == ERROR) { - return ERROR; - } - return (btClass & Device.Major.BITMASK); - } } // Devices in the COMPUTER major class @@ -178,42 +215,62 @@ public class BluetoothClass { public static final int HEALTH_PULSE_OXIMETER = 0x0914; public static final int HEALTH_PULSE_RATE = 0x0918; public static final int HEALTH_DATA_DISPLAY = 0x091C; + } - /** Returns the Device Class component of a bluetooth class. This includes - * both the major and minor device components. Values returned from this - * function can be compared with the constants Device.FOO. A bluetooth - * device can only be associated with one device class. - */ - public static int getDevice(int btClass) { - if (btClass == ERROR) { - return ERROR; - } - return (btClass & Device.BITMASK); - } + /** + * Return the major device class component of this Bluetooth class. + *

Values returned from this function can be compared with the + * public constants in {@link BluetoothClass.Device.Major} to determine + * which major class is encoded in this Bluetooth class. + * + * @return major device class component + */ + public int getMajorDeviceClass() { + return (mClass & Device.Major.BITMASK); + } + + /** + * Return the (major and minor) device class component of this + * {@link BluetoothClass}. + *

Values returned from this function can be compared with the + * public constants in {@link BluetoothClass.Device} to determine which + * device class is encoded in this Bluetooth class. + * + * @return device class component + */ + public int getDeviceClass() { + return (mClass & Device.BITMASK); } + /** @hide */ + public static final int PROFILE_HEADSET = 0; + /** @hide */ + public static final int PROFILE_A2DP = 1; + /** @hide */ + public static final int PROFILE_OPP = 2; + /** * Check class bits for possible bluetooth profile support. * This is a simple heuristic that tries to guess if a device with the * given class bits might support specified profile. It is not accurate for all * devices. It tries to err on the side of false positives. - * @param btClass The class * @param profile The profile to be checked * @return True if this device might support specified profile. + * @hide */ - public static boolean doesClassMatch(int btClass, int profile) { + public boolean doesClassMatch(int profile) { if (profile == PROFILE_A2DP) { - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + if (hasService(Service.RENDER)) { return true; } // By the A2DP spec, sinks must indicate the RENDER service. // However we found some that do not (Chordette). So lets also // match on some other class bits. - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: - case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_HEADPHONES: + case Device.AUDIO_VIDEO_LOUDSPEAKER: + case Device.AUDIO_VIDEO_CAR_AUDIO: return true; default: return false; @@ -221,37 +278,37 @@ public class BluetoothClass { } else if (profile == PROFILE_HEADSET) { // The render service class is required by the spec for HFP, so is a // pretty good signal - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { + if (hasService(Service.RENDER)) { return true; } // Just in case they forgot the render service class - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HANDSFREE: + case Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case Device.AUDIO_VIDEO_CAR_AUDIO: return true; default: return false; } } else if (profile == PROFILE_OPP) { - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.OBJECT_TRANSFER)) { + if (hasService(Service.OBJECT_TRANSFER)) { return true; } - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - case BluetoothClass.Device.COMPUTER_DESKTOP: - case BluetoothClass.Device.COMPUTER_SERVER: - case BluetoothClass.Device.COMPUTER_LAPTOP: - case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: - case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: - case BluetoothClass.Device.COMPUTER_WEARABLE: - case BluetoothClass.Device.PHONE_UNCATEGORIZED: - case BluetoothClass.Device.PHONE_CELLULAR: - case BluetoothClass.Device.PHONE_CORDLESS: - case BluetoothClass.Device.PHONE_SMART: - case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: - case BluetoothClass.Device.PHONE_ISDN: + switch (getDeviceClass()) { + case Device.COMPUTER_UNCATEGORIZED: + case Device.COMPUTER_DESKTOP: + case Device.COMPUTER_SERVER: + case Device.COMPUTER_LAPTOP: + case Device.COMPUTER_HANDHELD_PC_PDA: + case Device.COMPUTER_PALM_SIZE_PC_PDA: + case Device.COMPUTER_WEARABLE: + case Device.PHONE_UNCATEGORIZED: + case Device.PHONE_CELLULAR: + case Device.PHONE_CORDLESS: + case Device.PHONE_SMART: + case Device.PHONE_MODEM_OR_GATEWAY: + case Device.PHONE_ISDN: return true; default: return false; @@ -261,4 +318,3 @@ public class BluetoothClass { } } } - diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1ab4389b435..a9cec5033b6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.IBinder; import android.os.Parcel; @@ -38,8 +40,6 @@ import java.io.UnsupportedEncodingException; * are performed on the remote Bluetooth hardware address, using the * {@link BluetoothAdapter} that was used to create this {@link * BluetoothDevice}. - * - * TODO: unhide more of this class */ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; @@ -48,31 +48,203 @@ public final class BluetoothDevice implements Parcelable { * Sentinel error value for this class. Guaranteed to not equal any other * integer constant in this class. Provided as a convenience for functions * that require a sentinel error value, for example: - *

Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, - * BluetoothAdapter.ERROR) - */ - public static final int ERROR = -1; - - /** We do not have a link key for the remote device, and are therefore not - * bonded - * @hide*/ - public static final int BOND_NOT_BONDED = 0; - /** We have a link key for the remote device, and are probably bonded. - * @hide */ - public static final int BOND_BONDED = 1; - /** We are currently attempting bonding - * @hide */ - public static final int BOND_BONDING = 2; - - /** Ask device picker to show all kinds of BT devices. - * @hide */ - public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0; - /** Ask device picker to show BT devices that support AUDIO profiles. - * @hide */ - public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1; - /** Ask device picker to show BT devices that support Object Transfer. - * @hide */ - public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2; + *

Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + * BluetoothDevice.ERROR) + */ + public static final int ERROR = Integer.MIN_VALUE; + + /** + * Broadcast Action: Remote device discovered. + *

Sent when a remote device is found during discovery. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or + * {@link #EXTRA_RSSI} if they are available. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + // TODO: Change API to not broadcast RSSI if not available (incoming connection) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_FOUND = + "android.bluetooth.device.action.FOUND"; + + /** + * Broadcast Action: Remote device disappeared. + *

Sent when a remote device that was found in the last discovery is not + * found in the current discovery. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISAPPEARED = + "android.bluetooth.device.action.DISAPPEARED"; + + /** + * Broadcast Action: Bluetooth class of a remote device has changed. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_CLASS}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @see {@link BluetoothClass} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLASS_CHANGED = + "android.bluetooth.device.action.CLASS_CHANGED"; + + /** + * Broadcast Action: Indicates a low level (ACL) connection has been + * established with a remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

ACL connections are managed automatically by the Android Bluetooth + * stack. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_CONNECTED = + "android.bluetooth.device.action.ACL_CONNECTED"; + + /** + * Broadcast Action: Indicates that a low level (ACL) disconnection has + * been requested for a remote device, and it will soon be disconnected. + *

This is useful for graceful disconnection. Applications should use + * this intent as a hint to immediately terminate higher level connections + * (RFCOMM, L2CAP, or profile connections) to the remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_DISCONNECT_REQUESTED = + "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; + + /** + * Broadcast Action: Indicates a low level (ACL) disconnection from a + * remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

ACL connections are managed automatically by the Android Bluetooth + * stack. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_DISCONNECTED = + "android.bluetooth.device.action.ACL_DISCONNECTED"; + + /** + * Broadcast Action: Indicates the friendly name of a remote device has + * been retrieved for the first time, or changed since the last retrieval. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_NAME}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NAME_CHANGED = + "android.bluetooth.device.action.NAME_CHANGED"; + + /** + * Broadcast Action: Indicates a change in the bond state of a remote + * device. For example, if a device is bonded (paired). + *

Always contains the extra fields {@link #EXTRA_DEVICE}, {@link + * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also + // contain a hidden extra field EXTRA_REASON with the result code. + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BOND_STATE_CHANGED = + "android.bluetooth.device.action.BOND_STATE_CHANGED"; + + /** + * Used as a Parcelable {@link BluetoothDevice} extra field in every intent + * broadcast by this class. It contains the {@link BluetoothDevice} that + * the intent applies to. + */ + public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; + + /** + * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link + * #ACTION_FOUND} intents. It contains the friendly Bluetooth name. + */ + public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; + + /** + * Used as an optional short extra field in {@link #ACTION_FOUND} intents. + * Contains the RSSI value of the remote device as reported by the + * Bluetooth hardware. + */ + public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; + + /** + * Used as an Parcelable {@link BluetoothClass} extra field in {@link + * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. + */ + public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; + + /** + * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * Contains the bond state of the remote device. + *

Possible values are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + */ + public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; + /** + * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * Contains the previous bond state of the remote device. + *

Possible values are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + */ + public static final String EXTRA_PREVIOUS_BOND_STATE = + "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; + /** + * Indicates the remote device is not bonded (paired). + *

There is no shared link key with the remote device, so communication + * (if it is allowed at all) will be unauthenticated and unencrypted. + */ + public static final int BOND_NONE = 10; + /** + * Indicates bonding (pairing) is in progress with the remote device. + */ + public static final int BOND_BONDING = 11; + /** + * Indicates the remote device is bonded (paired). + *

A shared link keys exists locally for the remote device, so + * communication can be authenticated and encrypted. + *

Being bonded (paired) with a remote device does not necessarily + * mean the device is currently connected. It just means that the ponding + * procedure was compeleted at some earlier time, and the link key is still + * stored locally, ready to use on the next connection. + * + */ + public static final int BOND_BONDED = 12; + + /** @hide */ + public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; + /** @hide */ + public static final String EXTRA_PAIRING_VARIANT = + "android.bluetooth.device.extra.PAIRING_VARIANT"; + /** @hide */ + public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; + + /** + * Broadcast Action: Indicates a failure to retrieve the name of a remote + * device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + //TODO: is this actually useful? + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NAME_FAILED = + "android.bluetooth.device.action.NAME_FAILED"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PAIRING_REQUEST = + "android.bluetooth.device.action.PAIRING_REQUEST"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PAIRING_CANCEL = + "android.bluetooth.device.action.PAIRING_CANCEL"; /** A bond attempt succeeded * @hide */ @@ -98,7 +270,6 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int UNBOND_REASON_REMOVED = 6; - //TODO: Remove duplicates between here and BluetoothAdapter /** The user will be prompted to enter a pin * @hide */ public static final int PAIRING_VARIANT_PIN = 0; @@ -109,8 +280,6 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int PAIRING_VARIANT_CONFIRMATION = 2; - private static final int ADDRESS_LENGTH = 17; - private static IBluetooth sService; /* Guarenteed constant after first object constructed */ private final String mAddress; @@ -135,7 +304,7 @@ public final class BluetoothDevice implements Parcelable { } } - if (!checkBluetoothAddress(address)) { + if (!BluetoothAdapter.checkBluetoothAddress(address)) { throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); } @@ -216,15 +385,15 @@ public final class BluetoothDevice implements Parcelable { } /** - * Create a bonding with a remote bluetooth device. - * - * This is an asynchronous call. The result of this bonding attempt can be - * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. + * Start the bonding (pairing) process with the remote device. + *

This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when + * the bonding process completes, and its result. + *

Android system services will handle the necessary user interactions + * to confirm and complete the bonding process. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @param address the remote device Bluetooth address. - * @return false If there was an immediate problem creating the bonding, - * true otherwise. - * @hide + * @return false on immediate error, true if bonding will begin */ public boolean createBond() { try { @@ -234,8 +403,10 @@ public final class BluetoothDevice implements Parcelable { } /** - * Cancel an in-progress bonding request started with createBond. - * @hide + * Cancel an in-progress bonding request started with {@link #createBond}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @return true on sucess, false on error */ public boolean cancelBondProcess() { try { @@ -245,12 +416,13 @@ public final class BluetoothDevice implements Parcelable { } /** - * Removes the remote device and the pairing information associated - * with it. + * Remove bond (pairing) with the remote device. + *

Delete the link key associated with the remote device, and + * immediately terminate connections to that device that require + * authentication and encryption. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @return true if the device was disconnected, false otherwise and on - * error. - * @hide + * @return true on sucess, false on error */ public boolean removeBond() { try { @@ -260,21 +432,35 @@ public final class BluetoothDevice implements Parcelable { } /** - * Get the bonding state of a remote device. - * - * Result is one of: - * BOND_* - * ERROR + * Get the bond state of the remote device. + *

Possible values for the bond state are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. * - * @param address Bluetooth hardware address of the remote device to check. - * @return Result code - * @hide + * @return the bond state */ public int getBondState() { try { return sService.getBondState(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothDevice.ERROR; + return BOND_NONE; + } + + /** + * Get the Bluetooth class of the remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return Bluetooth class object, or null on error + */ + public BluetoothClass getBluetoothClass() { + try { + int classInt = sService.getRemoteClass(mAddress); + if (classInt == BluetoothClass.ERROR) return null; + return new BluetoothClass(classInt); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; } /** @@ -304,14 +490,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - public int getBluetoothClass() { - try { - return sService.getRemoteClass(mAddress); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothDevice.ERROR; - } - /** @hide */ public String[] getUuids() { try { @@ -433,28 +611,4 @@ public final class BluetoothDevice implements Parcelable { return pinBytes; } - /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" - * @hide */ - public static boolean checkBluetoothAddress(String address) { - if (address == null || address.length() != ADDRESS_LENGTH) { - return false; - } - for (int i = 0; i < ADDRESS_LENGTH; i++) { - char c = address.charAt(i); - switch (i % 3) { - case 0: - case 1: - if (Character.digit(c, 16) != -1) { - break; // hex character, OK - } - return false; - case 2: - if (c == ':') { - break; // OK - } - return false; - } - } - return true; - } } diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java new file mode 100644 index 00000000000..05eed0e6de6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +/** + * A helper to show a system "Device Picker" activity to the user. + * + * @hide + */ +public interface BluetoothDevicePicker { + public static final String EXTRA_NEED_AUTH = + "android.bluetooth.devicepicker.extra.NEED_AUTH"; + public static final String EXTRA_FILTER_TYPE = + "android.bluetooth.devicepicker.extra.FILTER_TYPE"; + public static final String EXTRA_LAUNCH_PACKAGE = + "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE"; + public static final String EXTRA_LAUNCH_CLASS = + "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS"; + + /** + * Broadcast when one BT device is selected from BT device picker screen. + * Selected BT device address is contained in extra string {@link BluetoothIntent} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEVICE_SELECTED = + "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; + + /** + * Broadcast when someone want to select one BT device from devices list. + * This intent contains below extra data: + * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication + * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be + * listed + * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this + * intent come from + * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent + * come from + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LAUNCH = + "android.bluetooth.devicepicker.action.LAUNCH"; + + /** Ask device picker to show all kinds of BT devices */ + public static final int FILTER_TYPE_ALL = 0; + /** Ask device picker to show BT devices that support AUDIO profiles */ + public static final int FILTER_TYPE_AUDIO = 1; + /** Ask device picker to show BT devices that support Object Transfer */ + public static final int FILTER_TYPE_TRANSFER = 2; +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index d31b6aef3c7..90cff6b8c90 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -54,6 +56,27 @@ public final class BluetoothHeadset { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = false; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STATE_CHANGED = + "android.bluetooth.headset.action.STATE_CHANGED"; + /** + * TODO(API release): Consider incorporating as new state in + * HEADSET_STATE_CHANGED + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AUDIO_STATE_CHANGED = + "android.bluetooth.headset.action.AUDIO_STATE_CHANGED"; + public static final String EXTRA_STATE = + "android.bluetooth.headset.extra.STATE"; + public static final String EXTRA_PREVIOUS_STATE = + "android.bluetooth.headset.extra.PREVIOUS_STATE"; + public static final String EXTRA_AUDIO_STATE = + "android.bluetooth.headset.extra.AUDIO_STATE"; + + /** + * TODO(API release): Consider incorporating as new state in + * HEADSET_STATE_CHANGED + */ private IBluetoothHeadset mService; private final Context mContext; private final ServiceListener mServiceListener; diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java deleted file mode 100644 index 8de19f5f5dc..00000000000 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2008 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.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; - -/** - * Bluetooth API constants. - * - * TODO: Deprecate this class - * @hide - */ -public interface BluetoothIntent { - public static final String DEVICE = - "android.bluetooth.intent.DEVICE"; - public static final String NAME = - "android.bluetooth.intent.NAME"; - public static final String ALIAS = - "android.bluetooth.intent.ALIAS"; - public static final String RSSI = - "android.bluetooth.intent.RSSI"; - public static final String CLASS = - "android.bluetooth.intent.CLASS"; - public static final String HEADSET_STATE = - "android.bluetooth.intent.HEADSET_STATE"; - public static final String HEADSET_PREVIOUS_STATE = - "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; - public static final String HEADSET_AUDIO_STATE = - "android.bluetooth.intent.HEADSET_AUDIO_STATE"; - public static final String BOND_STATE = - "android.bluetooth.intent.BOND_STATE"; - public static final String BOND_PREVIOUS_STATE = - "android.bluetooth.intent.BOND_PREVIOUS_STATE"; - public static final String REASON = - "android.bluetooth.intent.REASON"; - public static final String PAIRING_VARIANT = - "android.bluetooth.intent.PAIRING_VARIANT"; - public static final String PASSKEY = - "android.bluetooth.intent.PASSKEY"; - - public static final String DEVICE_PICKER_NEED_AUTH = - "android.bluetooth.intent.DEVICE_PICKER_NEED_AUTH"; - public static final String DEVICE_PICKER_FILTER_TYPE = - "android.bluetooth.intent.DEVICE_PICKER_FILTER_TYPE"; - public static final String DEVICE_PICKER_LAUNCH_PACKAGE = - "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_PACKAGE"; - public static final String DEVICE_PICKER_LAUNCH_CLASS = - "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_CLASS"; - - /** - * Broadcast when one BT device is selected from BT device picker screen. - * Selected BT device address is contained in extra string "BluetoothIntent.ADDRESS". - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DEVICE_PICKER_DEVICE_SELECTED = - "android.bluetooth.intent.action.DEVICE_SELECTED"; - - /** - * Broadcast when someone want to select one BT device from devices list. - * This intent contains below extra data: - * - BluetoothIntent.DEVICE_PICKER_NEED_AUTH (boolean): if need authentication - * - BluetoothIntent.DEVICE_PICKER_FILTER_TYPE (int): what kinds of device should be listed - * - BluetoothIntent.DEVICE_PICKER_LAUNCH_PACKAGE (string): where(which package) this intent come from - * - BluetoothIntent.DEVICE_PICKER_LAUNCH_CLASS (string): where(which class) this intent come from - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DEVICE_PICKER_DEVICE_PICKER = - "android.bluetooth.intent.action.DEVICE_PICKER"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String NAME_CHANGED_ACTION = - "android.bluetooth.intent.action.NAME_CHANGED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_STARTED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_STARTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_COMPLETED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_COMPLETED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_REQUEST_ACTION = - "android.bluetooth.intent.action.PAIRING_REQUEST"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_CANCEL_ACTION = - "android.bluetooth.intent.action.PAIRING_CANCEL"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_FOUND_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISAPPEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_UPDATED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_FAILED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - - /** - * Broadcast when the bond state of a remote device changes. - * Has string extra ADDRESS and int extras BOND_STATE and - * BOND_PREVIOUS_STATE. - * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will - * also have an int extra REASON with a value of: - * BluetoothDevice.BOND_RESULT_* - * */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BOND_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; - - /** - * TODO(API release): Move into BluetoothHeadset - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String HEADSET_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; - - /** - * TODO(API release): Consider incorporating as new state in - * HEADSET_STATE_CHANGED - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String HEADSET_AUDIO_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.HEADSET_ADUIO_STATE_CHANGED"; -} diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 645e24145c5..b48f48e9f19 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -221,9 +221,9 @@ public class BluetoothPbap { * devices. It tries to err on the side of false positives. * @return True if this device might support PBAP. */ - public static boolean doesClassMatchSink(int btClass) { + public static boolean doesClassMatchSink(BluetoothClass btClass) { // TODO optimize the rule - switch (BluetoothClass.Device.getDevice(btClass)) { + switch (btClass.getDeviceClass()) { case BluetoothClass.Device.COMPUTER_DESKTOP: case BluetoothClass.Device.COMPUTER_LAPTOP: case BluetoothClass.Device.COMPUTER_SERVER: -- GitLab From 7cf4dddd1866c64ecd949773abcb6f9def9ef832 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 10 Sep 2009 22:00:05 -0700 Subject: [PATCH 0051/1408] Changes for BT 2.1 1) Handle incoming 2.1 pairing requests 2) Modify displaying error messages on bond failures. 3) Add delay while accepting incoming pairing for certain 2.1 devices. When MITM is on, the link key request might come more than once. Auto accept with a delay. 4) Handle DisplayPasskey callback for pairing a 2.1 keyboard with a 2.1 device --- .../android/bluetooth/BluetoothDevice.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a9cec5033b6..b1861acb708 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -250,7 +250,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int BOND_SUCCESS = 0; /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time + * not respond to pin request in time * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; /** A bond attempt failed because the other side explicilty rejected @@ -266,9 +266,15 @@ public final class BluetoothDevice implements Parcelable { /** A bond attempt failed because a discovery is in progress * @hide */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + /** A bond attempt failed because of authentication timeout + * @hide */ + public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; + /** A bond attempt failed because of repeated attempts + * @hide */ + public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** An existing bond was explicitly revoked * @hide */ - public static final int UNBOND_REASON_REMOVED = 6; + public static final int UNBOND_REASON_REMOVED = 8; /** The user will be prompted to enter a pin * @hide */ @@ -278,7 +284,13 @@ public final class BluetoothDevice implements Parcelable { public static final int PAIRING_VARIANT_PASSKEY = 1; /** The user will be prompted to confirm the passkey displayed on the screen * @hide */ - public static final int PAIRING_VARIANT_CONFIRMATION = 2; + public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; + /** The user will be prompted to accept or deny the incoming pairing request + * @hide */ + public static final int PAIRING_VARIANT_CONSENT = 3; + /** The user will be prompted to enter the passkey displayed on remote device + * @hide */ + public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; private static IBluetooth sService; /* Guarenteed constant after first object constructed */ -- GitLab From ece220015d30a083133e52e52d6d536bc79e465e Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 15 Sep 2009 16:27:38 -0700 Subject: [PATCH 0052/1408] Add a parcelable version of UUID file. Change-Id:I2582e1c9893292ba35bb5cac90ccd2bd18041d8c --- .../java/android/bluetooth/ParcelUuid.aidl | 19 +++ .../java/android/bluetooth/ParcelUuid.java | 135 ++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 framework/java/android/bluetooth/ParcelUuid.aidl create mode 100644 framework/java/android/bluetooth/ParcelUuid.java diff --git a/framework/java/android/bluetooth/ParcelUuid.aidl b/framework/java/android/bluetooth/ParcelUuid.aidl new file mode 100644 index 00000000000..70bcc4b0db9 --- /dev/null +++ b/framework/java/android/bluetooth/ParcelUuid.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2009, 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; + +parcelable ParcelUuid; diff --git a/framework/java/android/bluetooth/ParcelUuid.java b/framework/java/android/bluetooth/ParcelUuid.java new file mode 100644 index 00000000000..27166a0d679 --- /dev/null +++ b/framework/java/android/bluetooth/ParcelUuid.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 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.os.Parcel; +import android.os.Parcelable; + +import java.util.UUID; + +/** + * This class is a Parcelable wrapper around {@link UUID} which is an + * immutable representation of a 128-bit universally unique + * identifier. + */ +public final class ParcelUuid implements Parcelable { + + private final UUID mUuid; + + /** + * Constructor creates a ParcelUuid instance from the + * given {@link UUID}. + * + * @param uuid UUID + */ + public ParcelUuid(UUID uuid) { + mUuid = uuid; + } + + /** + * Creates a new ParcelUuid from a string representation of {@link UUID}. + * + * @param uuid + * the UUID string to parse. + * @return an ParcelUuid instance. + * @throws NullPointerException + * if {@code uuid} is {@code null}. + * @throws IllegalArgumentException + * if {@code uuid} is not formatted correctly. + */ + public static ParcelUuid fromString(String uuid) { + return new ParcelUuid(UUID.fromString(uuid)); + } + + /** + * Get the {@link UUID} represented by the ParcelUuid. + * + * @return UUID contained in the ParcelUuid. + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns a string representation of the ParcelUuid + * For example: 0000110B-0000-1000-8000-00805F9B34FB will be the return value. + * + * @return a String instance. + */ + @Override + public String toString() { + return mUuid.toString(); + } + + + @Override + public int hashCode() { + return mUuid.hashCode(); + } + + /** + * Compares this ParcelUuid to another object for equality. If {@code object} + * is not {@code null}, is a ParcelUuid instance, and all bits are equal, then + * {@code true} is returned. + * + * @param object + * the {@code Object} to compare to. + * @return {@code true} if this ParcelUuid is equal to {@code object} + * or {@code false} if not. + */ + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } + + if (this == object) { + return true; + } + + if (!(object instanceof ParcelUuid)) { + return false; + } + + ParcelUuid that = (ParcelUuid) object; + + return (this.mUuid.equals(that.mUuid)); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public ParcelUuid createFromParcel(Parcel source) { + long mostSigBits = source.readLong(); + long leastSigBits = source.readLong(); + UUID uuid = new UUID(mostSigBits, leastSigBits); + return new ParcelUuid(uuid); + } + + public ParcelUuid[] newArray(int size) { + return new ParcelUuid[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mUuid.getMostSignificantBits()); + dest.writeLong(mUuid.getLeastSignificantBits()); + } +} -- GitLab From 55a0c0380ff13ec02ae83f4cef32674018942208 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 16 Sep 2009 12:30:02 -0700 Subject: [PATCH 0053/1408] Change handling of remoteUuids. Use the ParcelUuid instead of UUID Change-Id:Ie05d65a62e8a4df8182a4c737d46c14142bfec43 --- .../android/bluetooth/BluetoothDevice.java | 4 +- .../java/android/bluetooth/BluetoothUuid.java | 96 +++++++++++++++---- .../java/android/bluetooth/IBluetooth.aidl | 6 +- 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index b1861acb708..0b3f3c7b3a2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -503,7 +503,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ - public String[] getUuids() { + public ParcelUuid[] getUuids() { try { return sService.getRemoteUuids(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -511,7 +511,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ - public int getServiceChannel(String uuid) { + public int getServiceChannel(ParcelUuid uuid) { try { return sService.getRemoteServiceChannel(mAddress, uuid); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index c15bc20bfb0..409c744111d 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -16,10 +16,11 @@ package android.bluetooth; -import java.util.UUID; +import java.util.Arrays; +import java.util.HashSet; /** -* Static helper methods and constants to decode the UUID of remote devices. +* Static helper methods and constants to decode the ParcelUuid of remote devices. * @hide */ public final class BluetoothUuid { @@ -30,40 +31,99 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - public static final UUID AudioSink = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - public static final UUID AudioSource = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); - public static final UUID Handsfree = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB"); - public static final UUID AvrcpController = - UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB"); - public static final UUID AvrcpTarget = UUID.fromString("0000110C-0000-1000-8000-00805F9B34FB"); - - public static boolean isAudioSource(UUID uuid) { + public static final ParcelUuid AudioSink = + ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AudioSource = + ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AdvAudioDist = + ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid HSP = + ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid Handsfree = + ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpController = + ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpTarget = + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid ObexObjectPush = + ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + + public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } - public static boolean isAudioSink(UUID uuid) { + public static boolean isAudioSink(ParcelUuid uuid) { return uuid.equals(AudioSink); } - public static boolean isAdvAudioDist(UUID uuid) { + public static boolean isAdvAudioDist(ParcelUuid uuid) { return uuid.equals(AdvAudioDist); } - public static boolean isHandsfree(UUID uuid) { + public static boolean isHandsfree(ParcelUuid uuid) { return uuid.equals(Handsfree); } - public static boolean isHeadset(UUID uuid) { + public static boolean isHeadset(ParcelUuid uuid) { return uuid.equals(HSP); } - public static boolean isAvrcpController(UUID uuid) { + public static boolean isAvrcpController(ParcelUuid uuid) { return uuid.equals(AvrcpController); } - public static boolean isAvrcpTarget(UUID uuid) { + public static boolean isAvrcpTarget(ParcelUuid uuid) { return uuid.equals(AvrcpTarget); } + + /** + * Returns true if ParcelUuid is present in uuidArray + * + * @param uuidArray - Array of ParcelUuids + * @param uuid + */ + public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { + for (ParcelUuid element: uuidArray) { + if (element.equals(uuid)) return true; + } + return false; + } + + /** + * Returns true if there any common ParcelUuids in uuidA and uuidB. + * + * @param uuidA - List of ParcelUuids + * @param uuidB - List of ParcelUuids + * + */ + public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + if (uuidA == null || uuidB == null) return false; + + HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (uuidSet.contains(uuid)) return true; + } + return false; + } + + /** + * Returns true if all the ParcelUuids in ParcelUuidB are present in + * ParcelUuidA + * + * @param uuidA - Array of ParcelUuidsA + * @param uuidB - Array of ParcelUuidsB + * + */ + public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + if (uuidA == null || uuidB == null) return false; + + HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (!uuidSet.contains(uuid)) return false; + } + return true; + } + } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index a11ceac0983..04c8ec9545b 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.ParcelUuid; + /** * System private API for talking with the Bluetooth service. * @@ -50,8 +52,8 @@ interface IBluetooth String getRemoteName(in String address); int getRemoteClass(in String address); - String[] getRemoteUuids(in String address); - int getRemoteServiceChannel(in String address, String uuid); + ParcelUuid[] getRemoteUuids(in String address); + int getRemoteServiceChannel(in String address,in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); boolean setPasskey(in String address, int passkey); -- GitLab From 97c84ce71b56cb4b803f084ffa8534ef435f8df6 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 16 Sep 2009 17:50:52 -0700 Subject: [PATCH 0054/1408] Make ParcelUuid helper functions consistent. Treat zero length arrays and null arrays to be same. Change-Id: I8c6c28e5dc3da1f31f6f6abfc747db4c2975a90b --- .../java/android/bluetooth/BluetoothUuid.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 409c744111d..0596b21b03c 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -98,7 +98,14 @@ public final class BluetoothUuid { */ public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; - if (uuidA == null || uuidB == null) return false; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) { + return uuidA.length == 0 ? true : false; + } HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); for (ParcelUuid uuid: uuidB) { @@ -117,7 +124,12 @@ public final class BluetoothUuid { */ public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; - if (uuidA == null || uuidB == null) return false; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) return true; HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); for (ParcelUuid uuid: uuidB) { -- GitLab From f1288ced9e09172825f63644b1b548420f3ac7ea Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 16 Sep 2009 16:50:11 -0700 Subject: [PATCH 0055/1408] Do not @hide Parcelable implementations in BluetoothClass and BluetoothDevice. Change-Id: I92389c53a9b99c9507f78898329ff87b631c7aa3 --- framework/java/android/bluetooth/BluetoothClass.java | 3 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 1fbbf789464..6210380595a 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -72,12 +72,10 @@ public final class BluetoothClass implements Parcelable { return Integer.toHexString(mClass); } - /** @hide */ public int describeContents() { return 0; } - /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothClass createFromParcel(Parcel in) { @@ -88,7 +86,6 @@ public final class BluetoothClass implements Parcelable { } }; - /** @hide */ public void writeToParcel(Parcel out, int flags) { out.writeInt(mClass); } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0b3f3c7b3a2..f81ba731ef0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -349,12 +349,10 @@ public final class BluetoothDevice implements Parcelable { return mAddress; } - /** @hide */ public int describeContents() { return 0; } - /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothDevice createFromParcel(Parcel in) { @@ -365,7 +363,6 @@ public final class BluetoothDevice implements Parcelable { } }; - /** @hide */ public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); } -- GitLab From 51eae29605b1be4b089eed344784a46cb9895f67 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 18 Sep 2009 10:17:41 -0700 Subject: [PATCH 0056/1408] Add one line of logging when SCO SOCKET connect starts. This is to help collect information from Moto QA about delays connecting BT audio. Change-Id: I790c65f5b64c85aaffc0e68ebe8b6202f476b39f http://b/2129464 --- framework/java/android/bluetooth/ScoSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java index 1bf786ffd3a..116310ad9d7 100644 --- a/framework/java/android/bluetooth/ScoSocket.java +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -87,7 +87,7 @@ public class ScoSocket { * Does not block. */ public synchronized boolean connect(String address) { - if (VDBG) log("connect() " + this); + if (DBG) log("connect() " + this); if (mState != STATE_READY) { if (DBG) log("connect(): Bad state"); return false; -- GitLab From 6ef935c90c0988645d33529f4ddbe058977c765c Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 18 Sep 2009 11:37:06 -0700 Subject: [PATCH 0057/1408] Reject lowercase characters in checkBluetoothAddress(). This keeps consistency with Bluez which uses upper case string address. It's important to keep the case the same so that .equals() in BluetoothService.java work. Change-Id: I6404ca137d0aec3cc2e6e7cb79763d5305a03547 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 96a927b30cb..1fc22fe524d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -573,6 +573,7 @@ public final class BluetoothAdapter { /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + *

Alphabetic characters must be uppercase to be valid. * * @param address Bluetooth address as string * @return true if the address is valid, false otherwise @@ -586,8 +587,9 @@ public final class BluetoothAdapter { switch (i % 3) { case 0: case 1: - if (Character.digit(c, 16) != -1) { - break; // hex character, OK + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + // hex character, OK + break; } return false; case 2: -- GitLab From 6102eb028ab191da360093f1f0526683d8a91c94 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 18 Sep 2009 11:32:54 -0700 Subject: [PATCH 0058/1408] Add new API for fetching UUIDs using SDP. Add new API which clients can use to force an SDP query. The result is broadcast using an intent having the UUIDs. The intent is broadcast after a timeout, in case of an error. This timeout is greater than the page timeout. Change-Id: I61e6db4c05b34c42f679a66987e37e2063a793b6 --- .../android/bluetooth/BluetoothDevice.java | 45 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 46 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f81ba731ef0..b52a82260aa 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.util.Log; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.UUID; /** * Represents a remote Bluetooth device. @@ -225,6 +226,20 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; + /** + * Broadcast Action: This intent is used to broadcast the {@link UUID} + * wrapped as a {@link ParcelUuid} of the remote device after it has been + * fetched. This intent is sent only when the UUIDs of the remote device + * are requested to be fetched using Service Discovery Protocol + *

Always contains the extra field {@link #EXTRA_DEVICE} + *

Always contains the extra filed {@link #EXTRA_UUID} + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_UUID = + "android.bleutooth.device.action.UUID"; + /** * Broadcast Action: Indicates a failure to retrieve the name of a remote * device. @@ -292,6 +307,15 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; + /** + * Used as an extra field in {@link #ACTION_UUID} intents, + * Contains the {@link ParcelUuid}s of the remote device which is a parcelable + * version of {@link UUID}. + * @hide + */ + public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + + private static IBluetooth sService; /* Guarenteed constant after first object constructed */ private final String mAddress; @@ -507,6 +531,27 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** + * Perform a SDP query on the remote device to get the UUIDs + * supported. This API is asynchronous and an Intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * an Intent is sent with the UUIDs that is currently present in the + * cache. Clients should use the {@link getUuids} to get UUIDs + * is SDP is not to be performed. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + * @hide + */ + public boolean fetchUuidsWithSdp() { + try { + return sService.fetchRemoteUuidsWithSdp(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** @hide */ public int getServiceChannel(ParcelUuid uuid) { try { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 04c8ec9545b..203a61d8a1f 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -53,6 +53,7 @@ interface IBluetooth String getRemoteName(in String address); int getRemoteClass(in String address); ParcelUuid[] getRemoteUuids(in String address); + boolean fetchRemoteUuidsWithSdp(in String address); int getRemoteServiceChannel(in String address,in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); -- GitLab From 391e8a7d5df5ffd75bf84870c5facf31c02b648e Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 21 Sep 2009 12:48:51 -0700 Subject: [PATCH 0059/1408] Cache the remote device's service channel. Bluez Device implementation is such that when a device is unpaired, we removes the device and hence there is no way to interact with it unless you pair again. Remote service channel call is used to get the rfcomm channel number which will be used in profiles like OPP which don't require pairing. Change-Id: I868a6cdfdb1b7d3591dd8b66cd0320f41a9c1b92 --- framework/java/android/bluetooth/BluetoothUuid.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 0596b21b03c..24ad06a997a 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -83,6 +83,12 @@ public final class BluetoothUuid { * @param uuid */ public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { + if ((uuidArray == null || uuidArray.length == 0) && uuid == null) + return true; + + if (uuidArray == null) + return false; + for (ParcelUuid element: uuidArray) { if (element.equals(uuid)) return true; } -- GitLab From dc7043578c102cf719049414eb3b81c847d8f00c Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 24 Sep 2009 11:14:15 -0700 Subject: [PATCH 0060/1408] Bluetooth API: Do not allow apps to programmatically make BT discoverable. Instead add ACTION_REQUEST_DISCOVERABLE for the system to show a dialog to adjust discoverable mode. Also remove createBond(), removeBond() and cancelBondProcess(). The Settings App already handles these automatically when connections require bonding. Change-Id: I216154cd1b6de410de64ba91b07d7263ac03e8df --- .../android/bluetooth/BluetoothAdapter.java | 39 ++++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 3 ++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1fc22fe524d..6aec52c0d4c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -103,6 +103,38 @@ public final class BluetoothAdapter { */ public static final int STATE_TURNING_OFF = 13; + /** + * Activity Action: Show a system activity that requests discoverable mode. + *

Discoverable mode is equivalent to {@link + * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see + * this Bluetooth adapter when they perform a discovery. + *

For privacy, Android is not by default discoverable. + *

The sender can optionally use extra field {@link + * #EXTRA_DISCOVERABLE_DURATION} to request the duration of + * discoverability. Currently the default duration is 120 seconds, and + * maximum duration is capped at 300 seconds for each request. + *

Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be the duration (in seconds) of discoverability, or a negative + * value if the user rejected discoverability. + *

Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} + * for global notification whenever the scan mode changes. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISCOVERABLE = + "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; + + /** + * Used as an optional int extra field in {@link + * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration + * for discoverability in seconds. The current default is 120 seconds, and + * requests over 300 seconds will be capped. These values could change. + */ + public static final String EXTRA_DISCOVERABLE_DURATION = + "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. @@ -388,10 +420,15 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. - *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} + *

Applications cannot set the scan mode. They should use + * startActivityForResult( + * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) + * instead. * * @param mode valid scan mode * @return true if the scan mode was set, false otherwise + * @hide */ public boolean setScanMode(int mode) { try { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index b52a82260aa..c714f692a1c 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -427,6 +427,7 @@ public final class BluetoothDevice implements Parcelable { *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return false on immediate error, true if bonding will begin + * @hide */ public boolean createBond() { try { @@ -440,6 +441,7 @@ public final class BluetoothDevice implements Parcelable { *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on sucess, false on error + * @hide */ public boolean cancelBondProcess() { try { @@ -456,6 +458,7 @@ public final class BluetoothDevice implements Parcelable { *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on sucess, false on error + * @hide */ public boolean removeBond() { try { -- GitLab From 8946f239c4b2bc28191f5c61334ff026b574ee27 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 25 Sep 2009 16:31:39 +0400 Subject: [PATCH 0061/1408] Fix typo in Bluetooth docs. --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6aec52c0d4c..776c923c0b2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -297,7 +297,7 @@ public final class BluetoothAdapter { * Turn on the local Bluetooth adapter. *

This powers on the underlying Bluetooth hardware, and starts all * Bluetooth system services. - *

This is an asynchronous call: it will return immediatley, and + *

This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns * true, then the adapter state will immediately transition from {@link @@ -322,7 +322,7 @@ public final class BluetoothAdapter { * Turn off the local Bluetooth adapter. *

This gracefully shuts down all Bluetooth connections, stops Bluetooth * system services, and powers down the underlying Bluetooth hardware. - *

This is an asynchronous call: it will return immediatley, and + *

This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns * true, then the adapter state will immediately transition from {@link -- GitLab From 694305cd0ab6e95763aef6ee5e3732865af5642d Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 25 Sep 2009 15:00:29 -0700 Subject: [PATCH 0062/1408] Handle expiration of discovery mode in system server. Change-Id: I58fd199b40ffdf8168a5489be8eedb5d25d56722 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++++------ .../java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 776c923c0b2..3aaed385dfa 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -412,10 +412,10 @@ public final class BluetoothAdapter { * Set the Bluetooth scan mode of the local Bluetooth adapter. *

The Bluetooth scan mode determines if the local adapter is * connectable and/or discoverable from remote Bluetooth devices. - *

For privacy reasons, it is recommended to limit the duration of time - * that the local adapter remains in a discoverable scan mode. For example, - * 2 minutes is a generous time to allow a remote Bluetooth device to - * initiate and complete its discovery process. + *

For privacy reasons, discoverable mode is automatically turned off + * after duration seconds. For example, 120 seconds should be + * enough for a remote device to initiate and complete its discovery + * process. *

Valid scan mode values are: * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, @@ -427,16 +427,23 @@ public final class BluetoothAdapter { * instead. * * @param mode valid scan mode + * @param duration time in seconds to apply scan mode, only used for + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} * @return true if the scan mode was set, false otherwise * @hide */ - public boolean setScanMode(int mode) { + public boolean setScanMode(int mode, int duration) { try { - return mService.setScanMode(mode); + return mService.setScanMode(mode, duration); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } + /** @hide */ + public boolean setScanMode(int mode) { + return setScanMode(mode, 120); + } + /** @hide */ public int getDiscoverableTimeout() { try { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 203a61d8a1f..1bc2f968b38 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -35,7 +35,7 @@ interface IBluetooth boolean setName(in String name); int getScanMode(); - boolean setScanMode(int mode); + boolean setScanMode(int mode, int duration); int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); -- GitLab From b450b3153e4618801376fad8f4bbd945cf07eb31 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 28 Sep 2009 10:33:55 -0700 Subject: [PATCH 0063/1408] Add an API to request a system activity to turn on Bluetooth. Change-Id: I2fca33ad27017ea4e2ecba37854b749682d07672 --- .../android/bluetooth/BluetoothAdapter.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3aaed385dfa..7d945542865 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -105,6 +105,8 @@ public final class BluetoothAdapter { /** * Activity Action: Show a system activity that requests discoverable mode. + *

This activity will also request the user to turn on Bluetooth if it + * is not currently enabled. *

Discoverable mode is equivalent to {@link * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see * this Bluetooth adapter when they perform a discovery. @@ -120,7 +122,7 @@ public final class BluetoothAdapter { * value if the user rejected discoverability. *

Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} * for global notification whenever the scan mode changes. - *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

Requires {@link android.Manifest.permission#BLUETOOTH} */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISCOVERABLE = @@ -135,6 +137,24 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + /** + * Activity Action: Show a system activity that allows the user to turn on + * Bluetooth. + *

This system activity will return once Bluetooth has completed turning + * on, or the user has decided not to turn Bluetooth on. + *

Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be negative if the user did not turn on Bluetooth, and non-negative + * if Bluetooth has been turned on. + *

Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_ENABLE = + "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. -- GitLab From 03759e5324aeae8b7f95dfba6e6101baa3286184 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 28 Sep 2009 12:33:17 -0700 Subject: [PATCH 0064/1408] Move android.bluetooth.ParcelUuid to android.os.ParcelUuid Change-Id: I564429d5c5b6a5372b6ff26a53b0d7e518b53631 --- .../android/bluetooth/BluetoothAdapter.java | 1 + .../android/bluetooth/BluetoothDevice.java | 11 +- .../java/android/bluetooth/BluetoothUuid.java | 2 + .../java/android/bluetooth/IBluetooth.aidl | 2 +- .../java/android/bluetooth/ParcelUuid.aidl | 19 --- .../java/android/bluetooth/ParcelUuid.java | 135 ------------------ 6 files changed, 10 insertions(+), 160 deletions(-) delete mode 100644 framework/java/android/bluetooth/ParcelUuid.aidl delete mode 100644 framework/java/android/bluetooth/ParcelUuid.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7d945542865..e3ec2cc4cb2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c714f692a1c..d5393ed25cc 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -228,9 +229,9 @@ public final class BluetoothDevice implements Parcelable { /** * Broadcast Action: This intent is used to broadcast the {@link UUID} - * wrapped as a {@link ParcelUuid} of the remote device after it has been - * fetched. This intent is sent only when the UUIDs of the remote device - * are requested to be fetched using Service Discovery Protocol + * wrapped as a {@link android.os.ParcelUuid} of the remote device after it + * has been fetched. This intent is sent only when the UUIDs of the remote + * device are requested to be fetched using Service Discovery Protocol *

Always contains the extra field {@link #EXTRA_DEVICE} *

Always contains the extra filed {@link #EXTRA_UUID} *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. @@ -309,8 +310,8 @@ public final class BluetoothDevice implements Parcelable { /** * Used as an extra field in {@link #ACTION_UUID} intents, - * Contains the {@link ParcelUuid}s of the remote device which is a parcelable - * version of {@link UUID}. + * Contains the {@link android.os.ParcelUuid}s of the remote device which + * is a parcelable version of {@link UUID}. * @hide */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 24ad06a997a..da0564a2a05 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.os.ParcelUuid; + import java.util.Arrays; import java.util.HashSet; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 1bc2f968b38..2f77ba48da1 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -16,7 +16,7 @@ package android.bluetooth; -import android.bluetooth.ParcelUuid; +import android.os.ParcelUuid; /** * System private API for talking with the Bluetooth service. diff --git a/framework/java/android/bluetooth/ParcelUuid.aidl b/framework/java/android/bluetooth/ParcelUuid.aidl deleted file mode 100644 index 70bcc4b0db9..00000000000 --- a/framework/java/android/bluetooth/ParcelUuid.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2009, 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; - -parcelable ParcelUuid; diff --git a/framework/java/android/bluetooth/ParcelUuid.java b/framework/java/android/bluetooth/ParcelUuid.java deleted file mode 100644 index 27166a0d679..00000000000 --- a/framework/java/android/bluetooth/ParcelUuid.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2009 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.os.Parcel; -import android.os.Parcelable; - -import java.util.UUID; - -/** - * This class is a Parcelable wrapper around {@link UUID} which is an - * immutable representation of a 128-bit universally unique - * identifier. - */ -public final class ParcelUuid implements Parcelable { - - private final UUID mUuid; - - /** - * Constructor creates a ParcelUuid instance from the - * given {@link UUID}. - * - * @param uuid UUID - */ - public ParcelUuid(UUID uuid) { - mUuid = uuid; - } - - /** - * Creates a new ParcelUuid from a string representation of {@link UUID}. - * - * @param uuid - * the UUID string to parse. - * @return an ParcelUuid instance. - * @throws NullPointerException - * if {@code uuid} is {@code null}. - * @throws IllegalArgumentException - * if {@code uuid} is not formatted correctly. - */ - public static ParcelUuid fromString(String uuid) { - return new ParcelUuid(UUID.fromString(uuid)); - } - - /** - * Get the {@link UUID} represented by the ParcelUuid. - * - * @return UUID contained in the ParcelUuid. - */ - public UUID getUuid() { - return mUuid; - } - - /** - * Returns a string representation of the ParcelUuid - * For example: 0000110B-0000-1000-8000-00805F9B34FB will be the return value. - * - * @return a String instance. - */ - @Override - public String toString() { - return mUuid.toString(); - } - - - @Override - public int hashCode() { - return mUuid.hashCode(); - } - - /** - * Compares this ParcelUuid to another object for equality. If {@code object} - * is not {@code null}, is a ParcelUuid instance, and all bits are equal, then - * {@code true} is returned. - * - * @param object - * the {@code Object} to compare to. - * @return {@code true} if this ParcelUuid is equal to {@code object} - * or {@code false} if not. - */ - @Override - public boolean equals(Object object) { - if (object == null) { - return false; - } - - if (this == object) { - return true; - } - - if (!(object instanceof ParcelUuid)) { - return false; - } - - ParcelUuid that = (ParcelUuid) object; - - return (this.mUuid.equals(that.mUuid)); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public ParcelUuid createFromParcel(Parcel source) { - long mostSigBits = source.readLong(); - long leastSigBits = source.readLong(); - UUID uuid = new UUID(mostSigBits, leastSigBits); - return new ParcelUuid(uuid); - } - - public ParcelUuid[] newArray(int size) { - return new ParcelUuid[size]; - } - }; - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(mUuid.getMostSignificantBits()); - dest.writeLong(mUuid.getLeastSignificantBits()); - } -} -- GitLab From 031834bc1ac132f61a2ccf09e0376df491b0e4f7 Mon Sep 17 00:00:00 2001 From: Zhu Lan Date: Wed, 24 Jun 2009 10:51:57 +0800 Subject: [PATCH 0065/1408] Bluetooth A2DP suspend/resume functionality Change-Id: I8366852fa9b6ff9dacf18db00ea1c2be0c00ff34 --- .../java/android/bluetooth/BluetoothA2dp.java | 32 +++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 2 ++ 2 files changed, 34 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 2e9612ac063..a736ad14e4b 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -133,6 +133,38 @@ public final class BluetoothA2dp { } } + /** Initiate suspend from an A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * suspend is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public int suspendSink(BluetoothDevice device) { + try { + return mService.suspendSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Initiate resume from an suspended A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * resume is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public int resumeSink(BluetoothDevice device) { + try { + return mService.resumeSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** Check if a specified A2DP sink is connected. * @param device Remote BT device. * @return True if connected (or playing), false otherwise and on error. diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 2df7f238552..002cf4efff6 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -26,6 +26,8 @@ import android.bluetooth.BluetoothDevice; interface IBluetoothA2dp { boolean connectSink(in BluetoothDevice device); boolean disconnectSink(in BluetoothDevice device); + boolean suspendSink(in BluetoothDevice device); + boolean resumeSink(in BluetoothDevice device); BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports int getSinkState(in BluetoothDevice device); boolean setSinkPriority(in BluetoothDevice device, int priority); -- GitLab From 1acf1d7ba6e41227980670ba26d98f8bc3d54a90 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 30 Sep 2009 12:06:28 -0700 Subject: [PATCH 0066/1408] Fix broken build. Should be bool not int. Forgot to roll in local changes to last commit. Change-Id: I28bad88238e5b715a5033b0d5a897d924ef8b538 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index a736ad14e4b..7c4c9c1bd33 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -140,7 +140,7 @@ public final class BluetoothA2dp { * @return false on immediate error, true otherwise * @hide */ - public int suspendSink(BluetoothDevice device) { + public boolean suspendSink(BluetoothDevice device) { try { return mService.suspendSink(device); } catch (RemoteException e) { @@ -156,7 +156,7 @@ public final class BluetoothA2dp { * @return false on immediate error, true otherwise * @hide */ - public int resumeSink(BluetoothDevice device) { + public boolean resumeSink(BluetoothDevice device) { try { return mService.resumeSink(device); } catch (RemoteException e) { -- GitLab From 68d852798761bfc5d1fb6be88ebaf36e8ca73581 Mon Sep 17 00:00:00 2001 From: Zhu Lan Date: Wed, 24 Jun 2009 10:51:57 +0800 Subject: [PATCH 0067/1408] Bluetooth A2DP suspend/resume functionality Change-Id: I8366852fa9b6ff9dacf18db00ea1c2be0c00ff34 --- .../java/android/bluetooth/BluetoothA2dp.java | 32 +++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 2 ++ 2 files changed, 34 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 2e9612ac063..a736ad14e4b 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -133,6 +133,38 @@ public final class BluetoothA2dp { } } + /** Initiate suspend from an A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * suspend is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public int suspendSink(BluetoothDevice device) { + try { + return mService.suspendSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Initiate resume from an suspended A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * resume is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public int resumeSink(BluetoothDevice device) { + try { + return mService.resumeSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** Check if a specified A2DP sink is connected. * @param device Remote BT device. * @return True if connected (or playing), false otherwise and on error. diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 2df7f238552..002cf4efff6 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -26,6 +26,8 @@ import android.bluetooth.BluetoothDevice; interface IBluetoothA2dp { boolean connectSink(in BluetoothDevice device); boolean disconnectSink(in BluetoothDevice device); + boolean suspendSink(in BluetoothDevice device); + boolean resumeSink(in BluetoothDevice device); BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports int getSinkState(in BluetoothDevice device); boolean setSinkPriority(in BluetoothDevice device, int priority); -- GitLab From e5dd32670c963ebc0ad0540d5a04f673d5e48699 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 30 Sep 2009 12:06:28 -0700 Subject: [PATCH 0068/1408] Fix broken build. Should be bool not int. Forgot to roll in local changes to last commit. Change-Id: I28bad88238e5b715a5033b0d5a897d924ef8b538 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index a736ad14e4b..7c4c9c1bd33 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -140,7 +140,7 @@ public final class BluetoothA2dp { * @return false on immediate error, true otherwise * @hide */ - public int suspendSink(BluetoothDevice device) { + public boolean suspendSink(BluetoothDevice device) { try { return mService.suspendSink(device); } catch (RemoteException e) { @@ -156,7 +156,7 @@ public final class BluetoothA2dp { * @return false on immediate error, true otherwise * @hide */ - public int resumeSink(BluetoothDevice device) { + public boolean resumeSink(BluetoothDevice device) { try { return mService.resumeSink(device); } catch (RemoteException e) { -- GitLab From 64ba6a9758e39d34d7e92ed9a428b6343fbd1d32 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 29 Sep 2009 13:00:30 -0700 Subject: [PATCH 0069/1408] Try to reconnect twice when the rfcomm error code is Connection Refused. This happens when the the remote headset is in a bad state or is not accepting connections. Try twice before giving up. Change-Id: I55e15bad6b72904b8e4ccbca89e17e9bd3ddb61e --- framework/java/android/bluetooth/HeadsetBase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index 29cf41da931..e2935c95d32 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -211,9 +211,10 @@ public final class HeadsetBase { */ public boolean connectAsync() { - return connectAsyncNative(); + int ret = connectAsyncNative(); + return (ret == 0) ? true : false; } - private native boolean connectAsyncNative(); + private native int connectAsyncNative(); public int getRemainingAsyncConnectWaitingTimeMs() { return mTimeoutRemainingMs; -- GitLab From d19ea7e341ec6e27f6bdedb94d50efc11d94bc55 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 30 Sep 2009 17:59:40 -0700 Subject: [PATCH 0070/1408] b/2148252 Fix emulator Change-Id: Ic2a16d4fac2a2a12a209c83ad2049829255cb31f --- framework/java/android/bluetooth/BluetoothA2dp.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7c4c9c1bd33..e8a69d8c1f7 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -93,10 +93,15 @@ public final class BluetoothA2dp { mContext = c; IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); - if (b == null) { - throw new RuntimeException("Bluetooth A2DP service not available!"); + if (b != null) { + mService = IBluetoothA2dp.Stub.asInterface(b); + } else { + Log.w(TAG, "Bluetooth A2DP service not available!"); + + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later when it is actually used. + mService = null; } - mService = IBluetoothA2dp.Stub.asInterface(b); } /** Initiate a connection to an A2DP sink. -- GitLab From ee1402d8d697be3aa2858ee35e57db8c3e845a12 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 2 Oct 2009 20:34:18 -0700 Subject: [PATCH 0071/1408] Provide an API for apps to use a dynamic RFCOMM channel and SDP record. Hide listenUsingRfcommOn(int channel) Add listenUsingRfcomm(String name, ParcelUuid uuid) The new API automatically finds a free RFCOMM channel and registers an SDP record with the given uuid and name. The SDP record is automatically removed when the socket is closed, or if the application dies. Apps are prevented from registering SDP records with the uuid of system Bluetooth profiles, such as A2DP, HFP and OPP. Apps are prevented from removing SDP records that they did not create. This is tracked by pid. TODO: Provide an API for the connecting app to look up an SDP record. Bug: 2158900 DrNo: eastham Joke: "What did the dog say to the tree? bark." Change-Id: Ia92f51c34615a7270a403255ad2b8faa98c4a3f5 --- .../android/bluetooth/BluetoothAdapter.java | 158 ++++++++++++++++-- .../bluetooth/BluetoothServerSocket.java | 14 ++ .../android/bluetooth/BluetoothSocket.java | 28 +++- .../java/android/bluetooth/BluetoothUuid.java | 4 + .../java/android/bluetooth/IBluetooth.aidl | 3 + 5 files changed, 186 insertions(+), 21 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e3ec2cc4cb2..c6a0619cf8e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,14 +18,19 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; import java.io.IOException; import java.util.Collections; -import java.util.Set; import java.util.HashSet; +import java.util.LinkedList; +import java.util.Random; +import java.util.Set; /** * Represents the local Bluetooth adapter. @@ -40,6 +45,7 @@ import java.util.HashSet; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + private static final boolean DBG = true; //STOPSHIP: Remove excess logging /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -557,30 +563,139 @@ public final class BluetoothAdapter { return null; } + /** + * Randomly picks RFCOMM channels until none are left. + * Avoids reserved channels. + */ + private static class RfcommChannelPicker { + private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { + 10, // HFAG + 11, // HSAG + 12, // OPUSH + 19, // PBAP + }; + private static LinkedList sChannels; // master list of non-reserved channels + private static Random sRandom; + + private final LinkedList mChannels; // local list of channels left to try + + public RfcommChannelPicker() { + synchronized (RfcommChannelPicker.class) { + if (sChannels == null) { + // lazy initialization of non-reserved rfcomm channels + sChannels = new LinkedList(); + for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) { + sChannels.addLast(new Integer(i)); + } + for (int reserved : RESERVED_RFCOMM_CHANNELS) { + sChannels.remove(new Integer(reserved)); + } + sRandom = new Random(); + } + mChannels = (LinkedList)sChannels.clone(); + } + } + /* Returns next random channel, or -1 if we're out */ + public int nextChannel() { + if (mChannels.size() == 0) { + return -1; + } + return mChannels.remove(sRandom.nextInt(mChannels.size())); + } + } + /** * Create a listening, secure RFCOMM Bluetooth socket. *

A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. *

Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections to listening {@link BluetoothServerSocket}. + * connections from a listening {@link BluetoothServerSocket}. *

Valid RFCOMM channels are in range 1 to 30. - *

Requires {@link android.Manifest.permission#BLUETOOTH} + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. + * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, true, true, channel); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + + /** + * Create a listening, secure RFCOMM Bluetooth socket with Service Record. + *

A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

The system will assign an unused RFCOMM channel to listen on. + *

The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid) + throws IOException { + RfcommChannelPicker picker = new RfcommChannelPicker(); + + BluetoothServerSocket socket; + int channel; + int errno; + while (true) { + channel = picker.nextChannel(); + + if (channel == -1) { + throw new IOException("No available channels"); + } + + socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, channel); + errno = socket.mSocket.bindListen(); + if (errno == 0) { + if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); + break; // success + } else if (errno == BluetoothSocket.EADDRINUSE) { + if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use"); + try { + socket.close(); + } catch (IOException e) {} + continue; // try another channel + } else { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() + } + } + + int handle = -1; try { - socket.mSocket.bindListen(); - } catch (IOException e) { + handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + if (handle == -1) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + throw new IOException("Not able to register SDP record for " + name); } + socket.setCloseHandler(mHandler, handle); return socket; } @@ -595,13 +710,12 @@ public final class BluetoothAdapter { public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -617,13 +731,12 @@ public final class BluetoothAdapter { public static BluetoothServerSocket listenUsingScoOn() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_SCO, false, false, -1); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -636,6 +749,17 @@ public final class BluetoothAdapter { return Collections.unmodifiableSet(devices); } + private Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + /* handle socket closing */ + int handle = msg.what; + try { + if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle)); + mService.removeServiceRecord(handle); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + }; + /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" *

Alphabetic characters must be uppercase to be valid. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 45dc432d332..c14e4c0761d 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.os.Handler; + import java.io.Closeable; import java.io.IOException; @@ -52,6 +54,8 @@ import java.io.IOException; public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; + private Handler mHandler; + private int mMessage; /** * Construct a socket for incoming connections. @@ -101,6 +105,16 @@ public final class BluetoothServerSocket implements Closeable { * throw an IOException. */ public void close() throws IOException { + synchronized (this) { + if (mHandler != null) { + mHandler.obtainMessage(mMessage).sendToTarget(); + } + } mSocket.close(); } + + /*package*/ synchronized void setCloseHandler(Handler handler, int message) { + mHandler = handler; + mMessage = message; + } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index e462ea63831..573cb3d8264 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -54,11 +54,17 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothSocket implements Closeable { + /** @hide */ + public static final int MAX_RFCOMM_CHANNEL = 30; + /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ /*package*/ static final int TYPE_RFCOMM = 1; /*package*/ static final int TYPE_SCO = 2; /*package*/ static final int TYPE_L2CAP = 3; + /*package*/ static final int EBADFD = 77; + /*package*/ static final int EADDRINUSE = 98; + private final int mType; /* one of TYPE_RFCOMM etc */ private final int mPort; /* RFCOMM channel or L2CAP psm */ private final BluetoothDevice mDevice; /* remote device */ @@ -90,6 +96,11 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port) throws IOException { + if (type == BluetoothSocket.TYPE_RFCOMM) { + if (port < 1 || port > MAX_RFCOMM_CHANNEL) { + throw new IOException("Invalid RFCOMM channel: " + port); + } + } mType = type; mAuth = auth; mEncrypt = encrypt; @@ -211,11 +222,15 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } - /*package*/ void bindListen() throws IOException { + /** + * Currently returns unix errno instead of throwing IOException, + * so that BluetoothAdapter can check the error code for EADDRINUSE + */ + /*package*/ int bindListen() { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); - bindListenNative(); + if (mClosed) return EBADFD; + return bindListenNative(); } finally { mLock.readLock().unlock(); } @@ -264,11 +279,16 @@ public final class BluetoothSocket implements Closeable { private native void initSocketNative() throws IOException; private native void initSocketFromFdNative(int fd) throws IOException; private native void connectNative() throws IOException; - private native void bindListenNative() throws IOException; + private native int bindListenNative(); private native BluetoothSocket acceptNative(int timeout) throws IOException; private native int availableNative() throws IOException; private native int readNative(byte[] b, int offset, int length) throws IOException; private native int writeNative(byte[] b, int offset, int length) throws IOException; private native void abortNative() throws IOException; private native void destroyNative() throws IOException; + /** + * Throws an IOException for given posix errno. Done natively so we can + * use strerr to convert to string error. + */ + /*package*/ native void throwErrnoNative(int errno) throws IOException; } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index da0564a2a05..4164a3d6e66 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -50,6 +50,10 @@ public final class BluetoothUuid { public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid[] RESERVED_UUIDS = { + AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, + ObexObjectPush}; + public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 2f77ba48da1..e54abec2de2 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -63,4 +63,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + + int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); + void removeServiceRecord(int handle); } -- GitLab From 1e79198aafef8cde937694e6fdbd863acf4f7b8a Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 6 Oct 2009 16:10:02 +0200 Subject: [PATCH 0072/1408] Fix docs builds. --- framework/java/android/bluetooth/BluetoothServerSocket.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index c14e4c0761d..d126ea48ddd 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -40,7 +40,7 @@ import java.io.IOException; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. * diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 573cb3d8264..b9e33f3bda0 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. * -- GitLab From 07b84cb5bb5283352a27cc0cf651586ed00035a0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 7 Oct 2009 07:44:03 +0200 Subject: [PATCH 0073/1408] Encourage developers to connect RFCOMM by UUID instead of Channel. Hide createRfcommSocket(int channel) Add createRfcommSocketWithServiceRecord(UUID uuid) Rename listenUsingRfcomm(String,UUID) -> listenUsingRfcommWithServiceRecord(..) Now we have a complete API for developers to make peer-peer RFCOMM connections with hard-coding the limited (30) RFCOMM channels, instead using SDP lookup of an UUID. This commit addresses two serious bugs: - Do not throw IOException on accepting an incoming RFCOMM connection with BluetoothSocket. This was a regression from commit ee1402d8d697be3a - Workaround failure of bluez to update SDP cache when channel changes by trying to use the same RFCOMM channel on the server every time, instead of picking server channels randomly. This is a pretty ugly workaround, and we are still trying to fix the caching issue - but with this workaround we are at least shippable and apps will work at least until they start colliding on the 30 RFCOMM channels. DrNo: eastham Bug: 2158900 Joke: What did the digital watch say to his mom? "Look mom no hands." Change-Id: Ia4879943b83afac06b6f1a3f2391cf1628afce7d --- .../android/bluetooth/BluetoothAdapter.java | 40 +++++-- .../android/bluetooth/BluetoothDevice.java | 68 ++++++++--- .../bluetooth/BluetoothServerSocket.java | 12 +- .../android/bluetooth/BluetoothSocket.java | 109 +++++++++++++++--- .../java/android/bluetooth/IBluetooth.aidl | 5 +- .../android/bluetooth/IBluetoothCallback.aidl | 27 +++++ 6 files changed, 213 insertions(+), 48 deletions(-) create mode 100644 framework/java/android/bluetooth/IBluetoothCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c6a0619cf8e..8ce911de373 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.Random; import java.util.Set; +import java.util.UUID; /** * Represents the local Bluetooth adapter. @@ -564,8 +565,16 @@ public final class BluetoothAdapter { } /** - * Randomly picks RFCOMM channels until none are left. + * Picks RFCOMM channels until none are left. * Avoids reserved channels. + * Ideally we would pick random channels, but in the current implementation + * we start with the channel that is the hash of the UUID, and try every + * available channel from there. This means that in most cases a given + * uuid will use the same channel. This is a workaround for a Bluez SDP + * bug where we are not updating the cache when the channel changes for a + * uuid. + * TODO: Fix the Bluez SDP caching bug, and go back to random channel + * selection */ private static class RfcommChannelPicker { private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { @@ -579,7 +588,9 @@ public final class BluetoothAdapter { private final LinkedList mChannels; // local list of channels left to try - public RfcommChannelPicker() { + private final UUID mUuid; + + public RfcommChannelPicker(UUID uuid) { synchronized (RfcommChannelPicker.class) { if (sChannels == null) { // lazy initialization of non-reserved rfcomm channels @@ -594,13 +605,21 @@ public final class BluetoothAdapter { } mChannels = (LinkedList)sChannels.clone(); } + mUuid = uuid; } - /* Returns next random channel, or -1 if we're out */ + /* Returns next channel, or -1 if we're out */ public int nextChannel() { - if (mChannels.size() == 0) { - return -1; + int channel = mUuid.hashCode(); // always pick the same channel to try first + Integer channelInt; + while (mChannels.size() > 0) { + channelInt = new Integer(channel); + if (mChannels.remove(channelInt)) { + return channel; + } + channel = (channel % BluetoothSocket.MAX_RFCOMM_CHANNEL) + 1; } - return mChannels.remove(sRandom.nextInt(mChannels.size())); + + return -1; } } @@ -644,6 +663,8 @@ public final class BluetoothAdapter { * can use the same UUID to query our SDP server and discover which channel * to connect to. This SDP record will be removed when this socket is * closed, or if this application closes unexpectedly. + *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. *

Requires {@link android.Manifest.permission#BLUETOOTH} * @param name service name for SDP record * @param uuid uuid for SDP record @@ -651,9 +672,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. */ - public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid) + public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { - RfcommChannelPicker picker = new RfcommChannelPicker(); + RfcommChannelPicker picker = new RfcommChannelPicker(uuid); BluetoothServerSocket socket; int channel; @@ -687,7 +708,8 @@ public final class BluetoothAdapter { int handle = -1; try { - handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder()); + handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel, + new Binder()); } catch (RemoteException e) {Log.e(TAG, "", e);} if (handle == -1) { try { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d5393ed25cc..ce975c22794 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -316,21 +316,16 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; - - private static IBluetooth sService; /* Guarenteed constant after first object constructed */ + /** + * Lazy initialization. Guaranteed final after first object constructed, or + * getService() called. + * TODO: Unify implementation of sService amongst BluetoothFoo API's + */ + private static IBluetooth sService; private final String mAddress; - /** - * Create a new BluetoothDevice - * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", - * and is validated in this constructor. - * @param address valid Bluetooth MAC address - * @throws RuntimeException Bluetooth is not available on this platform - * @throws IllegalArgumentException address is invalid - * @hide - */ - /*package*/ BluetoothDevice(String address) { + /*package*/ static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); @@ -340,7 +335,20 @@ public final class BluetoothDevice implements Parcelable { sService = IBluetooth.Stub.asInterface(b); } } + return sService; + } + /** + * Create a new BluetoothDevice + * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", + * and is validated in this constructor. + * @param address valid Bluetooth MAC address + * @throws RuntimeException Bluetooth is not available on this platform + * @throws IllegalArgumentException address is invalid + * @hide + */ + /*package*/ BluetoothDevice(String address) { + getService(); // ensures sService is initialized if (!BluetoothAdapter.checkBluetoothAddress(address)) { throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); } @@ -551,7 +559,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean fetchUuidsWithSdp() { try { - return sService.fetchRemoteUuidsWithSdp(mAddress); + return sService.fetchRemoteUuids(mAddress, null, null); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -598,7 +606,7 @@ public final class BluetoothDevice implements Parcelable { /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure - * outgoing connection to this remote device. + * outgoing connection to this remote device on given channel. *

The remote device will be authenticated and communication on this * socket will be encrypted. *

Use {@link BluetoothSocket#connect} to intiate the outgoing @@ -610,9 +618,34 @@ public final class BluetoothDevice implements Parcelable { * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions + * @hide */ public BluetoothSocket createRfcommSocket(int channel) throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel); + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, + null); + } + + /** + * Create an RFCOMM {@link BluetoothSocket} ready to start a secure + * outgoing connection to this remote device using SDP lookup of uuid. + *

This is designed to be used with {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer + * Bluetooth applications. + *

Use {@link BluetoothSocket#connect} to intiate the outgoing + * connection. This will also perform an SDP lookup of the given uuid to + * determine which channel to connect to. + *

The remote device will be authenticated and communication on this + * socket will be encrypted. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param uuid service record uuid to lookup RFCOMM channel + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + */ + public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, + new ParcelUuid(uuid)); } /** @@ -628,7 +661,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, + null); } /** @@ -640,7 +674,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createScoSocket() throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); + return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } /** diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index d126ea48ddd..605bdc11e19 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -36,13 +36,13 @@ import java.io.IOException; * connection orientated, streaming transport over Bluetooth. It is also known * as the Serial Port Profile (SPP). * - *

Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link - * BluetoothSocket} ready for an outgoing connection to a remote + *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create + * a new {@link BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening - * {@link BluetoothServerSocket} ready for incoming connections to the local - * {@link BluetoothAdapter}. + *

Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to + * create a listening {@link BluetoothServerSocket} ready for incoming + * connections to the local {@link BluetoothAdapter}. * *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread * safe. In particular, {@link #close} will always immediately abort ongoing @@ -68,7 +68,7 @@ public final class BluetoothServerSocket implements Closeable { */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port); + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); } /** diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index b9e33f3bda0..7e725903b3e 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,11 +16,15 @@ package android.bluetooth; +import android.bluetooth.IBluetoothCallback; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; - import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -38,13 +42,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * connection orientated, streaming transport over Bluetooth. It is also known * as the Serial Port Profile (SPP). * - *

Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link - * BluetoothSocket} ready for an outgoing connection to a remote + *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create + * a new {@link BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening - * {@link BluetoothServerSocket} ready for incoming connections to the local - * {@link BluetoothAdapter}. + *

Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to + * create a listening {@link BluetoothServerSocket} ready for incoming + * connections to the local {@link BluetoothAdapter}. * *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread * safe. In particular, {@link #close} will always immediately abort ongoing @@ -54,6 +58,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothSocket implements Closeable { + private static final String TAG = "BluetoothSocket"; + /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; @@ -66,13 +72,15 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int EADDRINUSE = 98; private final int mType; /* one of TYPE_RFCOMM etc */ - private final int mPort; /* RFCOMM channel or L2CAP psm */ private final BluetoothDevice mDevice; /* remote device */ private final String mAddress; /* remote address */ private final boolean mAuth; private final boolean mEncrypt; private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; + private final SdpHelper mSdp; + + private int mPort; /* RFCOMM channel or L2CAP psm */ /** prevents all native calls after destroyNative() */ private boolean mClosed; @@ -91,16 +99,24 @@ public final class BluetoothSocket implements Closeable { * @param encrypt require the connection to be encrypted * @param device remote device that this socket can connect to * @param port remote port + * @param uuid SDP uuid * @throws IOException On error, for example Bluetooth not available, or * insufficient priveleges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, - BluetoothDevice device, int port) throws IOException { - if (type == BluetoothSocket.TYPE_RFCOMM) { + BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { + if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) { if (port < 1 || port > MAX_RFCOMM_CHANNEL) { throw new IOException("Invalid RFCOMM channel: " + port); } } + if (uuid == null) { + mPort = port; + mSdp = null; + } else { + mSdp = new SdpHelper(device, uuid); + mPort = -1; + } mType = type; mAuth = auth; mEncrypt = encrypt; @@ -110,7 +126,6 @@ public final class BluetoothSocket implements Closeable { } else { mAddress = device.getAddress(); } - mPort = port; if (fd == -1) { initSocketNative(); } else { @@ -123,7 +138,7 @@ public final class BluetoothSocket implements Closeable { } /** - * Construct a BluetoothSocket from address. + * Construct a BluetoothSocket from address. Used by native code. * @param type type of socket * @param fd fd to use for connected socket, or -1 for a new socket * @param auth require the remote device to be authenticated @@ -135,7 +150,7 @@ public final class BluetoothSocket implements Closeable { */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port); + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null); } /** @hide */ @@ -160,7 +175,12 @@ public final class BluetoothSocket implements Closeable { mLock.readLock().lock(); try { if (mClosed) throw new IOException("socket closed"); - connectNative(); + + if (mSdp != null) { + mPort = mSdp.doSdp(); // blocks + } + + connectNative(); // blocks } finally { mLock.readLock().unlock(); } @@ -176,12 +196,15 @@ public final class BluetoothSocket implements Closeable { mLock.readLock().lock(); try { if (mClosed) return; + if (mSdp != null) { + mSdp.cancel(); + } abortNative(); } finally { mLock.readLock().unlock(); } - // all native calls are guarenteed to immediately return after + // all native calls are guaranteed to immediately return after // abortNative(), so this lock should immediatley acquire mLock.writeLock().lock(); try { @@ -291,4 +314,62 @@ public final class BluetoothSocket implements Closeable { * use strerr to convert to string error. */ /*package*/ native void throwErrnoNative(int errno) throws IOException; + + /** + * Helper to perform blocking SDP lookup. + */ + private static class SdpHelper extends IBluetoothCallback.Stub { + private final IBluetooth service; + private final ParcelUuid uuid; + private final BluetoothDevice device; + private int channel; + private boolean canceled; + public SdpHelper(BluetoothDevice device, ParcelUuid uuid) { + service = BluetoothDevice.getService(); + this.device = device; + this.uuid = uuid; + canceled = false; + } + /** + * Returns the RFCOMM channel for the UUID, or throws IOException + * on failure. + */ + public synchronized int doSdp() throws IOException { + if (canceled) throw new IOException("Service discovery canceled"); + channel = -1; + + boolean inProgress = false; + try { + inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + + if (!inProgress) throw new IOException("Unable to start Service Discovery"); + + try { + /* 12 second timeout as a precaution - onRfcommChannelFound + * should always occur before the timeout */ + wait(12000); // block + + } catch (InterruptedException e) {} + + if (canceled) throw new IOException("Service discovery canceled"); + if (channel < 1) throw new IOException("Service discovery failed"); + + return channel; + } + /** Object cannot be re-used after calling cancel() */ + public synchronized void cancel() { + if (!canceled) { + canceled = true; + channel = -1; + notifyAll(); // unblock + } + } + public synchronized void onRfcommChannelFound(int channel) { + if (!canceled) { + this.channel = channel; + notifyAll(); // unblock + } + } + } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index e54abec2de2..7e752af428f 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -16,6 +16,7 @@ package android.bluetooth; +import android.bluetooth.IBluetoothCallback; import android.os.ParcelUuid; /** @@ -53,8 +54,8 @@ interface IBluetooth String getRemoteName(in String address); int getRemoteClass(in String address); ParcelUuid[] getRemoteUuids(in String address); - boolean fetchRemoteUuidsWithSdp(in String address); - int getRemoteServiceChannel(in String address,in ParcelUuid uuid); + boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback); + int getRemoteServiceChannel(in String address, in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); boolean setPasskey(in String address, int passkey); diff --git a/framework/java/android/bluetooth/IBluetoothCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl new file mode 100644 index 00000000000..8edb3f4c413 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009, 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; + +/** + * System private API for Bluetooth service callbacks. + * + * {@hide} + */ +interface IBluetoothCallback +{ + void onRfcommChannelFound(int channel); +} -- GitLab From 79b3ff80ff00f3186f8d8cc07520a3c211e489a0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 8 Oct 2009 00:12:45 +0200 Subject: [PATCH 0074/1408] Introduce BluetoothAdapter.getDefaultAdapter(). This is the main entry point to the Bluetooth APIs, and returns the default local Bluetooth adapter. It replaces context.getSystemService(Context.BLUETOOTH_SERVICE). This was never in a public SDK release. DrNo: eastham Bug: 2158765 Joke: Why can't you play cards in the jungle? Because there's too many cheetas! Change-Id: Ieed8be009ee5aba621cb69090ee8c8a9c19c840d --- .../android/bluetooth/BluetoothAdapter.java | 38 ++++++++++++++++--- .../android/bluetooth/BluetoothDevice.java | 3 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8ce911de373..cc35b7da9f7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,9 +20,11 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import java.io.IOException; @@ -36,10 +38,8 @@ import java.util.UUID; /** * Represents the local Bluetooth adapter. * - *

Use {@link android.content.Context#getSystemService} with {@link - * android.content.Context#BLUETOOTH_SERVICE} to get the default local - * Bluetooth adapter. On most Android devices there is only one local - * Bluetotoh adapter. + *

Use {@link #getDefaultAdapter} to get the default local Bluetooth + * adapter. * *

Use the {@link BluetoothDevice} class for operations on remote Bluetooth * devices. @@ -257,12 +257,40 @@ public final class BluetoothAdapter { */ public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; + /** @hide */ + public static final String BLUETOOTH_SERVICE = "bluetooth"; + private static final int ADDRESS_LENGTH = 17; + /** + * Lazyily initialized singleton. Guaranteed final after first object + * constructed. + */ + private static BluetoothAdapter sAdapter; + private final IBluetooth mService; /** - * Do not use this constructor. Use Context.getSystemService() instead. + * Get a handle to the default local Bluetooth adapter. + *

Currently Android only supports one Bluetooth adapter, but the API + * could be extended to support more. This will always return the default + * adapter. + * @return the default local adapter, or null if Bluetooth is not supported + * on this hardware platform + */ + public static synchronized BluetoothAdapter getDefaultAdapter() { + if (sAdapter == null) { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b != null) { + IBluetooth service = IBluetooth.Stub.asInterface(b); + sAdapter = new BluetoothAdapter(service); + } + } + return sAdapter; + } + + /** + * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. * @hide */ public BluetoothAdapter(IBluetooth service) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ce975c22794..9c23746494a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.Context; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -328,7 +327,7 @@ public final class BluetoothDevice implements Parcelable { /*package*/ static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { - IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); if (b == null) { throw new RuntimeException("Bluetooth service not available"); } -- GitLab From 8640fe7f971532097ea032e82507c0518d682393 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 8 Oct 2009 02:27:52 -0700 Subject: [PATCH 0075/1408] Set the Bond State to NONE when we receive a Agent Cancel. Sometimes during OPP, we can get stuck in Pairing state when the remote end, cancels the Pairing process - we will just get onAgentCancel and thus not set the Pairing state properly. DrNo: Eastham Bug:2174874 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 9c23746494a..39a74acee88 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -287,9 +287,13 @@ public final class BluetoothDevice implements Parcelable { /** A bond attempt failed because of repeated attempts * @hide */ public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; + /** A bond attempt failed because we received an Authentication Cancel + * by remote end + * @hide */ + public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** An existing bond was explicitly revoked * @hide */ - public static final int UNBOND_REASON_REMOVED = 8; + public static final int UNBOND_REASON_REMOVED = 9; /** The user will be prompted to enter a pin * @hide */ -- GitLab From 2076d39018a62610c1242870a63e1f057b1e076a Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 8 Oct 2009 23:27:28 +0200 Subject: [PATCH 0076/1408] BT API security audit: fix a couple of permission mistakes. Make functions that are meant to be BLUETOOTH_ADMIN really BLUETOOTH_ADMIN. Add some missing javadoc for permissions. The only functional change here is the BLUETOOTH->BLUETOOTH_ADMIN changes. This is super safe because every system app that uses BT has both permissions. Change-Id: Iddc61f9fd5d81fe0171358665a0fa52f2fa02871 DrNo: eastham Joke: How do you catch a rabbit? Hide behind a tree and make carrott noises. --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 ++ framework/java/android/bluetooth/BluetoothDevice.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cc35b7da9f7..5b34ef9ec05 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -569,6 +569,7 @@ public final class BluetoothAdapter { *

Applications can also register for {@link #ACTION_DISCOVERY_STARTED} * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery * starts or completes. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return true if discovering */ @@ -582,6 +583,7 @@ public final class BluetoothAdapter { /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 39a74acee88..849e6c7b246 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -513,6 +513,7 @@ public final class BluetoothDevice implements Parcelable { /** * Get trust state of a remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. * @hide */ public boolean getTrustState() { @@ -526,6 +527,7 @@ public final class BluetoothDevice implements Parcelable { /** * Set trust state for a remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * @param value the trust state value (true or false) * @hide */ @@ -657,6 +659,8 @@ public final class BluetoothDevice implements Parcelable { * Call #connect on the returned #BluetoothSocket to begin the connection. * The remote device will not be authenticated and communication on this * socket will not be encrypted. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * * @param port remote port * @return An RFCOMM BluetoothSocket * @throws IOException On error, for example Bluetooth not available, or @@ -671,6 +675,8 @@ public final class BluetoothDevice implements Parcelable { /** * Construct a SCO socket ready to start an outgoing connection. * Call #connect on the returned #BluetoothSocket to begin the connection. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * * @return a SCO BluetoothSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions. -- GitLab From 4d4be0b0198c1718f42b1457e57d5cd1b178d2c7 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 9 Oct 2009 00:54:41 +0200 Subject: [PATCH 0077/1408] Remove STOPSHIP BT logging. Do not merge. Change-Id: I428bc0fc67030f24112f2e9c865824dfaea4897d DrNo: eastham Bug: 2089423 Joke: Why was Tigger looking in the toilet? To find Pooh --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5b34ef9ec05..ff48583a803 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -46,7 +46,7 @@ import java.util.UUID; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - private static final boolean DBG = true; //STOPSHIP: Remove excess logging + private static final boolean DBG = false; /** * Sentinel error value for this class. Guaranteed to not equal any other -- GitLab From 0abeb7e929ff9102e6c927c3603c7dbda17ffd20 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 30 Oct 2009 09:37:25 -0700 Subject: [PATCH 0078/1408] Revert the channge where channels were not selected randomly. The Bluez SDP bug has been fixed. Reverting parts of the commit: 07b84cb5bb5283352a27cc0cf651586ed00035a0 Bug: 2173752 Dr No: Eastham --- .../android/bluetooth/BluetoothAdapter.java | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ff48583a803..3fc676b271f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -597,14 +597,6 @@ public final class BluetoothAdapter { /** * Picks RFCOMM channels until none are left. * Avoids reserved channels. - * Ideally we would pick random channels, but in the current implementation - * we start with the channel that is the hash of the UUID, and try every - * available channel from there. This means that in most cases a given - * uuid will use the same channel. This is a workaround for a Bluez SDP - * bug where we are not updating the cache when the channel changes for a - * uuid. - * TODO: Fix the Bluez SDP caching bug, and go back to random channel - * selection */ private static class RfcommChannelPicker { private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { @@ -637,19 +629,12 @@ public final class BluetoothAdapter { } mUuid = uuid; } - /* Returns next channel, or -1 if we're out */ + /* Returns next random channel, or -1 if we're out */ public int nextChannel() { - int channel = mUuid.hashCode(); // always pick the same channel to try first - Integer channelInt; - while (mChannels.size() > 0) { - channelInt = new Integer(channel); - if (mChannels.remove(channelInt)) { - return channel; - } - channel = (channel % BluetoothSocket.MAX_RFCOMM_CHANNEL) + 1; + if (mChannels.size() == 0) { + return -1; } - - return -1; + return mChannels.remove(sRandom.nextInt(mChannels.size())); } } -- GitLab From 2e2338332fd394754e74481c3f1f9e923bc3e842 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Thu, 5 Nov 2009 18:29:01 -0800 Subject: [PATCH 0079/1408] b/2234854 Fixed Bluetooth API return codes for requesting permission to enable bluetooth --- framework/java/android/bluetooth/BluetoothAdapter.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3fc676b271f..4684f455e95 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -126,8 +126,9 @@ public final class BluetoothAdapter { *

Notification of the result of this activity is posted using the * {@link android.app.Activity#onActivityResult} callback. The * resultCode - * will be the duration (in seconds) of discoverability, or a negative - * value if the user rejected discoverability. + * will be the duration (in seconds) of discoverability or + * {@link android.app.Activity#RESULT_CANCELED} if the user rejected + * discoverability or an error has occurred. *

Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} * for global notification whenever the scan mode changes. *

Requires {@link android.Manifest.permission#BLUETOOTH} @@ -153,8 +154,9 @@ public final class BluetoothAdapter { *

Notification of the result of this activity is posted using the * {@link android.app.Activity#onActivityResult} callback. The * resultCode - * will be negative if the user did not turn on Bluetooth, and non-negative - * if Bluetooth has been turned on. + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. *

Applications can also listen for {@link #ACTION_STATE_CHANGED} * for global notification whenever Bluetooth is turned on or off. *

Requires {@link android.Manifest.permission#BLUETOOTH} -- GitLab From beef809fa89a5e7955a4c25cf70e00adbcf2e24e Mon Sep 17 00:00:00 2001 From: Scott Main Date: Tue, 3 Nov 2009 18:17:59 -0800 Subject: [PATCH 0080/1408] docs: add more documentation for the bluetooth apis. more descriptions for some of the classes and a new overview and pseudo-code example for using BT APIs in the package summary. --- .../android/bluetooth/BluetoothAdapter.java | 27 ++++- .../android/bluetooth/BluetoothClass.java | 52 +++++--- .../android/bluetooth/BluetoothDevice.java | 23 +++- .../bluetooth/BluetoothServerSocket.java | 38 +++--- .../android/bluetooth/BluetoothSocket.java | 44 ++++--- framework/java/android/bluetooth/package.html | 112 ++++++++++++++++-- 6 files changed, 228 insertions(+), 68 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ff48583a803..595156f71c5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,13 +36,30 @@ import java.util.Set; import java.util.UUID; /** - * Represents the local Bluetooth adapter. + * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} + * lets you perform fundamental Bluetooth tasks, such as initiate + * device discovery, query a list of bonded (paired) devices, + * instantiate a {@link BluetoothDevice} using a known MAC address, and create + * a {@link BluetoothServerSocket} to listen for connection requests from other + * devices. * - *

Use {@link #getDefaultAdapter} to get the default local Bluetooth - * adapter. + *

To get a {@link BluetoothAdapter} representing the local Bluetooth + * adapter, call the static {@link #getDefaultAdapter} method. + * Fundamentally, this is your starting point for all + * Bluetooth actions. Once you have the local adapter, you can get a set of + * {@link BluetoothDevice} objects representing all paired devices with + * {@link #getBondedDevices()}; start device discovery with + * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to + * listen for incoming connection requests with + * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}. * - *

Use the {@link BluetoothDevice} class for operations on remote Bluetooth - * devices. + *

Note: + * Most methods require the {@link android.Manifest.permission#BLUETOOTH} + * permission and some also require the + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * {@see BluetoothDevice} + * {@see BluetoothServerSocket} */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 6210380595a..bc067130584 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -20,25 +20,37 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Represents a Bluetooth class. + * Represents a Bluetooth class, which describes general characteristics + * and capabilities of a device. For example, a Bluetooth class will + * specify the general device type such as a phone, a computer, or + * headset, and whether it's capable of services such as audio or telephony. * - *

Bluetooth Class is a 32 bit field. The format of these bits is defined at - * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class contains that 32 bit field, and provides - * constants and methods to determine which Service Class(es) and Device Class - * are encoded in that field. + *

The Bluetooth class is useful as a hint to roughly describe a device (for example to + * show an icon in the UI), but does not reliably describe which Bluetooth + * profiles or services are actually supported by a device. * - *

Every Bluetooth Class is composed of zero or more service classes, and + *

Every Bluetooth class is composed of zero or more service classes, and * exactly one device class. The device class is further broken down into major * and minor device class components. * - *

Class is useful as a hint to roughly describe a device (for example to - * show an icon in the UI), but does not reliably describe which Bluetooth - * profiles or services are actually supported by a device. Accurate service - * discovery is done through SDP requests. + *

{@link BluetoothClass} is useful as a hint to roughly describe a device + * (for example to show an icon in the UI), but does not reliably describe which + * Bluetooth profiles or services are actually supported by a device. Accurate + * service discovery is done through SDP requests, which are automatically + * performed when creating an RFCOMM socket with {@link + * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}

* *

Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for * a remote device. + * + * */ public final class BluetoothClass implements Parcelable { /** @@ -91,7 +103,7 @@ public final class BluetoothClass implements Parcelable { } /** - * Bluetooth service classes. + * Defines all service class constants. *

Each {@link BluetoothClass} encodes zero or more service classes. */ public static final class Service { @@ -109,7 +121,8 @@ public final class BluetoothClass implements Parcelable { } /** - * Return true if the specified service class is supported by this class. + * Return true if the specified service class is supported by this + * {@link BluetoothClass}. *

Valid service classes are the public constants in * {@link BluetoothClass.Service}. For example, {@link * BluetoothClass.Service#AUDIO}. @@ -122,17 +135,22 @@ public final class BluetoothClass implements Parcelable { } /** - * Bluetooth device classes. + * Defines all device class constants. *

Each {@link BluetoothClass} encodes exactly one device class, with * major and minor components. *

The constants in {@link * BluetoothClass.Device} represent a combination of major and minor - * components (the complete device class). The constants in {@link - * BluetoothClass.Device.Major} represent just the major device classes. + * device components (the complete device class). The constants in {@link + * BluetoothClass.Device.Major} represent only major device classes. + *

See {@link BluetoothClass.Service} for service class constants. */ public static class Device { private static final int BITMASK = 0x1FFC; + /** + * Defines all major device class constants. + *

See {@link BluetoothClass.Device} for minor classes. + */ public static class Major { private static final int BITMASK = 0x1F00; @@ -215,7 +233,7 @@ public final class BluetoothClass implements Parcelable { } /** - * Return the major device class component of this Bluetooth class. + * Return the major device class component of this {@link BluetoothClass}. *

Values returned from this function can be compared with the * public constants in {@link BluetoothClass.Device.Major} to determine * which major class is encoded in this Bluetooth class. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 849e6c7b246..6cb9770a610 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -31,16 +31,31 @@ import java.io.UnsupportedEncodingException; import java.util.UUID; /** - * Represents a remote Bluetooth device. - * - *

Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link - * BluetoothDevice}. + * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you + * create a connection with the repective device or query information about + * it, such as the name, address, class, and bonding state. * *

This class is really just a thin wrapper for a Bluetooth hardware * address. Objects of this class are immutable. Operations on this class * are performed on the remote Bluetooth hardware address, using the * {@link BluetoothAdapter} that was used to create this {@link * BluetoothDevice}. + * + *

To get a {@link BluetoothDevice}, use + * {@link BluetoothAdapter#getRemoteDevice(String) + * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device + * of a known MAC address (which you can get through device discovery with + * {@link BluetoothAdapter}) or get one from the set of bonded devices + * returned by {@link BluetoothAdapter#getBondedDevices() + * BluetoothAdapter.getBondedDevices()}. You can then open a + * {@link BluetoothSocket} for communciation with the remote device, using + * {@link #createRfcommSocketToServiceRecord(UUID)}. + * + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothAdapter} + * {@see BluetoothSocket} */ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 605bdc11e19..1b23f6c048d 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -27,29 +27,31 @@ import java.io.IOException; *

The interface for Bluetooth Sockets is similar to that of TCP sockets: * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. It will return a new, connected {@link BluetoothSocket} on an - * accepted connection. On the client side, use the same - * {@link BluetoothSocket} object to both intiate the outgoing connection, - * and to manage the connected socket. + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection. * - *

The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a - * connection orientated, streaming transport over Bluetooth. It is also known - * as the Serial Port Profile (SPP). + *

The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). * - *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create - * a new {@link BluetoothSocket} ready for an outgoing connection to a remote - * {@link BluetoothDevice}. + *

To create a listenting {@link BluetoothServerSocket} that's ready for + * incoming connections, use + * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord + * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call + * {@link #accept()} to listen for incoming connection requests. This call + * will block until a connection is established, at which point, it will return + * a {@link BluetoothSocket} to manage the connection. * - *

Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to - * create a listening {@link BluetoothServerSocket} ready for incoming - * connections to the local {@link BluetoothAdapter}. - * - *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + *

{@link BluetoothServerSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing - * operations and close the socket. + * operations and close the server socket. + * + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. * - *

All methods on a {@link BluetoothServerSocket} require - * {@link android.Manifest.permission#BLUETOOTH} + * {@see BluetoothSocket} */ public final class BluetoothServerSocket implements Closeable { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 7e725903b3e..dbcc758574b 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -33,29 +33,41 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; *

The interface for Bluetooth Sockets is similar to that of TCP sockets: * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. It will return a new, connected {@link BluetoothSocket} on an - * accepted connection. On the client side, use the same - * {@link BluetoothSocket} object to both intiate the outgoing connection, - * and to manage the connected socket. + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection. * - *

The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a - * connection orientated, streaming transport over Bluetooth. It is also known - * as the Serial Port Profile (SPP). + *

The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). * - *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create - * a new {@link BluetoothSocket} ready for an outgoing connection to a remote - * {@link BluetoothDevice}. + *

To create a {@link BluetoothSocket} for connecting to a known device, use + * {@link BluetoothDevice#createRfcommSocketToServiceRecord + * BluetoothDevice.createRfcommSocketToServiceRecord()}. + * Then call {@link #connect()} to attempt a connection to the remote device. + * This call will block until a connection is established or the connection + * fails. * - *

Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to - * create a listening {@link BluetoothServerSocket} ready for incoming - * connections to the local {@link BluetoothAdapter}. + *

To create a {@link BluetoothSocket} as a server (or "host"), see the + * {@link BluetoothServerSocket} documentation. * - *

{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + *

Once the socket is connected, whether initiated as a client or accepted + * as a server, open the IO streams by calling {@link #getInputStream} and + * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream} + * and {@link java.io.OutputStream} objects, respectively, which are + * automatically connected to the socket. + * + *

{@link BluetoothSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing * operations and close the socket. * - *

All methods on a {@link BluetoothSocket} require - * {@link android.Manifest.permission#BLUETOOTH} + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothServerSocket} + * {@see java.io.InputStream} + * {@see java.io.OutputStream} */ public final class BluetoothSocket implements Closeable { private static final String TAG = "BluetoothSocket"; diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 79abf0cb4a0..4f0755e7150 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -1,13 +1,109 @@ -Provides classes that manage Bluetooth functionality on the device. -

-The Bluetooth APIs allow applications can connect and disconnect headsets, or scan -for other kinds of Bluetooth devices and pair them. Further control includes the -ability to write and modify the local Service Discovery Protocol (SDP) database, -query the SDP database of other Bluetooth devices, establish RFCOMM -channels/sockets on Android, and connect to specified sockets on other devices. +Provides classes that manage Bluetooth functionality, such as scanning for +devices, connecting with devices, and managing data transfer between devices. + +

The Bluetooth APIs let applications:

+
    +
  • Scan for other Bluetooth devices
  • +
  • Query the local Bluetooth adapter for paired Bluetooth devices
  • +
  • Establish RFCOMM channels/sockets
  • +
  • Connect to specified sockets on other devices
  • +
  • Transfer data to and from other devices
  • +
+ +

Note: +To perform Bluetooth communication using these APIs, an application must +declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some +additional functionality, such as requesting device discovery and +pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +permission.

-

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

+ +

Overview

+ +

Here's a basic introduction to the Bluetooth classes:

+
+
{@link android.bluetooth.BluetoothAdapter}
+
This represents the local Bluetooth adapter, which is essentially the + entry-point to performing any interaction with Bluetooth. With it, you can + discover other Bluetooth devices, query a list of bonded (paired) devices, + initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC + address, and create a {@link android.bluetooth.BluetoothServerSocket} to + listen for communications from other devices.
+ +
{@link android.bluetooth.BluetoothDevice}
+
This represents a remote Bluetooth device. Use this to request a + connection with a remote device through a + {@link android.bluetooth.BluetoothSocket} + or query information about the device such as its name, address, class, and + bonding state.
+ +
{@link android.bluetooth.BluetoothSocket}
+
This represents the interface for a Bluetooth socket + (similar to a TCP client-side {@link java.net.Socket}). This is the + connection point that allows an app to transfer data with another Bluetooth + device via {@link java.io.InputStream} and {@link java.io.OutputStream}.
+
{@link android.bluetooth.BluetoothServerSocket}
+ +
This represents an open server socket that listens for incoming requests + (similar to a TCP server-side {@link java.net.ServerSocket}). + When attempting to connect two Android devices, one device will need to open + a server socket with this class. When a connection is accepted, a new + {@link android.bluetooth.BluetoothSocket} will be returned, + which can be used to manage the connection and transfer data.
+ +
{@link android.bluetooth.BluetoothClass}
+
This represents the Bluetooth class for a device which describes general + characteristics and capabilities of a device. This class and its subclasses + don't provide any actual functionality. The sub-classes are entirely composed + of constants for the device and service class definitions.
+
+ + +

Example Procedure

+ +

For example, here's an pseudo-code procedure for discovering and +connecting a remote device, and transfering data:

+ +
    +
  1. Register a {@link android.content.BroadcastReceiver} that accepts the + {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.
  2. +
  3. Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to + retrieve the Android system's local + {@link android.bluetooth.BluetoothAdapter}.
  4. +
  5. Call {@link android.bluetooth.BluetoothAdapter#startDiscovery() + BluetoothAdapter.startDiscovery()} to scan for local devices. This is where + the BroadcastReceiver comes in; Android now scans for devices and will + broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent + for each remote device discovered. The + {@link android.content.BroadcastReceiver} + you created will receive each Intent.
  6. +
  7. The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent + includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE} + Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice} + object. Extract this from the Intent and call + {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID) + BluetoothDevice.createRfcommSocketToServiceRecord()} + to open a {@link android.bluetooth.BluetoothSocket} with a chosen + remote device.
  8. +
  9. Call {@link android.bluetooth.BluetoothSocket#connect() + BluetoothSocket.connect()} to connect with the remote device.
  10. +
  11. When successfully connected, call + {@link android.bluetooth.BluetoothSocket#getInputStream() + BluetoothSocket.getInputStream()} and/or + {@link android.bluetooth.BluetoothSocket#getOutputStream() + BluetoothSocket.getOutputStream()} to retreive an + {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively, + which are hooked into the socket.
  12. +
  13. Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and + {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer + data.
  14. +
+ + + +

Note: +Not all Android devices are guaranteed to have Bluetooth functionality.

-- GitLab From 47f62c25c25d3349dc95bf02cf91c5bdbc498e04 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Thu, 12 Nov 2009 18:45:53 -0800 Subject: [PATCH 0081/1408] eclair snapshot --- .../java/android/bluetooth/BluetoothA2dp.java | 188 ++-- .../android/bluetooth/BluetoothAdapter.java | 849 ++++++++++++++++ .../bluetooth/BluetoothAudioGateway.java | 60 +- .../android/bluetooth/BluetoothClass.java | 276 +++-- .../android/bluetooth/BluetoothDevice.aidl | 19 + .../android/bluetooth/BluetoothDevice.java | 943 ++++++++++-------- .../bluetooth/BluetoothDevicePicker.java | 66 ++ .../android/bluetooth/BluetoothHeadset.java | 103 +- .../bluetooth/BluetoothInputStream.java | 98 ++ .../android/bluetooth/BluetoothIntent.java | 145 --- .../bluetooth/BluetoothOutputStream.java | 87 ++ .../java/android/bluetooth/BluetoothPbap.java | 257 +++++ .../bluetooth/BluetoothServerSocket.java | 122 +++ .../android/bluetooth/BluetoothSocket.java | 387 +++++++ .../java/android/bluetooth/BluetoothUuid.java | 153 +++ .../java/android/bluetooth/Database.java | 200 ---- .../java/android/bluetooth/HeadsetBase.java | 38 +- ...{IBluetoothDevice.aidl => IBluetooth.aidl} | 42 +- .../android/bluetooth/IBluetoothA2dp.aidl | 16 +- ...eCallback.aidl => IBluetoothCallback.aidl} | 8 +- .../android/bluetooth/IBluetoothHeadset.aidl | 12 +- ...luetoothError.java => IBluetoothPbap.aidl} | 30 +- .../java/android/bluetooth/RfcommSocket.java | 674 ------------- .../java/android/bluetooth/ScoSocket.java | 2 +- framework/java/android/bluetooth/package.html | 112 ++- 25 files changed, 3149 insertions(+), 1738 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdapter.java create mode 100644 framework/java/android/bluetooth/BluetoothDevice.aidl create mode 100644 framework/java/android/bluetooth/BluetoothDevicePicker.java create mode 100644 framework/java/android/bluetooth/BluetoothInputStream.java delete mode 100644 framework/java/android/bluetooth/BluetoothIntent.java create mode 100644 framework/java/android/bluetooth/BluetoothOutputStream.java create mode 100644 framework/java/android/bluetooth/BluetoothPbap.java create mode 100644 framework/java/android/bluetooth/BluetoothServerSocket.java create mode 100644 framework/java/android/bluetooth/BluetoothSocket.java create mode 100644 framework/java/android/bluetooth/BluetoothUuid.java delete mode 100644 framework/java/android/bluetooth/Database.java rename framework/java/android/bluetooth/{IBluetoothDevice.aidl => IBluetooth.aidl} (58%) rename framework/java/android/bluetooth/{IBluetoothDeviceCallback.aidl => IBluetoothCallback.aidl} (76%) rename framework/java/android/bluetooth/{BluetoothError.java => IBluetoothPbap.aidl} (54%) delete mode 100644 framework/java/android/bluetooth/RfcommSocket.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 2ea45d567f6..e8a69d8c1f7 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -25,7 +25,10 @@ import android.os.RemoteException; import android.os.IBinder; import android.util.Log; -import java.util.List; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; /** * Public API for controlling the Bluetooth A2DP Profile Service. @@ -39,32 +42,30 @@ import java.util.List; * * Currently the BluetoothA2dp service runs in the system server and this * proxy object will be immediately bound to the service on construction. - * However this may change in future releases, and error codes such as - * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the - * proxy object is not yet attached. * * Currently this class provides methods to connect to A2DP audio sinks. * * @hide */ -public class BluetoothA2dp { +public final class BluetoothA2dp { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = false; - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_STATE = - "android.bluetooth.a2dp.intent.SINK_STATE"; - /** int extra for SINK_STATE_CHANGED_ACTION */ - public static final String SINK_PREVIOUS_STATE = - "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE"; + /** int extra for ACTION_SINK_STATE_CHANGED */ + public static final String EXTRA_SINK_STATE = + "android.bluetooth.a2dp.extra.SINK_STATE"; + /** int extra for ACTION_SINK_STATE_CHANGED */ + public static final String EXTRA_PREVIOUS_SINK_STATE = + "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE"; /** Indicates the state of an A2DP audio sink has changed. - * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. + * This intent will always contain EXTRA_SINK_STATE, + * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE + * extras. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SINK_STATE_CHANGED_ACTION = - "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED"; + public static final String ACTION_SINK_STATE_CHANGED = + "android.bluetooth.a2dp.action.SINK_STATE_CHANGED"; public static final int STATE_DISCONNECTED = 0; public static final int STATE_CONNECTING = 1; @@ -79,6 +80,7 @@ public class BluetoothA2dp { /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; + private final IBluetoothA2dp mService; private final Context mContext; @@ -89,84 +91,123 @@ public class BluetoothA2dp { */ public BluetoothA2dp(Context c) { mContext = c; + IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); - if (b == null) { - throw new RuntimeException("Bluetooth A2DP service not available!"); + if (b != null) { + mService = IBluetoothA2dp.Stub.asInterface(b); + } else { + Log.w(TAG, "Bluetooth A2DP service not available!"); + + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later when it is actually used. + mService = null; } - mService = IBluetoothA2dp.Stub.asInterface(b); } /** Initiate a connection to an A2DP sink. * Listen for SINK_STATE_CHANGED_ACTION to find out when the * connection is completed. - * @param address Remote BT address. - * @return Result code, negative indicates an immediate error. + * @param device Remote BT device. + * @return false on immediate error, true otherwise * @hide */ - public int connectSink(String address) { - if (DBG) log("connectSink(" + address + ")"); + public boolean connectSink(BluetoothDevice device) { + if (DBG) log("connectSink(" + device + ")"); try { - return mService.connectSink(address); + return mService.connectSink(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } /** Initiate disconnect from an A2DP sink. * Listen for SINK_STATE_CHANGED_ACTION to find out when * disconnect is completed. - * @param address Remote BT address. - * @return Result code, negative indicates an immediate error. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean disconnectSink(BluetoothDevice device) { + if (DBG) log("disconnectSink(" + device + ")"); + try { + return mService.disconnectSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Initiate suspend from an A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * suspend is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean suspendSink(BluetoothDevice device) { + try { + return mService.suspendSink(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Initiate resume from an suspended A2DP sink. + * Listen for SINK_STATE_CHANGED_ACTION to find out when + * resume is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise * @hide */ - public int disconnectSink(String address) { - if (DBG) log("disconnectSink(" + address + ")"); + public boolean resumeSink(BluetoothDevice device) { try { - return mService.disconnectSink(address); + return mService.resumeSink(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } /** Check if a specified A2DP sink is connected. - * @param address Remote BT address. + * @param device Remote BT device. * @return True if connected (or playing), false otherwise and on error. * @hide */ - public boolean isSinkConnected(String address) { - if (DBG) log("isSinkConnected(" + address + ")"); - int state = getSinkState(address); + public boolean isSinkConnected(BluetoothDevice device) { + if (DBG) log("isSinkConnected(" + device + ")"); + int state = getSinkState(device); return state == STATE_CONNECTED || state == STATE_PLAYING; } /** Check if any A2DP sink is connected. - * @return a List of connected A2DP sinks, or null on error. + * @return a unmodifiable set of connected A2DP sinks, or null on error. * @hide */ - public List listConnectedSinks() { - if (DBG) log("listConnectedSinks()"); + public Set getConnectedSinks() { + if (DBG) log("getConnectedSinks()"); try { - return mService.listConnectedSinks(); + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(mService.getConnectedSinks()))); } catch (RemoteException e) { - Log.w(TAG, "", e); + Log.e(TAG, "", e); return null; } } /** Get the state of an A2DP sink - * @param address Remote BT address. - * @return State code, or negative on error + * @param device Remote BT device. + * @return State code, one of STATE_ * @hide */ - public int getSinkState(String address) { - if (DBG) log("getSinkState(" + address + ")"); + public int getSinkState(BluetoothDevice device) { + if (DBG) log("getSinkState(" + device + ")"); try { - return mService.getSinkState(address); + return mService.getSinkState(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return BluetoothA2dp.STATE_DISCONNECTED; } } @@ -177,58 +218,33 @@ public class BluetoothA2dp { * Sinks with priority greater than zero will accept incoming connections * (if no sink is currently connected). * Priority for unpaired sink must be PRIORITY_NONE. - * @param address Paired sink + * @param device Paired sink * @param priority Integer priority, for example PRIORITY_AUTO or * PRIORITY_NONE - * @return Result code, negative indicates an error + * @return true if priority is set, false on error */ - public int setSinkPriority(String address, int priority) { - if (DBG) log("setSinkPriority(" + address + ", " + priority + ")"); + public boolean setSinkPriority(BluetoothDevice device, int priority) { + if (DBG) log("setSinkPriority(" + device + ", " + priority + ")"); try { - return mService.setSinkPriority(address, priority); + return mService.setSinkPriority(device, priority); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; + Log.e(TAG, "", e); + return false; } } /** * Get priority of a2dp sink. - * @param address Sink + * @param device Sink * @return non-negative priority, or negative error code on error. */ - public int getSinkPriority(String address) { - if (DBG) log("getSinkPriority(" + address + ")"); + public int getSinkPriority(BluetoothDevice device) { + if (DBG) log("getSinkPriority(" + device + ")"); try { - return mService.getSinkPriority(address); + return mService.getSinkPriority(device); } catch (RemoteException e) { - Log.w(TAG, "", e); - return BluetoothError.ERROR_IPC; - } - } - - /** - * Check class bits for possible A2DP Sink support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might be a A2DP Sink. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might be a A2DP sink - */ - public static boolean doesClassMatchSink(int btClass) { - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // By the A2DP spec, sinks must indicate the RENDER service. - // However we found some that do not (Chordette). So lets also - // match on some other class bits. - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: - case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; + Log.e(TAG, "", e); + return PRIORITY_OFF; } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java new file mode 100644 index 00000000000..bd5b07cff64 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -0,0 +1,849 @@ +/* + * Copyright (C) 2009 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Random; +import java.util.Set; +import java.util.UUID; + +/** + * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} + * lets you perform fundamental Bluetooth tasks, such as initiate + * device discovery, query a list of bonded (paired) devices, + * instantiate a {@link BluetoothDevice} using a known MAC address, and create + * a {@link BluetoothServerSocket} to listen for connection requests from other + * devices. + * + *

To get a {@link BluetoothAdapter} representing the local Bluetooth + * adapter, call the static {@link #getDefaultAdapter} method. + * Fundamentally, this is your starting point for all + * Bluetooth actions. Once you have the local adapter, you can get a set of + * {@link BluetoothDevice} objects representing all paired devices with + * {@link #getBondedDevices()}; start device discovery with + * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to + * listen for incoming connection requests with + * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}. + * + *

Note: + * Most methods require the {@link android.Manifest.permission#BLUETOOTH} + * permission and some also require the + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * {@see BluetoothDevice} + * {@see BluetoothServerSocket} + */ +public final class BluetoothAdapter { + private static final String TAG = "BluetoothAdapter"; + private static final boolean DBG = false; + + /** + * Sentinel error value for this class. Guaranteed to not equal any other + * integer constant in this class. Provided as a convenience for functions + * that require a sentinel error value, for example: + *

Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + * BluetoothAdapter.ERROR) + */ + public static final int ERROR = Integer.MIN_VALUE; + + /** + * Broadcast Action: The state of the local Bluetooth adapter has been + * changed. + *

For example, Bluetooth has been turned on or off. + *

Always contains the extra fields {@link #EXTRA_STATE} and {@link + * #EXTRA_PREVIOUS_STATE} containing the new and old states + * respectively. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STATE_CHANGED = + "android.bluetooth.adapter.action.STATE_CHANGED"; + + /** + * Used as an int extra field in {@link #ACTION_STATE_CHANGED} + * intents to request the current power state. Possible values are: + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + */ + public static final String EXTRA_STATE = + "android.bluetooth.adapter.extra.STATE"; + /** + * Used as an int extra field in {@link #ACTION_STATE_CHANGED} + * intents to request the previous power state. Possible values are: + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + */ + public static final String EXTRA_PREVIOUS_STATE = + "android.bluetooth.adapter.extra.PREVIOUS_STATE"; + + /** + * Indicates the local Bluetooth adapter is off. + */ + public static final int STATE_OFF = 10; + /** + * Indicates the local Bluetooth adapter is turning on. However local + * clients should wait for {@link #STATE_ON} before attempting to + * use the adapter. + */ + public static final int STATE_TURNING_ON = 11; + /** + * Indicates the local Bluetooth adapter is on, and ready for use. + */ + public static final int STATE_ON = 12; + /** + * Indicates the local Bluetooth adapter is turning off. Local clients + * should immediately attempt graceful disconnection of any remote links. + */ + public static final int STATE_TURNING_OFF = 13; + + /** + * Activity Action: Show a system activity that requests discoverable mode. + *

This activity will also request the user to turn on Bluetooth if it + * is not currently enabled. + *

Discoverable mode is equivalent to {@link + * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see + * this Bluetooth adapter when they perform a discovery. + *

For privacy, Android is not by default discoverable. + *

The sender can optionally use extra field {@link + * #EXTRA_DISCOVERABLE_DURATION} to request the duration of + * discoverability. Currently the default duration is 120 seconds, and + * maximum duration is capped at 300 seconds for each request. + *

Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be the duration (in seconds) of discoverability or + * {@link android.app.Activity#RESULT_CANCELED} if the user rejected + * discoverability or an error has occurred. + *

Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} + * for global notification whenever the scan mode changes. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISCOVERABLE = + "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; + + /** + * Used as an optional int extra field in {@link + * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration + * for discoverability in seconds. The current default is 120 seconds, and + * requests over 300 seconds will be capped. These values could change. + */ + public static final String EXTRA_DISCOVERABLE_DURATION = + "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + + /** + * Activity Action: Show a system activity that allows the user to turn on + * Bluetooth. + *

This system activity will return once Bluetooth has completed turning + * on, or the user has decided not to turn Bluetooth on. + *

Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_ENABLE = + "android.bluetooth.adapter.action.REQUEST_ENABLE"; + + /** + * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter + * has changed. + *

Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link + * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes + * respectively. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SCAN_MODE_CHANGED = + "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; + + /** + * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} + * intents to request the current scan mode. Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, + */ + public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE"; + /** + * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} + * intents to request the previous scan mode. Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, + */ + public static final String EXTRA_PREVIOUS_SCAN_MODE = + "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; + + /** + * Indicates that both inquiry scan and page scan are disabled on the local + * Bluetooth adapter. Therefore this device is neither discoverable + * nor connectable from remote Bluetooth devices. + */ + public static final int SCAN_MODE_NONE = 20; + /** + * Indicates that inquiry scan is disabled, but page scan is enabled on the + * local Bluetooth adapter. Therefore this device is not discoverable from + * remote Bluetooth devices, but is connectable from remote devices that + * have previously discovered this device. + */ + public static final int SCAN_MODE_CONNECTABLE = 21; + /** + * Indicates that both inquiry scan and page scan are enabled on the local + * Bluetooth adapter. Therefore this device is both discoverable and + * connectable from remote Bluetooth devices. + */ + public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; + + + /** + * Broadcast Action: The local Bluetooth adapter has started the remote + * device discovery process. + *

This usually involves an inquiry scan of about 12 seconds, followed + * by a page scan of each new device to retrieve its Bluetooth name. + *

Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as + * remote Bluetooth devices are found. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISCOVERY_STARTED = + "android.bluetooth.adapter.action.DISCOVERY_STARTED"; + /** + * Broadcast Action: The local Bluetooth adapter has finished the device + * discovery process. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISCOVERY_FINISHED = + "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; + + /** + * Broadcast Action: The local Bluetooth adapter has changed its friendly + * Bluetooth name. + *

This name is visible to remote Bluetooth devices. + *

Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing + * the name. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LOCAL_NAME_CHANGED = + "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; + /** + * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED} + * intents to request the local Bluetooth name. + */ + public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; + + /** @hide */ + public static final String BLUETOOTH_SERVICE = "bluetooth"; + + private static final int ADDRESS_LENGTH = 17; + + /** + * Lazyily initialized singleton. Guaranteed final after first object + * constructed. + */ + private static BluetoothAdapter sAdapter; + + private final IBluetooth mService; + + /** + * Get a handle to the default local Bluetooth adapter. + *

Currently Android only supports one Bluetooth adapter, but the API + * could be extended to support more. This will always return the default + * adapter. + * @return the default local adapter, or null if Bluetooth is not supported + * on this hardware platform + */ + public static synchronized BluetoothAdapter getDefaultAdapter() { + if (sAdapter == null) { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b != null) { + IBluetooth service = IBluetooth.Stub.asInterface(b); + sAdapter = new BluetoothAdapter(service); + } + } + return sAdapter; + } + + /** + * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. + * @hide + */ + public BluetoothAdapter(IBluetooth service) { + if (service == null) { + throw new IllegalArgumentException("service is null"); + } + mService = service; + } + + /** + * Get a {@link BluetoothDevice} object for the given Bluetooth hardware + * address. + *

Valid Bluetooth hardware addresses must be upper case, in a format + * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is + * available to validate a Bluetooth address. + *

A {@link BluetoothDevice} will always be returned for a valid + * hardware address, even if this adapter has never seen that device. + * + * @param address valid Bluetooth MAC address + * @throws IllegalArgumentException if address is invalid + */ + public BluetoothDevice getRemoteDevice(String address) { + return new BluetoothDevice(address); + } + + /** + * Return true if Bluetooth is currently enabled and ready for use. + *

Equivalent to: + * getBluetoothState() == STATE_ON + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return true if the local adapter is turned on + */ + public boolean isEnabled() { + try { + return mService.isEnabled(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the current state of the local Bluetooth adapter. + *

Possible return values are + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return current state of Bluetooth adapter + */ + public int getState() { + try { + return mService.getBluetoothState(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return STATE_OFF; + } + + /** + * Turn on the local Bluetooth adapter. + *

This powers on the underlying Bluetooth hardware, and starts all + * Bluetooth system services. + *

This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned on - + * such as Airplane mode, or the adapter is already turned on. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @return true to indicate adapter startup has begun, or false on + * immediate error + */ + public boolean enable() { + try { + return mService.enable(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Turn off the local Bluetooth adapter. + *

This gracefully shuts down all Bluetooth connections, stops Bluetooth + * system services, and powers down the underlying Bluetooth hardware. + *

This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned off - + * such as the adapter already being turned off. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @return true to indicate adapter shutdown has begun, or false on + * immediate error + */ + public boolean disable() { + try { + return mService.disable(true); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Returns the hardware address of the local Bluetooth adapter. + *

For example, "00:11:22:AA:BB:CC". + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return Bluetooth hardware address as string + */ + public String getAddress() { + try { + return mService.getAddress(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Get the friendly Bluetooth name of the local Bluetooth adapter. + *

This name is visible to remote Bluetooth devices. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return the Bluetooth name, or null on error + */ + public String getName() { + try { + return mService.getName(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Set the friendly Bluetooth name of the local Bluetoth adapter. + *

This name is visible to remote Bluetooth devices. + *

Valid Bluetooth names are a maximum of 248 UTF-8 characters, however + * many remote devices can only display the first 40 characters, and some + * may be limited to just 20. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param name a valid Bluetooth name + * @return true if the name was set, false otherwise + */ + public boolean setName(String name) { + try { + return mService.setName(name); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the current Bluetooth scan mode of the local Bluetooth adaper. + *

The Bluetooth scan mode determines if the local adapter is + * connectable and/or discoverable from remote Bluetooth devices. + *

Possible values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return scan mode + */ + public int getScanMode() { + try { + return mService.getScanMode(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return SCAN_MODE_NONE; + } + + /** + * Set the Bluetooth scan mode of the local Bluetooth adapter. + *

The Bluetooth scan mode determines if the local adapter is + * connectable and/or discoverable from remote Bluetooth devices. + *

For privacy reasons, discoverable mode is automatically turned off + * after duration seconds. For example, 120 seconds should be + * enough for a remote device to initiate and complete its discovery + * process. + *

Valid scan mode values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} + *

Applications cannot set the scan mode. They should use + * startActivityForResult( + * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) + * instead. + * + * @param mode valid scan mode + * @param duration time in seconds to apply scan mode, only used for + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} + * @return true if the scan mode was set, false otherwise + * @hide + */ + public boolean setScanMode(int mode, int duration) { + try { + return mService.setScanMode(mode, duration); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** @hide */ + public boolean setScanMode(int mode) { + return setScanMode(mode, 120); + } + + /** @hide */ + public int getDiscoverableTimeout() { + try { + return mService.getDiscoverableTimeout(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return -1; + } + + /** @hide */ + public void setDiscoverableTimeout(int timeout) { + try { + mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + /** + * Start the remote device discovery process. + *

The discovery process usually involves an inquiry scan of about 12 + * seconds, followed by a page scan of each new device to retrieve its + * Bluetooth name. + *

This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_DISCOVERY_STARTED} and {@link + * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the + * discovery starts and completes. Register for {@link + * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices + * are found. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Device discovery will only find remote devices that are currently + * discoverable (inquiry scan enabled). Many Bluetooth devices are + * not discoverable by default, and need to be entered into a special mode. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @return true on success, false on error + */ + public boolean startDiscovery() { + try { + return mService.startDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Cancel the current device discovery process. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @return true on success, false on error + */ + public boolean cancelDiscovery() { + try { + mService.cancelDiscovery(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Return true if the local Bluetooth adapter is currently in the device + * discovery process. + *

Device discovery is a heavyweight procedure. New connections to + * remote Bluetooth devices should not be attempted while discovery is in + * progress, and existing connections will experience limited bandwidth + * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing + * discovery. + *

Applications can also register for {@link #ACTION_DISCOVERY_STARTED} + * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery + * starts or completes. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return true if discovering + */ + public boolean isDiscovering() { + try { + return mService.isDiscovering(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Return the set of {@link BluetoothDevice} objects that are bonded + * (paired) to the local adapter. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return unmodifiable set of {@link BluetoothDevice}, or null on error + */ + public Set getBondedDevices() { + try { + return toDeviceSet(mService.listBonds()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Picks RFCOMM channels until none are left. + * Avoids reserved channels. + */ + private static class RfcommChannelPicker { + private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { + 10, // HFAG + 11, // HSAG + 12, // OPUSH + 19, // PBAP + }; + private static LinkedList sChannels; // master list of non-reserved channels + private static Random sRandom; + + private final LinkedList mChannels; // local list of channels left to try + + private final UUID mUuid; + + public RfcommChannelPicker(UUID uuid) { + synchronized (RfcommChannelPicker.class) { + if (sChannels == null) { + // lazy initialization of non-reserved rfcomm channels + sChannels = new LinkedList(); + for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) { + sChannels.addLast(new Integer(i)); + } + for (int reserved : RESERVED_RFCOMM_CHANNELS) { + sChannels.remove(new Integer(reserved)); + } + sRandom = new Random(); + } + mChannels = (LinkedList)sChannels.clone(); + } + mUuid = uuid; + } + /* Returns next random channel, or -1 if we're out */ + public int nextChannel() { + if (mChannels.size() == 0) { + return -1; + } + return mChannels.remove(sRandom.nextInt(mChannels.size())); + } + } + + /** + * Create a listening, secure RFCOMM Bluetooth socket. + *

A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

Valid RFCOMM channels are in range 1 to 30. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * @param channel RFCOMM channel to listen on + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, channel); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + + /** + * Create a listening, secure RFCOMM Bluetooth socket with Service Record. + *

A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

The system will assign an unused RFCOMM channel to listen on. + *

The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) + throws IOException { + RfcommChannelPicker picker = new RfcommChannelPicker(uuid); + + BluetoothServerSocket socket; + int channel; + int errno; + while (true) { + channel = picker.nextChannel(); + + if (channel == -1) { + throw new IOException("No available channels"); + } + + socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, channel); + errno = socket.mSocket.bindListen(); + if (errno == 0) { + if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); + break; // success + } else if (errno == BluetoothSocket.EADDRINUSE) { + if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use"); + try { + socket.close(); + } catch (IOException e) {} + continue; // try another channel + } else { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() + } + } + + int handle = -1; + try { + handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel, + new Binder()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + if (handle == -1) { + try { + socket.close(); + } catch (IOException e) {} + throw new IOException("Not able to register SDP record for " + name); + } + socket.setCloseHandler(mHandler, handle); + return socket; + } + + /** + * Construct an unencrypted, unauthenticated, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, false, false, port); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + + /** + * Construct a SCO server socket. + * Call #accept to retrieve connections to this socket. + * @return A SCO BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public static BluetoothServerSocket listenUsingScoOn() throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_SCO, false, false, -1); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + + private Set toDeviceSet(String[] addresses) { + Set devices = new HashSet(addresses.length); + for (int i = 0; i < addresses.length; i++) { + devices.add(getRemoteDevice(addresses[i])); + } + return Collections.unmodifiableSet(devices); + } + + private Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + /* handle socket closing */ + int handle = msg.what; + try { + if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle)); + mService.removeServiceRecord(handle); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + }; + + /** + * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + *

Alphabetic characters must be uppercase to be valid. + * + * @param address Bluetooth address as string + * @return true if the address is valid, false otherwise + */ + public static boolean checkBluetoothAddress(String address) { + if (address == null || address.length() != ADDRESS_LENGTH) { + return false; + } + for (int i = 0; i < ADDRESS_LENGTH; i++) { + char c = address.charAt(i); + switch (i % 3) { + case 0: + case 1: + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + // hex character, OK + break; + } + return false; + case 2: + if (c == ':') { + break; // OK + } + return false; + } + } + return true; + } +} diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java index f3afd2a4a0b..abd7723c57c 100644 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -9,24 +9,22 @@ import android.util.Log; /** * Listen's for incoming RFCOMM connection for the headset / handsfree service. * - * This class is planned for deletion, in favor of a generic Rfcomm class. + * TODO: Use the new generic BluetoothSocket class instead of this legacy code * * @hide */ -public class BluetoothAudioGateway { +public final class BluetoothAudioGateway { private static final String TAG = "BT Audio Gateway"; private static final boolean DBG = false; private int mNativeData; static { classInitNative(); } - private BluetoothDevice mBluetooth; - /* in */ private int mHandsfreeAgRfcommChannel = -1; private int mHeadsetAgRfcommChannel = -1; - /* out */ + /* out - written by native code */ private String mConnectingHeadsetAddress; private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ private int mConnectingHeadsetSocketFd; @@ -35,17 +33,18 @@ public class BluetoothAudioGateway { private int mConnectingHandsfreeSocketFd; private int mTimeoutRemainingMs; /* in/out */ + private final BluetoothAdapter mAdapter; + public static final int DEFAULT_HF_AG_CHANNEL = 10; public static final int DEFAULT_HS_AG_CHANNEL = 11; - public BluetoothAudioGateway(BluetoothDevice bluetooth) { - this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); + public BluetoothAudioGateway(BluetoothAdapter adapter) { + this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); } - public BluetoothAudioGateway(BluetoothDevice bluetooth, - int handsfreeAgRfcommChannel, - int headsetAgRfcommChannel) { - mBluetooth = bluetooth; + public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel, + int headsetAgRfcommChannel) { + mAdapter = adapter; mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; mHeadsetAgRfcommChannel = headsetAgRfcommChannel; initializeNativeDataNative(); @@ -58,18 +57,17 @@ public class BluetoothAudioGateway { private Handler mCallback; public class IncomingConnectionInfo { - IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChan) { - mBluetooth = bluetooth; - mAddress = address; + public BluetoothAdapter mAdapter; + public BluetoothDevice mRemoteDevice; + public int mSocketFd; + public int mRfcommChan; + IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice, + int socketFd, int rfcommChan) { + mAdapter = adapter; + mRemoteDevice = remoteDevice; mSocketFd = socketFd; mRfcommChan = rfcommChan; } - - public BluetoothDevice mBluetooth; - public String mAddress; - public int mSocketFd; - public int mRfcommChan; } public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; @@ -111,12 +109,11 @@ public class BluetoothAudioGateway { mConnectingHeadsetRfcommChannel); Message msg = Message.obtain(mCallback); msg.what = MSG_INCOMING_HEADSET_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHeadsetAddress, - mConnectingHeadsetSocketFd, - mConnectingHeadsetRfcommChannel); + msg.obj = new IncomingConnectionInfo( + mAdapter, + mAdapter.getRemoteDevice(mConnectingHeadsetAddress), + mConnectingHeadsetSocketFd, + mConnectingHeadsetRfcommChannel); msg.sendToTarget(); } if (mConnectingHandsfreeRfcommChannel >= 0) { @@ -126,12 +123,11 @@ public class BluetoothAudioGateway { Message msg = Message.obtain(); msg.setTarget(mCallback); msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; - msg.obj = - new IncomingConnectionInfo( - mBluetooth, - mConnectingHandsfreeAddress, - mConnectingHandsfreeSocketFd, - mConnectingHandsfreeRfcommChannel); + msg.obj = new IncomingConnectionInfo( + mAdapter, + mAdapter.getRemoteDevice(mConnectingHandsfreeAddress), + mConnectingHandsfreeSocketFd, + mConnectingHandsfreeRfcommChannel); msg.sendToTarget(); } } diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 88ce18b4577..bc067130584 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,39 +16,98 @@ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; + /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Static helper methods and constants to decode the device class bit vector - * returned by the Bluetooth API. + * Represents a Bluetooth class, which describes general characteristics + * and capabilities of a device. For example, a Bluetooth class will + * specify the general device type such as a phone, a computer, or + * headset, and whether it's capable of services such as audio or telephony. * - * The Android Bluetooth API returns a 32-bit integer to represent the class. - * The format of these bits is defined at - * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class provides static helper methods and constants to - * determine what Service Class(es) and Device Class are encoded in the 32-bit - * class. + *

The Bluetooth class is useful as a hint to roughly describe a device (for example to + * show an icon in the UI), but does not reliably describe which Bluetooth + * profiles or services are actually supported by a device. * - * Devices typically have zero or more service classes, and exactly one device - * class. The device class is encoded as a major and minor device class, the - * minor being a subset of the major. + *

Every Bluetooth class is composed of zero or more service classes, and + * exactly one device class. The device class is further broken down into major + * and minor device class components. * - * Class is useful to describe a device (for example to show an icon), - * but does not reliably describe what profiles a device supports. To determine - * profile support you usually need to perform SDP queries. + *

{@link BluetoothClass} is useful as a hint to roughly describe a device + * (for example to show an icon in the UI), but does not reliably describe which + * Bluetooth profiles or services are actually supported by a device. Accurate + * service discovery is done through SDP requests, which are automatically + * performed when creating an RFCOMM socket with {@link + * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}

* - * Each of these helper methods takes the 32-bit integer class as an argument. + *

Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for + * a remote device. * - * @hide + * */ -public class BluetoothClass { - /** Indicates the Bluetooth API could not retrieve the class */ +public final class BluetoothClass implements Parcelable { + /** + * Legacy error value. Applications should use null instead. + * @hide + */ public static final int ERROR = 0xFF000000; - /** Every Bluetooth device has zero or more service classes */ - public static class Service { - public static final int BITMASK = 0xFFE000; + private final int mClass; + + /** @hide */ + public BluetoothClass(int classInt) { + mClass = classInt; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothClass) { + return mClass == ((BluetoothClass)o).mClass; + } + return false; + } + + @Override + public int hashCode() { + return mClass; + } + + @Override + public String toString() { + return Integer.toHexString(mClass); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothClass createFromParcel(Parcel in) { + return new BluetoothClass(in.readInt()); + } + public BluetoothClass[] newArray(int size) { + return new BluetoothClass[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mClass); + } + + /** + * Defines all service class constants. + *

Each {@link BluetoothClass} encodes zero or more service classes. + */ + public static final class Service { + private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; public static final int POSITIONING = 0x010000; @@ -59,32 +118,41 @@ public class BluetoothClass { public static final int AUDIO = 0x200000; public static final int TELEPHONY = 0x400000; public static final int INFORMATION = 0x800000; + } - /** Returns true if the given class supports the given Service Class. - * A bluetooth device can claim to support zero or more service classes. - * @param btClass The bluetooth class. - * @param serviceClass The service class constant to test for. For - * example, Service.AUDIO. Must be one of the - * Service.FOO constants. - * @return True if the service class is supported. - */ - public static boolean hasService(int btClass, int serviceClass) { - if (btClass == ERROR) { - return false; - } - return ((btClass & Service.BITMASK & serviceClass) != 0); - } + /** + * Return true if the specified service class is supported by this + * {@link BluetoothClass}. + *

Valid service classes are the public constants in + * {@link BluetoothClass.Service}. For example, {@link + * BluetoothClass.Service#AUDIO}. + * + * @param service valid service class + * @return true if the service class is supported + */ + public boolean hasService(int service) { + return ((mClass & Service.BITMASK & service) != 0); } - /** Every Bluetooth device has exactly one device class, comprimised of - * major and minor components. We have not included the minor classes for - * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work - * a little differently. */ + /** + * Defines all device class constants. + *

Each {@link BluetoothClass} encodes exactly one device class, with + * major and minor components. + *

The constants in {@link + * BluetoothClass.Device} represent a combination of major and minor + * device components (the complete device class). The constants in {@link + * BluetoothClass.Device.Major} represent only major device classes. + *

See {@link BluetoothClass.Service} for service class constants. + */ public static class Device { - public static final int BITMASK = 0x1FFC; + private static final int BITMASK = 0x1FFC; + /** + * Defines all major device class constants. + *

See {@link BluetoothClass.Device} for minor classes. + */ public static class Major { - public static final int BITMASK = 0x1F00; + private static final int BITMASK = 0x1F00; public static final int MISC = 0x0000; public static final int COMPUTER = 0x0100; @@ -97,18 +165,6 @@ public class BluetoothClass { public static final int TOY = 0x0800; public static final int HEALTH = 0x0900; public static final int UNCATEGORIZED = 0x1F00; - - /** Returns the Major Device Class component of a bluetooth class. - * Values returned from this function can be compared with the constants - * Device.Major.FOO. A bluetooth device can only be associated - * with one major class. - */ - public static int getDeviceMajor(int btClass) { - if (btClass == ERROR) { - return ERROR; - } - return (btClass & Device.Major.BITMASK); - } } // Devices in the COMPUTER major class @@ -174,18 +230,106 @@ public class BluetoothClass { public static final int HEALTH_PULSE_OXIMETER = 0x0914; public static final int HEALTH_PULSE_RATE = 0x0918; public static final int HEALTH_DATA_DISPLAY = 0x091C; + } - /** Returns the Device Class component of a bluetooth class. This includes - * both the major and minor device components. Values returned from this - * function can be compared with the constants Device.FOO. A bluetooth - * device can only be associated with one device class. - */ - public static int getDevice(int btClass) { - if (btClass == ERROR) { - return ERROR; + /** + * Return the major device class component of this {@link BluetoothClass}. + *

Values returned from this function can be compared with the + * public constants in {@link BluetoothClass.Device.Major} to determine + * which major class is encoded in this Bluetooth class. + * + * @return major device class component + */ + public int getMajorDeviceClass() { + return (mClass & Device.Major.BITMASK); + } + + /** + * Return the (major and minor) device class component of this + * {@link BluetoothClass}. + *

Values returned from this function can be compared with the + * public constants in {@link BluetoothClass.Device} to determine which + * device class is encoded in this Bluetooth class. + * + * @return device class component + */ + public int getDeviceClass() { + return (mClass & Device.BITMASK); + } + + /** @hide */ + public static final int PROFILE_HEADSET = 0; + /** @hide */ + public static final int PROFILE_A2DP = 1; + /** @hide */ + public static final int PROFILE_OPP = 2; + + /** + * Check class bits for possible bluetooth profile support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support specified profile. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @param profile The profile to be checked + * @return True if this device might support specified profile. + * @hide + */ + public boolean doesClassMatch(int profile) { + if (profile == PROFILE_A2DP) { + if (hasService(Service.RENDER)) { + return true; + } + // By the A2DP spec, sinks must indicate the RENDER service. + // However we found some that do not (Chordette). So lets also + // match on some other class bits. + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_HEADPHONES: + case Device.AUDIO_VIDEO_LOUDSPEAKER: + case Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } else if (profile == PROFILE_HEADSET) { + // The render service class is required by the spec for HFP, so is a + // pretty good signal + if (hasService(Service.RENDER)) { + return true; } - return (btClass & Device.BITMASK); + // Just in case they forgot the render service class + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HANDSFREE: + case Device.AUDIO_VIDEO_WEARABLE_HEADSET: + case Device.AUDIO_VIDEO_CAR_AUDIO: + return true; + default: + return false; + } + } else if (profile == PROFILE_OPP) { + if (hasService(Service.OBJECT_TRANSFER)) { + return true; + } + + switch (getDeviceClass()) { + case Device.COMPUTER_UNCATEGORIZED: + case Device.COMPUTER_DESKTOP: + case Device.COMPUTER_SERVER: + case Device.COMPUTER_LAPTOP: + case Device.COMPUTER_HANDHELD_PC_PDA: + case Device.COMPUTER_PALM_SIZE_PC_PDA: + case Device.COMPUTER_WEARABLE: + case Device.PHONE_UNCATEGORIZED: + case Device.PHONE_CELLULAR: + case Device.PHONE_CORDLESS: + case Device.PHONE_SMART: + case Device.PHONE_MODEM_OR_GATEWAY: + case Device.PHONE_ISDN: + return true; + default: + return false; + } + } else { + return false; } } } - diff --git a/framework/java/android/bluetooth/BluetoothDevice.aidl b/framework/java/android/bluetooth/BluetoothDevice.aidl new file mode 100644 index 00000000000..daae74d52c4 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevice.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009 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; + +parcelable BluetoothDevice; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 951b4b0ab47..6cb9770a610 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 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. @@ -16,515 +16,689 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; +import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.UUID; /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. + * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you + * create a connection with the repective device or query information about + * it, such as the name, address, class, and bonding state. * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. + *

This class is really just a thin wrapper for a Bluetooth hardware + * address. Objects of this class are immutable. Operations on this class + * are performed on the remote Bluetooth hardware address, using the + * {@link BluetoothAdapter} that was used to create this {@link + * BluetoothDevice}. * - * @hide + *

To get a {@link BluetoothDevice}, use + * {@link BluetoothAdapter#getRemoteDevice(String) + * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device + * of a known MAC address (which you can get through device discovery with + * {@link BluetoothAdapter}) or get one from the set of bonded devices + * returned by {@link BluetoothAdapter#getBondedDevices() + * BluetoothAdapter.getBondedDevices()}. You can then open a + * {@link BluetoothSocket} for communciation with the remote device, using + * {@link #createRfcommSocketToServiceRecord(UUID)}. + * + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothAdapter} + * {@see BluetoothSocket} */ -public class BluetoothDevice { - - public static final int BLUETOOTH_STATE_OFF = 0; - public static final int BLUETOOTH_STATE_TURNING_ON = 1; - public static final int BLUETOOTH_STATE_ON = 2; - public static final int BLUETOOTH_STATE_TURNING_OFF = 3; - - /** Inquiry scan and page scan are both off. - * Device is neither discoverable nor connectable */ - public static final int SCAN_MODE_NONE = 0; - /** Page scan is on, inquiry scan is off. - * Device is connectable, but not discoverable */ - public static final int SCAN_MODE_CONNECTABLE = 1; - /** Page scan and inquiry scan are on. - * Device is connectable and discoverable */ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; - - public static final int RESULT_FAILURE = -1; - public static final int RESULT_SUCCESS = 0; - - /** We do not have a link key for the remote device, and are therefore not - * bonded */ - public static final int BOND_NOT_BONDED = 0; - /** We have a link key for the remote device, and are probably bonded. */ - public static final int BOND_BONDED = 1; - /** We are currently attempting bonding */ - public static final int BOND_BONDING = 2; - - //TODO: Unify these result codes in BluetoothResult or BluetoothError - /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time */ - public static final int UNBOND_REASON_AUTH_FAILED = 1; - /** A bond attempt failed because the other side explicilty rejected - * bonding */ - public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we canceled the bonding process */ - public static final int UNBOND_REASON_AUTH_CANCELED = 3; - /** A bond attempt failed because we could not contact the remote device */ - public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** A bond attempt failed because a discovery is in progress */ - public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; - /** An existing bond was explicitly revoked */ - public static final int UNBOND_REASON_REMOVED = 6; - +public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; - - private final IBluetoothDevice mService; + /** - * @hide - hide this because it takes a parameter of type - * IBluetoothDevice, which is a System private class. - * Also note that Context.getSystemService is a factory that - * returns a BlueToothDevice. That is the right way to get - * a BluetoothDevice. + * Sentinel error value for this class. Guaranteed to not equal any other + * integer constant in this class. Provided as a convenience for functions + * that require a sentinel error value, for example: + *

Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + * BluetoothDevice.ERROR) */ - public BluetoothDevice(IBluetoothDevice service) { - mService = service; - } + public static final int ERROR = Integer.MIN_VALUE; /** - * Is Bluetooth currently turned on. - * - * @return true if Bluetooth enabled, false otherwise. + * Broadcast Action: Remote device discovered. + *

Sent when a remote device is found during discovery. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or + * {@link #EXTRA_RSSI} if they are available. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - public boolean isEnabled() { - try { - return mService.isEnabled(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + // TODO: Change API to not broadcast RSSI if not available (incoming connection) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_FOUND = + "android.bluetooth.device.action.FOUND"; /** - * Get the current state of Bluetooth. - * - * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR. + * Broadcast Action: Remote device disappeared. + *

Sent when a remote device that was found in the last discovery is not + * found in the current discovery. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide */ - public int getBluetoothState() { - try { - return mService.getBluetoothState(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR; - } + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISAPPEARED = + "android.bluetooth.device.action.DISAPPEARED"; /** - * Enable the Bluetooth device. - * Turn on the underlying hardware. - * This is an asynchronous call, - * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if - * and when the device is sucessfully enabled. - * @return false if we cannot enable the Bluetooth device. True does not - * imply the device was enabled, it only implies that so far there were no - * problems. + * Broadcast Action: Bluetooth class of a remote device has changed. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_CLASS}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @see {@link BluetoothClass} */ - public boolean enable() { - try { - return mService.enable(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLASS_CHANGED = + "android.bluetooth.device.action.CLASS_CHANGED"; /** - * Disable the Bluetooth device. - * This turns off the underlying hardware. - * - * @return true if successful, false otherwise. + * Broadcast Action: Indicates a low level (ACL) connection has been + * established with a remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

ACL connections are managed automatically by the Android Bluetooth + * stack. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - public boolean disable() { - try { - return mService.disable(true); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_CONNECTED = + "android.bluetooth.device.action.ACL_CONNECTED"; - public String getAddress() { - try { - return mService.getAddress(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } + /** + * Broadcast Action: Indicates that a low level (ACL) disconnection has + * been requested for a remote device, and it will soon be disconnected. + *

This is useful for graceful disconnection. Applications should use + * this intent as a hint to immediately terminate higher level connections + * (RFCOMM, L2CAP, or profile connections) to the remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_DISCONNECT_REQUESTED = + "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; /** - * Get the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. Currently it is only - * possible to retrieve the Bluetooth name when Bluetooth is enabled. - * - * @return the Bluetooth name, or null if there was a problem. + * Broadcast Action: Indicates a low level (ACL) disconnection from a + * remote device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

ACL connections are managed automatically by the Android Bluetooth + * stack. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - public String getName() { - try { - return mService.getName(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACL_DISCONNECTED = + "android.bluetooth.device.action.ACL_DISCONNECTED"; /** - * Set the friendly Bluetooth name of this device. - * - * This name is visible to remote Bluetooth devices. The Bluetooth Service - * is responsible for persisting this name. - * - * @param name the name to set - * @return true, if the name was successfully set. False otherwise. + * Broadcast Action: Indicates the friendly name of a remote device has + * been retrieved for the first time, or changed since the last retrieval. + *

Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_NAME}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - public boolean setName(String name) { - try { - return mService.setName(name); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NAME_CHANGED = + "android.bluetooth.device.action.NAME_CHANGED"; - public String getVersion() { - try { - return mService.getVersion(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getRevision() { - try { - return mService.getRevision(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getManufacturer() { - try { - return mService.getManufacturer(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String getCompany() { - try { - return mService.getCompany(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } + /** + * Broadcast Action: Indicates a change in the bond state of a remote + * device. For example, if a device is bonded (paired). + *

Always contains the extra fields {@link #EXTRA_DEVICE}, {@link + * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also + // contain a hidden extra field EXTRA_REASON with the result code. + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BOND_STATE_CHANGED = + "android.bluetooth.device.action.BOND_STATE_CHANGED"; /** - * Get the current scan mode. - * Used to determine if the local device is connectable and/or discoverable - * @return Scan mode, one of SCAN_MODE_* or an error code + * Used as a Parcelable {@link BluetoothDevice} extra field in every intent + * broadcast by this class. It contains the {@link BluetoothDevice} that + * the intent applies to. */ - public int getScanMode() { - try { - return mService.getScanMode(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; - } + public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; /** - * Set the current scan mode. - * Used to make the local device connectable and/or discoverable - * @param scanMode One of SCAN_MODE_* + * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link + * #ACTION_FOUND} intents. It contains the friendly Bluetooth name. */ - public void setScanMode(int scanMode) { - try { - mService.setScanMode(scanMode); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } + public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; - public int getDiscoverableTimeout() { - try { - return mService.getDiscoverableTimeout(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return -1; - } - public void setDiscoverableTimeout(int timeout) { - try { - mService.setDiscoverableTimeout(timeout); - } catch (RemoteException e) {Log.e(TAG, "", e);} + /** + * Used as an optional short extra field in {@link #ACTION_FOUND} intents. + * Contains the RSSI value of the remote device as reported by the + * Bluetooth hardware. + */ + public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; + + /** + * Used as an Parcelable {@link BluetoothClass} extra field in {@link + * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. + */ + public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; + + /** + * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * Contains the bond state of the remote device. + *

Possible values are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + */ + public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; + /** + * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * Contains the previous bond state of the remote device. + *

Possible values are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + */ + public static final String EXTRA_PREVIOUS_BOND_STATE = + "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; + /** + * Indicates the remote device is not bonded (paired). + *

There is no shared link key with the remote device, so communication + * (if it is allowed at all) will be unauthenticated and unencrypted. + */ + public static final int BOND_NONE = 10; + /** + * Indicates bonding (pairing) is in progress with the remote device. + */ + public static final int BOND_BONDING = 11; + /** + * Indicates the remote device is bonded (paired). + *

A shared link keys exists locally for the remote device, so + * communication can be authenticated and encrypted. + *

Being bonded (paired) with a remote device does not necessarily + * mean the device is currently connected. It just means that the ponding + * procedure was compeleted at some earlier time, and the link key is still + * stored locally, ready to use on the next connection. + * + */ + public static final int BOND_BONDED = 12; + + /** @hide */ + public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; + /** @hide */ + public static final String EXTRA_PAIRING_VARIANT = + "android.bluetooth.device.extra.PAIRING_VARIANT"; + /** @hide */ + public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; + + /** + * Broadcast Action: This intent is used to broadcast the {@link UUID} + * wrapped as a {@link android.os.ParcelUuid} of the remote device after it + * has been fetched. This intent is sent only when the UUIDs of the remote + * device are requested to be fetched using Service Discovery Protocol + *

Always contains the extra field {@link #EXTRA_DEVICE} + *

Always contains the extra filed {@link #EXTRA_UUID} + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_UUID = + "android.bleutooth.device.action.UUID"; + + /** + * Broadcast Action: Indicates a failure to retrieve the name of a remote + * device. + *

Always contains the extra field {@link #EXTRA_DEVICE}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + //TODO: is this actually useful? + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NAME_FAILED = + "android.bluetooth.device.action.NAME_FAILED"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PAIRING_REQUEST = + "android.bluetooth.device.action.PAIRING_REQUEST"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PAIRING_CANCEL = + "android.bluetooth.device.action.PAIRING_CANCEL"; + + /** A bond attempt succeeded + * @hide */ + public static final int BOND_SUCCESS = 0; + /** A bond attempt failed because pins did not match, or remote device did + * not respond to pin request in time + * @hide */ + public static final int UNBOND_REASON_AUTH_FAILED = 1; + /** A bond attempt failed because the other side explicilty rejected + * bonding + * @hide */ + public static final int UNBOND_REASON_AUTH_REJECTED = 2; + /** A bond attempt failed because we canceled the bonding process + * @hide */ + public static final int UNBOND_REASON_AUTH_CANCELED = 3; + /** A bond attempt failed because we could not contact the remote device + * @hide */ + public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; + /** A bond attempt failed because a discovery is in progress + * @hide */ + public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + /** A bond attempt failed because of authentication timeout + * @hide */ + public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; + /** A bond attempt failed because of repeated attempts + * @hide */ + public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; + /** A bond attempt failed because we received an Authentication Cancel + * by remote end + * @hide */ + public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; + /** An existing bond was explicitly revoked + * @hide */ + public static final int UNBOND_REASON_REMOVED = 9; + + /** The user will be prompted to enter a pin + * @hide */ + public static final int PAIRING_VARIANT_PIN = 0; + /** The user will be prompted to enter a passkey + * @hide */ + public static final int PAIRING_VARIANT_PASSKEY = 1; + /** The user will be prompted to confirm the passkey displayed on the screen + * @hide */ + public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; + /** The user will be prompted to accept or deny the incoming pairing request + * @hide */ + public static final int PAIRING_VARIANT_CONSENT = 3; + /** The user will be prompted to enter the passkey displayed on remote device + * @hide */ + public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; + + /** + * Used as an extra field in {@link #ACTION_UUID} intents, + * Contains the {@link android.os.ParcelUuid}s of the remote device which + * is a parcelable version of {@link UUID}. + * @hide + */ + public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + + /** + * Lazy initialization. Guaranteed final after first object constructed, or + * getService() called. + * TODO: Unify implementation of sService amongst BluetoothFoo API's + */ + private static IBluetooth sService; + + private final String mAddress; + + /*package*/ static IBluetooth getService() { + synchronized (BluetoothDevice.class) { + if (sService == null) { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b == null) { + throw new RuntimeException("Bluetooth service not available"); + } + sService = IBluetooth.Stub.asInterface(b); + } + } + return sService; } - public boolean startDiscovery() { - return startDiscovery(true); + /** + * Create a new BluetoothDevice + * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", + * and is validated in this constructor. + * @param address valid Bluetooth MAC address + * @throws RuntimeException Bluetooth is not available on this platform + * @throws IllegalArgumentException address is invalid + * @hide + */ + /*package*/ BluetoothDevice(String address) { + getService(); // ensures sService is initialized + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); + } + + mAddress = address; } - public boolean startDiscovery(boolean resolveNames) { - try { - return mService.startDiscovery(resolveNames); - } catch (RemoteException e) {Log.e(TAG, "", e);} + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothDevice) { + return mAddress.equals(((BluetoothDevice)o).getAddress()); + } return false; } - public void cancelDiscovery() { - try { - mService.cancelDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + @Override + public int hashCode() { + return mAddress.hashCode(); } - public boolean isDiscovering() { - try { - return mService.isDiscovering(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + /** + * Returns a string representation of this BluetoothDevice. + *

Currently this is the Bluetooth hardware address, for example + * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} + * if you explicitly require the Bluetooth hardware address in case the + * {@link #toString} representation changes in the future. + * @return string representation of this BluetoothDevice + */ + @Override + public String toString() { + return mAddress; } - public boolean startPeriodicDiscovery() { - try { - return mService.startPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + public int describeContents() { + return 0; } - public boolean stopPeriodicDiscovery() { - try { - return mService.stopPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } - public boolean isPeriodicDiscovery() { - try { - return mService.isPeriodicDiscovery(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothDevice createFromParcel(Parcel in) { + return new BluetoothDevice(in.readString()); + } + public BluetoothDevice[] newArray(int size) { + return new BluetoothDevice[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeString(mAddress); } - public String[] listRemoteDevices() { - try { - return mService.listRemoteDevices(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + /** + * Returns the hardware address of this BluetoothDevice. + *

For example, "00:11:22:AA:BB:CC". + * @return Bluetooth hardware address as string + */ + public String getAddress() { + return mAddress; } /** - * List remote devices that have a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. + * Get the friendly Bluetooth name of the remote device. * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. + *

The local adapter will automatically retrieve remote names when + * performing a device scan, and will cache them. This method just returns + * the name for this device from the cache. + *

Requires {@link android.Manifest.permission#BLUETOOTH} * - * @return bluetooth hardware addresses of remote devices with a current - * ACL connection. Array size is 0 if no devices have a - * connection. Null on error. + * @return the Bluetooth name, or null if there was a problem. */ - public String[] listAclConnections() { + public String getName() { try { - return mService.listAclConnections(); + return sService.getRemoteName(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } /** - * Check if a specified remote device has a low level (ACL) connection. - * - * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have - * an ACL connection even when not paired - this is common for SDP queries - * or for in-progress pairing requests. - * - * In most cases you probably want to test if a higher level protocol is - * connected, rather than testing ACL connections. + * Start the bonding (pairing) process with the remote device. + *

This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when + * the bonding process completes, and its result. + *

Android system services will handle the necessary user interactions + * to confirm and complete the bonding process. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @param address the Bluetooth hardware address you want to check. - * @return true if there is an ACL connection, false otherwise and on - * error. + * @return false on immediate error, true if bonding will begin + * @hide */ - public boolean isAclConnected(String address) { + public boolean createBond() { try { - return mService.isAclConnected(address); + return sService.createBond(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** - * Perform a low level (ACL) disconnection of a remote device. + * Cancel an in-progress bonding request started with {@link #createBond}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * This forcably disconnects the ACL layer connection to a remote device, - * which will cause all RFCOMM, SDP and L2CAP connections to this remote - * device to close. - * - * @param address the Bluetooth hardware address you want to disconnect. - * @return true if the device was disconnected, false otherwise and on - * error. + * @return true on sucess, false on error + * @hide */ - public boolean disconnectRemoteDeviceAcl(String address) { + public boolean cancelBondProcess() { try { - return mService.disconnectRemoteDeviceAcl(address); + return sService.cancelBondProcess(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** - * Create a bonding with a remote bluetooth device. - * - * This is an asynchronous call. The result of this bonding attempt can be - * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. + * Remove bond (pairing) with the remote device. + *

Delete the link key associated with the remote device, and + * immediately terminate connections to that device that require + * authentication and encryption. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @param address the remote device Bluetooth address. - * @return false If there was an immediate problem creating the bonding, - * true otherwise. + * @return true on sucess, false on error + * @hide */ - public boolean createBond(String address) { + public boolean removeBond() { try { - return mService.createBond(address); + return sService.removeBond(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** - * Cancel an in-progress bonding request started with createBond. + * Get the bond state of the remote device. + *

Possible values for the bond state are: + * {@link #BOND_NONE}, + * {@link #BOND_BONDING}, + * {@link #BOND_BONDED}. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return the bond state */ - public boolean cancelBondProcess(String address) { + public int getBondState() { try { - return mService.cancelBondProcess(address); + return sService.getBondState(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + return BOND_NONE; } /** - * Remove an already exisiting bonding (delete the link key). + * Get the Bluetooth class of the remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return Bluetooth class object, or null on error */ - public boolean removeBond(String address) { + public BluetoothClass getBluetoothClass() { try { - return mService.removeBond(address); + int classInt = sService.getRemoteClass(mAddress); + if (classInt == BluetoothClass.ERROR) return null; + return new BluetoothClass(classInt); } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + return null; } /** - * List remote devices that are bonded (paired) to the local device. - * - * Bonding (pairing) is the process by which the user enters a pin code for - * the device, which generates a shared link key, allowing for - * authentication and encryption of future connections. In Android we - * require bonding before RFCOMM or SCO connections can be made to a remote - * device. - * - * This function lists which remote devices we have a link key for. It does - * not cause any RF transmission, and does not check if the remote device - * still has it's link key with us. If the other side no longer has its - * link key then the RFCOMM or SCO connection attempt will result in an - * error. - * - * This function does not check if the remote device is in range. - * - * Remote devices that have an in-progress bonding attempt are not - * returned. - * - * @return bluetooth hardware addresses of remote devices that are - * bonded. Array size is 0 if no devices are bonded. Null on error. + * Get trust state of a remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH}. + * @hide */ - public String[] listBonds() { + public boolean getTrustState() { try { - return mService.listBonds(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return sService.getTrustState(mAddress); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; } /** - * Get the bonding state of a remote device. - * - * Result is one of: - * BluetoothError.* - * BOND_* - * - * @param address Bluetooth hardware address of the remote device to check. - * @return Result code + * Set trust state for a remote device. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * @param value the trust state value (true or false) + * @hide */ - public int getBondState(String address) { + public boolean setTrust(boolean value) { try { - return mService.getBondState(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothError.ERROR_IPC; + return sService.setTrust(mAddress, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; } - public String getRemoteName(String address) { + /** @hide */ + public ParcelUuid[] getUuids() { try { - return mService.getRemoteName(address); + return sService.getRemoteUuids(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - public String getRemoteVersion(String address) { - try { - return mService.getRemoteVersion(address); + /** + * Perform a SDP query on the remote device to get the UUIDs + * supported. This API is asynchronous and an Intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * an Intent is sent with the UUIDs that is currently present in the + * cache. Clients should use the {@link getUuids} to get UUIDs + * is SDP is not to be performed. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + * @hide + */ + public boolean fetchUuidsWithSdp() { + try { + return sService.fetchRemoteUuids(mAddress, null, null); } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return false; } - public String getRemoteRevision(String address) { + + /** @hide */ + public int getServiceChannel(ParcelUuid uuid) { + try { + return sService.getRemoteServiceChannel(mAddress, uuid); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BluetoothDevice.ERROR; + } + + /** @hide */ + public boolean setPin(byte[] pin) { try { - return mService.getRemoteRevision(address); + return sService.setPin(mAddress, pin); } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return false; } - public String getRemoteManufacturer(String address) { + + /** @hide */ + public boolean setPasskey(int passkey) { try { - return mService.getRemoteManufacturer(address); + return sService.setPasskey(mAddress, passkey); } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return false; } - public String getRemoteCompany(String address) { + + /** @hide */ + public boolean setPairingConfirmation(boolean confirm) { try { - return mService.getRemoteCompany(address); + return sService.setPairingConfirmation(mAddress, confirm); } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + return false; } - /** - * Returns the RFCOMM channel associated with the 16-byte UUID on - * the remote Bluetooth address. - * - * Performs a SDP ServiceSearchAttributeRequest transaction. The provided - * uuid is verified in the returned record. If there was a problem, or the - * specified uuid does not exist, -1 is returned. - */ - public boolean getRemoteServiceChannel(String address, short uuid16, - IBluetoothDeviceCallback callback) { + /** @hide */ + public boolean cancelPairingUserInput() { try { - return mService.getRemoteServiceChannel(address, uuid16, callback); + return sService.cancelPairingUserInput(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** - * Get the major, minor and servics classes of a remote device. - * These classes are encoded as a 32-bit integer. See BluetoothClass. - * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass, or - * BluetoothClass.ERROR on error + * Create an RFCOMM {@link BluetoothSocket} ready to start a secure + * outgoing connection to this remote device on given channel. + *

The remote device will be authenticated and communication on this + * socket will be encrypted. + *

Use {@link BluetoothSocket#connect} to intiate the outgoing + * connection. + *

Valid RFCOMM channels are in range 1 to 30. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param channel RFCOMM channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + * @hide */ - public int getRemoteClass(String address) { - try { - return mService.getRemoteClass(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return BluetoothClass.ERROR; + public BluetoothSocket createRfcommSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, + null); } - public byte[] getRemoteFeatures(String address) { - try { - return mService.getRemoteFeatures(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastSeen(String address) { - try { - return mService.lastSeen(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; - } - public String lastUsed(String address) { - try { - return mService.lastUsed(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return null; + /** + * Create an RFCOMM {@link BluetoothSocket} ready to start a secure + * outgoing connection to this remote device using SDP lookup of uuid. + *

This is designed to be used with {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer + * Bluetooth applications. + *

Use {@link BluetoothSocket#connect} to intiate the outgoing + * connection. This will also perform an SDP lookup of the given uuid to + * determine which channel to connect to. + *

The remote device will be authenticated and communication on this + * socket will be encrypted. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param uuid service record uuid to lookup RFCOMM channel + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + */ + public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, + new ParcelUuid(uuid)); } - public boolean setPin(String address, byte[] pin) { - try { - return mService.setPin(address, pin); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + /** + * Construct an insecure RFCOMM socket ready to start an outgoing + * connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + * The remote device will not be authenticated and communication on this + * socket will not be encrypted. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param port remote port + * @return An RFCOMM BluetoothSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, + null); } - public boolean cancelPin(String address) { - try { - return mService.cancelPin(address); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + + /** + * Construct a SCO socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @return a SCO BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothSocket createScoSocket() throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } /** @@ -534,6 +708,7 @@ public class BluetoothDevice { * @param pin pin as java String * @return the pin code as a UTF8 byte array, or null if it is an invalid * Bluetooth pin. + * @hide */ public static byte[] convertPinToBytes(String pin) { if (pin == null) { @@ -552,28 +727,4 @@ public class BluetoothDevice { return pinBytes; } - private static final int ADDRESS_LENGTH = 17; - /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ - public static boolean checkBluetoothAddress(String address) { - if (address == null || address.length() != ADDRESS_LENGTH) { - return false; - } - for (int i = 0; i < ADDRESS_LENGTH; i++) { - char c = address.charAt(i); - switch (i % 3) { - case 0: - case 1: - if (Character.digit(c, 16) != -1) { - break; // hex character, OK - } - return false; - case 2: - if (c == ':') { - break; // OK - } - return false; - } - } - return true; - } } diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java new file mode 100644 index 00000000000..05eed0e6de6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +/** + * A helper to show a system "Device Picker" activity to the user. + * + * @hide + */ +public interface BluetoothDevicePicker { + public static final String EXTRA_NEED_AUTH = + "android.bluetooth.devicepicker.extra.NEED_AUTH"; + public static final String EXTRA_FILTER_TYPE = + "android.bluetooth.devicepicker.extra.FILTER_TYPE"; + public static final String EXTRA_LAUNCH_PACKAGE = + "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE"; + public static final String EXTRA_LAUNCH_CLASS = + "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS"; + + /** + * Broadcast when one BT device is selected from BT device picker screen. + * Selected BT device address is contained in extra string {@link BluetoothIntent} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEVICE_SELECTED = + "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; + + /** + * Broadcast when someone want to select one BT device from devices list. + * This intent contains below extra data: + * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication + * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be + * listed + * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this + * intent come from + * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent + * come from + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LAUNCH = + "android.bluetooth.devicepicker.action.LAUNCH"; + + /** Ask device picker to show all kinds of BT devices */ + public static final int FILTER_TYPE_ALL = 0; + /** Ask device picker to show BT devices that support AUDIO profiles */ + public static final int FILTER_TYPE_AUDIO = 1; + /** Ask device picker to show BT devices that support Object Transfer */ + public static final int FILTER_TYPE_TRANSFER = 2; +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index fe1e09af984..90cff6b8c90 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -49,11 +51,32 @@ import android.util.Log; * * @hide */ -public class BluetoothHeadset { +public final class BluetoothHeadset { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = false; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STATE_CHANGED = + "android.bluetooth.headset.action.STATE_CHANGED"; + /** + * TODO(API release): Consider incorporating as new state in + * HEADSET_STATE_CHANGED + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AUDIO_STATE_CHANGED = + "android.bluetooth.headset.action.AUDIO_STATE_CHANGED"; + public static final String EXTRA_STATE = + "android.bluetooth.headset.extra.STATE"; + public static final String EXTRA_PREVIOUS_STATE = + "android.bluetooth.headset.extra.PREVIOUS_STATE"; + public static final String EXTRA_AUDIO_STATE = + "android.bluetooth.headset.extra.AUDIO_STATE"; + + /** + * TODO(API release): Consider incorporating as new state in + * HEADSET_STATE_CHANGED + */ private IBluetoothHeadset mService; private final Context mContext; private final ServiceListener mServiceListener; @@ -163,16 +186,16 @@ public class BluetoothHeadset { } /** - * Get the Bluetooth address of the current headset. - * @return The Bluetooth address, or null if not in connected or connecting + * Get the BluetoothDevice for the current headset. + * @return current headset, or null if not in connected or connecting * state, or if this proxy object is not connected to the Headset * service. */ - public String getHeadsetAddress() { - if (DBG) log("getHeadsetAddress()"); + public BluetoothDevice getCurrentHeadset() { + if (DBG) log("getCurrentHeadset()"); if (mService != null) { try { - return mService.getHeadsetAddress(); + return mService.getCurrentHeadset(); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -185,19 +208,19 @@ public class BluetoothHeadset { * Request to initiate a connection to a headset. * This call does not block. Fails if a headset is already connecting * or connected. - * Initiates auto-connection if address is null. Tries to connect to all + * Initiates auto-connection if device is null. Tries to connect to all * devices with priority greater than PRIORITY_AUTO in descending order. - * @param address The Bluetooth Address to connect to, or null to - * auto-connect to the last connected headset. - * @return False if there was a problem initiating the connection - * procedure, and no further HEADSET_STATE_CHANGED intents - * will be expected. + * @param device device to connect to, or null to auto-connect last connected + * headset + * @return false if there was a problem initiating the connection + * procedure, and no further HEADSET_STATE_CHANGED intents + * will be expected. */ - public boolean connectHeadset(String address) { - if (DBG) log("connectHeadset(" + address + ")"); + public boolean connectHeadset(BluetoothDevice device) { + if (DBG) log("connectHeadset(" + device + ")"); if (mService != null) { try { - if (mService.connectHeadset(address)) { + if (mService.connectHeadset(device)) { return true; } } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -213,11 +236,11 @@ public class BluetoothHeadset { * connecting). Returns false if not connected, or if this proxy object * if not currently connected to the headset service. */ - public boolean isConnected(String address) { - if (DBG) log("isConnected(" + address + ")"); + public boolean isConnected(BluetoothDevice device) { + if (DBG) log("isConnected(" + device + ")"); if (mService != null) { try { - return mService.isConnected(address); + return mService.isConnected(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -295,16 +318,16 @@ public class BluetoothHeadset { * auto-connected. * Incoming connections are ignored regardless of priority if there is * already a headset connected. - * @param address Paired headset + * @param device paired headset * @param priority Integer priority, for example PRIORITY_AUTO or * PRIORITY_NONE - * @return True if successful, false if there was some error. + * @return true if successful, false if there was some error */ - public boolean setPriority(String address, int priority) { - if (DBG) log("setPriority(" + address + ", " + priority + ")"); + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null) { try { - return mService.setPriority(address, priority); + return mService.setPriority(device, priority); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -315,14 +338,14 @@ public class BluetoothHeadset { /** * Get priority of headset. - * @param address Headset - * @return non-negative priority, or negative error code on error. + * @param device headset + * @return non-negative priority, or negative error code on error */ - public int getPriority(String address) { - if (DBG) log("getPriority(" + address + ")"); + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); if (mService != null) { try { - return mService.getPriority(address); + return mService.getPriority(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -356,30 +379,6 @@ public class BluetoothHeadset { return -1; } - /** - * Check class bits for possible HSP or HFP support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support HSP or HFP. It is not accurate for all - * devices. It tries to err on the side of false positives. - * @return True if this device might support HSP or HFP. - */ - public static boolean doesClassMatch(int btClass) { - // The render service class is required by the spec for HFP, so is a - // pretty good signal - if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) { - return true; - } - // Just in case they forgot the render service class - switch (BluetoothClass.Device.getDevice(btClass)) { - case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } - private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java new file mode 100644 index 00000000000..03af95337c5 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.InputStream; + +/** + * BluetoothInputStream. + * + * Used to write to a Bluetooth socket. + * + * @hide + */ +/*package*/ final class BluetoothInputStream extends InputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothInputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Return number of bytes available before this stream will block. + */ + public int available() throws IOException { + return mSocket.available(); + } + + public void close() throws IOException { + mSocket.close(); + } + + /** + * Reads a single byte from this stream and returns it as an integer in the + * range from 0 to 255. Returns -1 if the end of the stream has been + * reached. Blocks until one byte has been read, the end of the source + * stream is detected or an exception is thrown. + * + * @return the byte read or -1 if the end of stream has been reached. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.5 + */ + public int read() throws IOException { + byte b[] = new byte[1]; + int ret = mSocket.read(b, 0, 1); + if (ret == 1) { + return (int)b[0] & 0xff; + } else { + return -1; + } + } + + /** + * Reads at most {@code length} bytes from this stream and stores them in + * the byte array {@code b} starting at {@code offset}. + * + * @param b + * the byte array in which to store the bytes read. + * @param offset + * the initial position in {@code buffer} to store the bytes + * read from this stream. + * @param length + * the maximum number of bytes to store in {@code b}. + * @return the number of bytes actually read or -1 if the end of the stream + * has been reached. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code length < 0}, or if + * {@code offset + length} is greater than the length of + * {@code b}. + * @throws IOException + * if the stream is closed or another IOException occurs. + * @since Android 1.5 + */ + public int read(byte[] b, int offset, int length) throws IOException { + if (b == null) { + throw new NullPointerException("byte array is null"); + } + if ((offset | length) < 0 || length > b.length - offset) { + throw new ArrayIndexOutOfBoundsException("invalid offset or length"); + } + return mSocket.read(b, offset, length); + } +} diff --git a/framework/java/android/bluetooth/BluetoothIntent.java b/framework/java/android/bluetooth/BluetoothIntent.java deleted file mode 100644 index 344601b0b89..00000000000 --- a/framework/java/android/bluetooth/BluetoothIntent.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2008 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.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Manages the local Bluetooth device. Scan for devices, create bondings, - * power up and down the adapter. - * - * @hide - */ -public interface BluetoothIntent { - public static final String SCAN_MODE = - "android.bluetooth.intent.SCAN_MODE"; - public static final String ADDRESS = - "android.bluetooth.intent.ADDRESS"; - public static final String NAME = - "android.bluetooth.intent.NAME"; - public static final String ALIAS = - "android.bluetooth.intent.ALIAS"; - public static final String RSSI = - "android.bluetooth.intent.RSSI"; - public static final String CLASS = - "android.bluetooth.intent.CLASS"; - public static final String BLUETOOTH_STATE = - "android.bluetooth.intent.BLUETOOTH_STATE"; - public static final String BLUETOOTH_PREVIOUS_STATE = - "android.bluetooth.intent.BLUETOOTH_PREVIOUS_STATE"; - public static final String HEADSET_STATE = - "android.bluetooth.intent.HEADSET_STATE"; - public static final String HEADSET_PREVIOUS_STATE = - "android.bluetooth.intent.HEADSET_PREVIOUS_STATE"; - public static final String HEADSET_AUDIO_STATE = - "android.bluetooth.intent.HEADSET_AUDIO_STATE"; - public static final String BOND_STATE = - "android.bluetooth.intent.BOND_STATE"; - public static final String BOND_PREVIOUS_STATE = - "android.bluetooth.intent.BOND_PREVIOUS_STATE"; - public static final String REASON = - "android.bluetooth.intent.REASON"; - - /** Broadcast when the local Bluetooth device state changes, for example - * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and - * BLUETOOTH_PREVIOUS_STATE. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BLUETOOTH_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.BLUETOOTH_STATE_CHANGED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String NAME_CHANGED_ACTION = - "android.bluetooth.intent.action.NAME_CHANGED"; - - /** - * Broadcast when the scan mode changes. Always contains an int extra - * named SCAN_MODE that contains the new scan mode. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String SCAN_MODE_CHANGED_ACTION = - "android.bluetooth.intent.action.SCAN_MODE_CHANGED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_STARTED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_STARTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String DISCOVERY_COMPLETED_ACTION = - "android.bluetooth.intent.action.DISCOVERY_COMPLETED"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_REQUEST_ACTION = - "android.bluetooth.intent.action.PAIRING_REQUEST"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String PAIRING_CANCEL_ACTION = - "android.bluetooth.intent.action.PAIRING_CANCEL"; - - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_FOUND_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISAPPEARED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_CONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_DEVICE_DISCONNECTED_ACTION = - "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_UPDATED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_UPDATED"; - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String REMOTE_NAME_FAILED_ACTION = - "android.bluetooth.intent.action.REMOTE_NAME_FAILED"; - - /** - * Broadcast when the bond state of a remote device changes. - * Has string extra ADDRESS and int extras BOND_STATE and - * BOND_PREVIOUS_STATE. - * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will - * also have an int extra REASON with a value of: - * BluetoothDevice.BOND_RESULT_* - * */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String BOND_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION"; - - /** - * TODO(API release): Move into BluetoothHeadset - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String HEADSET_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.HEADSET_STATE_CHANGED"; - - /** - * TODO(API release): Consider incorporating as new state in - * HEADSET_STATE_CHANGED - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String HEADSET_AUDIO_STATE_CHANGED_ACTION = - "android.bluetooth.intent.action.HEADSET_ADUIO_STATE_CHANGED"; -} diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java new file mode 100644 index 00000000000..62242a2672f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 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 java.io.IOException; +import java.io.OutputStream; + +/** + * BluetoothOutputStream. + * + * Used to read from a Bluetooth socket. + * + * @hide + */ +/*package*/ final class BluetoothOutputStream extends OutputStream { + private BluetoothSocket mSocket; + + /*package*/ BluetoothOutputStream(BluetoothSocket s) { + mSocket = s; + } + + /** + * Close this output stream and the socket associated with it. + */ + public void close() throws IOException { + mSocket.close(); + } + + /** + * Writes a single byte to this stream. Only the least significant byte of + * the integer {@code oneByte} is written to the stream. + * + * @param oneByte + * the byte to be written. + * @throws IOException + * if an error occurs while writing to this stream. + * @since Android 1.0 + */ + public void write(int oneByte) throws IOException { + byte b[] = new byte[1]; + b[0] = (byte)oneByte; + mSocket.write(b, 0, 1); + } + + /** + * Writes {@code count} bytes from the byte array {@code buffer} starting + * at position {@code offset} to this stream. + * + * @param b + * the buffer to be written. + * @param offset + * the start position in {@code buffer} from where to get bytes. + * @param count + * the number of bytes from {@code buffer} to write to this + * stream. + * @throws IOException + * if an error occurs while writing to this stream. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code count < 0}, or if + * {@code offset + count} is bigger than the length of + * {@code buffer}. + * @since Android 1.0 + */ + public void write(byte[] b, int offset, int count) throws IOException { + if (b == null) { + throw new NullPointerException("buffer is null"); + } + if ((offset | count) < 0 || count > b.length - offset) { + throw new IndexOutOfBoundsException("invalid offset or length"); + } + mSocket.write(b, offset, count); + } +} diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java new file mode 100644 index 00000000000..b48f48e9f19 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2008 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +/** + * The Android Bluetooth API is not finalized, and *will* change. Use at your + * own risk. + * + * Public API for controlling the Bluetooth Pbap Service. This includes + * Bluetooth Phone book Access profile. + * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap + * Service via IPC. + * + * Creating a BluetoothPbap object will create a binding with the + * BluetoothPbap service. Users of this object should call close() when they + * are finished with the BluetoothPbap, so that this proxy object can unbind + * from the service. + * + * This BluetoothPbap object is not immediately bound to the + * BluetoothPbap service. Use the ServiceListener interface to obtain a + * notification when it is bound, this is especially important if you wish to + * immediately call methods on BluetoothPbap after construction. + * + * Android only supports one connected Bluetooth Pce at a time. + * + * @hide + */ +public class BluetoothPbap { + + private static final String TAG = "BluetoothPbap"; + private static final boolean DBG = false; + + /** int extra for PBAP_STATE_CHANGED_ACTION */ + public static final String PBAP_STATE = + "android.bluetooth.pbap.intent.PBAP_STATE"; + /** int extra for PBAP_STATE_CHANGED_ACTION */ + public static final String PBAP_PREVIOUS_STATE = + "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; + + /** Indicates the state of an pbap connection state has changed. + * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. + */ + public static final String PBAP_STATE_CHANGED_ACTION = + "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; + + private IBluetoothPbap mService; + private final Context mContext; + private final ServiceListener mServiceListener; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + /** No client currently connected */ + public static final int STATE_DISCONNECTED = 0; + /** Connection attempt in progress */ + public static final int STATE_CONNECTING = 1; + /** Client is currently connected */ + public static final int STATE_CONNECTED = 2; + + 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; + + /** + * An interface for notifying Bluetooth PCE IPC clients when they have + * been connected to the BluetoothPbap service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothPbap service. Clients must wait for + * this callback before making IPC calls on the BluetoothPbap + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothPbap service. Clients must not + * make IPC calls on the BluetoothPbap service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothPbap service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + /** + * Create a BluetoothPbap proxy object. + */ + public BluetoothPbap(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service"); + } + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothPbap will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized void close() { + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } + } + + /** + * Get the current state of the BluetoothPbap service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Pbap service. + */ + public int getState() { + if (DBG) log("getState()"); + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return BluetoothPbap.STATE_ERROR; + } + + /** + * Get the currently connected remote Bluetooth device (PCE). + * @return The remote Bluetooth device, or null if not in connected or + * connecting state, or if this proxy object is not connected to + * the Pbap service. + */ + public BluetoothDevice getClient() { + if (DBG) log("getClient()"); + if (mService != null) { + try { + return mService.getClient(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return null; + } + + /** + * Returns true if the specified Bluetooth device is connected (does not + * include connecting). Returns false if not connected, or if this proxy + * object is not currently connected to the Pbap service. + */ + public boolean isConnected(BluetoothDevice device) { + if (DBG) log("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(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Disconnects the current Pbap client (PCE). Currently this call blocks, + * it may soon be made asynchornous. Returns false if this proxy object is + * not currently connected to the Pbap service. + */ + public boolean disconnect() { + if (DBG) log("disconnect()"); + if (mService != null) { + try { + mService.disconnect(); + return true; + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Check class bits for possible PBAP support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support PBAP. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support PBAP. + */ + public static boolean doesClassMatchSink(BluetoothClass btClass) { + // TODO optimize the rule + switch (btClass.getDeviceClass()) { + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return true; + default: + return false; + } + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) log("Proxy object connected"); + mService = IBluetoothPbap.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) log("Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java new file mode 100644 index 00000000000..1b23f6c048d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 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.os.Handler; + +import java.io.Closeable; +import java.io.IOException; + +/** + * A listening Bluetooth socket. + * + *

The interface for Bluetooth Sockets is similar to that of TCP sockets: + * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server + * side, use a {@link BluetoothServerSocket} to create a listening server + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection. + * + *

The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). + * + *

To create a listenting {@link BluetoothServerSocket} that's ready for + * incoming connections, use + * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord + * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call + * {@link #accept()} to listen for incoming connection requests. This call + * will block until a connection is established, at which point, it will return + * a {@link BluetoothSocket} to manage the connection. + * + *

{@link BluetoothServerSocket} is thread + * safe. In particular, {@link #close} will always immediately abort ongoing + * operations and close the server socket. + * + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothSocket} + */ +public final class BluetoothServerSocket implements Closeable { + + /*package*/ final BluetoothSocket mSocket; + private Handler mHandler; + private int mMessage; + + /** + * Construct a socket for incoming connections. + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) + throws IOException { + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); + } + + /** + * Block until a connection is established. + *

Returns a connected {@link BluetoothSocket} on successful connection. + *

Once this call returns, it can be called again to accept subsequent + * incoming connections. + *

{@link #close} can be used to abort this call from another thread. + * @return a connected {@link BluetoothSocket} + * @throws IOException on error, for example this call was aborted, or + * timeout + */ + public BluetoothSocket accept() throws IOException { + return accept(-1); + } + + /** + * Block until a connection is established, with timeout. + *

Returns a connected {@link BluetoothSocket} on successful connection. + *

Once this call returns, it can be called again to accept subsequent + * incoming connections. + *

{@link #close} can be used to abort this call from another thread. + * @return a connected {@link BluetoothSocket} + * @throws IOException on error, for example this call was aborted, or + * timeout + */ + public BluetoothSocket accept(int timeout) throws IOException { + return mSocket.accept(timeout); + } + + /** + * Immediately close this socket, and release all associated resources. + *

Causes blocked calls on this socket in other threads to immediately + * throw an IOException. + */ + public void close() throws IOException { + synchronized (this) { + if (mHandler != null) { + mHandler.obtainMessage(mMessage).sendToTarget(); + } + } + mSocket.close(); + } + + /*package*/ synchronized void setCloseHandler(Handler handler, int message) { + mHandler = handler; + mMessage = message; + } +} diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java new file mode 100644 index 00000000000..dbcc758574b --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2009 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.bluetooth.IBluetoothCallback; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * A connected or connecting Bluetooth socket. + * + *

The interface for Bluetooth Sockets is similar to that of TCP sockets: + * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server + * side, use a {@link BluetoothServerSocket} to create a listening server + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection. + * + *

The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). + * + *

To create a {@link BluetoothSocket} for connecting to a known device, use + * {@link BluetoothDevice#createRfcommSocketToServiceRecord + * BluetoothDevice.createRfcommSocketToServiceRecord()}. + * Then call {@link #connect()} to attempt a connection to the remote device. + * This call will block until a connection is established or the connection + * fails. + * + *

To create a {@link BluetoothSocket} as a server (or "host"), see the + * {@link BluetoothServerSocket} documentation. + * + *

Once the socket is connected, whether initiated as a client or accepted + * as a server, open the IO streams by calling {@link #getInputStream} and + * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream} + * and {@link java.io.OutputStream} objects, respectively, which are + * automatically connected to the socket. + * + *

{@link BluetoothSocket} is thread + * safe. In particular, {@link #close} will always immediately abort ongoing + * operations and close the socket. + * + *

Note: + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothServerSocket} + * {@see java.io.InputStream} + * {@see java.io.OutputStream} + */ +public final class BluetoothSocket implements Closeable { + private static final String TAG = "BluetoothSocket"; + + /** @hide */ + public static final int MAX_RFCOMM_CHANNEL = 30; + + /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ + /*package*/ static final int TYPE_RFCOMM = 1; + /*package*/ static final int TYPE_SCO = 2; + /*package*/ static final int TYPE_L2CAP = 3; + + /*package*/ static final int EBADFD = 77; + /*package*/ static final int EADDRINUSE = 98; + + private final int mType; /* one of TYPE_RFCOMM etc */ + private final BluetoothDevice mDevice; /* remote device */ + private final String mAddress; /* remote address */ + private final boolean mAuth; + private final boolean mEncrypt; + private final BluetoothInputStream mInputStream; + private final BluetoothOutputStream mOutputStream; + private final SdpHelper mSdp; + + private int mPort; /* RFCOMM channel or L2CAP psm */ + + /** prevents all native calls after destroyNative() */ + private boolean mClosed; + + /** protects mClosed */ + private final ReentrantReadWriteLock mLock; + + /** used by native code only */ + private int mSocketData; + + /** + * Construct a BluetoothSocket. + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param device remote device that this socket can connect to + * @param port remote port + * @param uuid SDP uuid + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, + BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { + if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) { + if (port < 1 || port > MAX_RFCOMM_CHANNEL) { + throw new IOException("Invalid RFCOMM channel: " + port); + } + } + if (uuid == null) { + mPort = port; + mSdp = null; + } else { + mSdp = new SdpHelper(device, uuid); + mPort = -1; + } + mType = type; + mAuth = auth; + mEncrypt = encrypt; + mDevice = device; + if (device == null) { + mAddress = null; + } else { + mAddress = device.getAddress(); + } + if (fd == -1) { + initSocketNative(); + } else { + initSocketFromFdNative(fd); + } + mInputStream = new BluetoothInputStream(this); + mOutputStream = new BluetoothOutputStream(this); + mClosed = false; + mLock = new ReentrantReadWriteLock(); + } + + /** + * Construct a BluetoothSocket from address. Used by native code. + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param address remote device that this socket can connect to + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or + * insufficient priveleges + */ + private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, + int port) throws IOException { + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Attempt to connect to a remote device. + *

This method will block until a connection is made or the connection + * fails. If this method returns without an exception then this socket + * is now connected. + *

{@link #close} can be used to abort this call from another thread. + * @throws IOException on error, for example connection failure + */ + public void connect() throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + + if (mSdp != null) { + mPort = mSdp.doSdp(); // blocks + } + + connectNative(); // blocks + } finally { + mLock.readLock().unlock(); + } + } + + /** + * Immediately close this socket, and release all associated resources. + *

Causes blocked calls on this socket in other threads to immediately + * throw an IOException. + */ + public void close() throws IOException { + // abort blocking operations on the socket + mLock.readLock().lock(); + try { + if (mClosed) return; + if (mSdp != null) { + mSdp.cancel(); + } + abortNative(); + } finally { + mLock.readLock().unlock(); + } + + // all native calls are guaranteed to immediately return after + // abortNative(), so this lock should immediatley acquire + mLock.writeLock().lock(); + try { + mClosed = true; + destroyNative(); + } finally { + mLock.writeLock().unlock(); + } + } + + /** + * Get the remote device this socket is connecting, or connected, to. + * @return remote device + */ + public BluetoothDevice getRemoteDevice() { + return mDevice; + } + + /** + * Get the input stream associated with this socket. + *

The input stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return InputStream + */ + public InputStream getInputStream() throws IOException { + return mInputStream; + } + + /** + * Get the output stream associated with this socket. + *

The output stream will be returned even if the socket is not yet + * connected, but operations on that stream will throw IOException until + * the associated socket is connected. + * @return OutputStream + */ + public OutputStream getOutputStream() throws IOException { + return mOutputStream; + } + + /** + * Currently returns unix errno instead of throwing IOException, + * so that BluetoothAdapter can check the error code for EADDRINUSE + */ + /*package*/ int bindListen() { + mLock.readLock().lock(); + try { + if (mClosed) return EBADFD; + return bindListenNative(); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ BluetoothSocket accept(int timeout) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return acceptNative(timeout); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int available() throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return availableNative(); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int read(byte[] b, int offset, int length) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return readNative(b, offset, length); + } finally { + mLock.readLock().unlock(); + } + } + + /*package*/ int write(byte[] b, int offset, int length) throws IOException { + mLock.readLock().lock(); + try { + if (mClosed) throw new IOException("socket closed"); + return writeNative(b, offset, length); + } finally { + mLock.readLock().unlock(); + } + } + + private native void initSocketNative() throws IOException; + private native void initSocketFromFdNative(int fd) throws IOException; + private native void connectNative() throws IOException; + private native int bindListenNative(); + private native BluetoothSocket acceptNative(int timeout) throws IOException; + private native int availableNative() throws IOException; + private native int readNative(byte[] b, int offset, int length) throws IOException; + private native int writeNative(byte[] b, int offset, int length) throws IOException; + private native void abortNative() throws IOException; + private native void destroyNative() throws IOException; + /** + * Throws an IOException for given posix errno. Done natively so we can + * use strerr to convert to string error. + */ + /*package*/ native void throwErrnoNative(int errno) throws IOException; + + /** + * Helper to perform blocking SDP lookup. + */ + private static class SdpHelper extends IBluetoothCallback.Stub { + private final IBluetooth service; + private final ParcelUuid uuid; + private final BluetoothDevice device; + private int channel; + private boolean canceled; + public SdpHelper(BluetoothDevice device, ParcelUuid uuid) { + service = BluetoothDevice.getService(); + this.device = device; + this.uuid = uuid; + canceled = false; + } + /** + * Returns the RFCOMM channel for the UUID, or throws IOException + * on failure. + */ + public synchronized int doSdp() throws IOException { + if (canceled) throw new IOException("Service discovery canceled"); + channel = -1; + + boolean inProgress = false; + try { + inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + + if (!inProgress) throw new IOException("Unable to start Service Discovery"); + + try { + /* 12 second timeout as a precaution - onRfcommChannelFound + * should always occur before the timeout */ + wait(12000); // block + + } catch (InterruptedException e) {} + + if (canceled) throw new IOException("Service discovery canceled"); + if (channel < 1) throw new IOException("Service discovery failed"); + + return channel; + } + /** Object cannot be re-used after calling cancel() */ + public synchronized void cancel() { + if (!canceled) { + canceled = true; + channel = -1; + notifyAll(); // unblock + } + } + public synchronized void onRfcommChannelFound(int channel) { + if (!canceled) { + this.channel = channel; + notifyAll(); // unblock + } + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java new file mode 100644 index 00000000000..4164a3d6e66 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009 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.os.ParcelUuid; + +import java.util.Arrays; +import java.util.HashSet; + +/** +* Static helper methods and constants to decode the ParcelUuid of remote devices. +* @hide +*/ +public final class BluetoothUuid { + + /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs + * for the various services. + * + * The following 128 bit values are calculated as: + * uuid * 2^96 + BASE_UUID + */ + public static final ParcelUuid AudioSink = + ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AudioSource = + ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AdvAudioDist = + ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid HSP = + ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid Handsfree = + ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpController = + ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpTarget = + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid ObexObjectPush = + ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + + public static final ParcelUuid[] RESERVED_UUIDS = { + AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, + ObexObjectPush}; + + public static boolean isAudioSource(ParcelUuid uuid) { + return uuid.equals(AudioSource); + } + + public static boolean isAudioSink(ParcelUuid uuid) { + return uuid.equals(AudioSink); + } + + public static boolean isAdvAudioDist(ParcelUuid uuid) { + return uuid.equals(AdvAudioDist); + } + + public static boolean isHandsfree(ParcelUuid uuid) { + return uuid.equals(Handsfree); + } + + public static boolean isHeadset(ParcelUuid uuid) { + return uuid.equals(HSP); + } + + public static boolean isAvrcpController(ParcelUuid uuid) { + return uuid.equals(AvrcpController); + } + + public static boolean isAvrcpTarget(ParcelUuid uuid) { + return uuid.equals(AvrcpTarget); + } + + /** + * Returns true if ParcelUuid is present in uuidArray + * + * @param uuidArray - Array of ParcelUuids + * @param uuid + */ + public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { + if ((uuidArray == null || uuidArray.length == 0) && uuid == null) + return true; + + if (uuidArray == null) + return false; + + for (ParcelUuid element: uuidArray) { + if (element.equals(uuid)) return true; + } + return false; + } + + /** + * Returns true if there any common ParcelUuids in uuidA and uuidB. + * + * @param uuidA - List of ParcelUuids + * @param uuidB - List of ParcelUuids + * + */ + public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) { + return uuidA.length == 0 ? true : false; + } + + HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (uuidSet.contains(uuid)) return true; + } + return false; + } + + /** + * Returns true if all the ParcelUuids in ParcelUuidB are present in + * ParcelUuidA + * + * @param uuidA - Array of ParcelUuidsA + * @param uuidB - Array of ParcelUuidsB + * + */ + public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + + if (uuidA == null) { + return uuidB.length == 0 ? true : false; + } + + if (uuidB == null) return true; + + HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (!uuidSet.contains(uuid)) return false; + } + return true; + } + +} diff --git a/framework/java/android/bluetooth/Database.java b/framework/java/android/bluetooth/Database.java deleted file mode 100644 index fef641a5f7c..00000000000 --- a/framework/java/android/bluetooth/Database.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.RfcommSocket; - -import android.util.Log; - -import java.io.*; -import java.util.*; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * A low-level API to the Service Discovery Protocol (SDP) Database. - * - * Allows service records to be added to the local SDP database. Once added, - * these services will be advertised to remote devices when they make SDP - * queries on this device. - * - * Currently this API is a thin wrapper to the bluez SDP Database API. See: - * http://wiki.bluez.org/wiki/Database - * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords - * @hide - */ -public final class Database { - private static Database mInstance; - - private static final String sLogName = "android.bluetooth.Database"; - - /** - * Class load time initialization - */ - static { - classInitNative(); - } - private native static void classInitNative(); - - /** - * Private to enforce singleton property - */ - private Database() { - initializeNativeDataNative(); - } - private native void initializeNativeDataNative(); - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - private native void cleanupNativeDataNative(); - - /** - * Singelton accessor - * @return The singleton instance of Database - */ - public static synchronized Database getInstance() { - if (mInstance == null) { - mInstance = new Database(); - } - return mInstance; - } - - /** - * Advertise a service with an RfcommSocket. - * - * This adds the service the SDP Database with the following attributes - * set: Service Name, Protocol Descriptor List, Service Class ID List - * TODO: Construct a byte[] record directly, rather than via XML. - * @param socket The rfcomm socket to advertise (by channel). - * @param serviceName A short name for this service - * @param uuid - * Unique identifier for this service, by which clients - * can search for your service - * @return Handle to the new service record - */ - public int advertiseRfcommService(RfcommSocket socket, - String serviceName, - UUID uuid) throws IOException { - String xmlRecord = - "\n" + - "\n" + - " \n" + // ServiceClassIDList - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ProtocolDescriptorList - " \n" + - " \n" + - " \n" + // L2CAP - " \n" + - " \n" + - " \n" + // RFCOMM - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + // ServiceName - " \n" + - " \n" + - "\n"; - Log.i(sLogName, xmlRecord); - return addServiceRecordFromXml(xmlRecord); - } - - - /** - * Add a new service record. - * @param record The byte[] record - * @return A handle to the new record - */ - public synchronized int addServiceRecord(byte[] record) throws IOException { - int handle = addServiceRecordNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordNative(byte[] record) - throws IOException; - - /** - * Add a new service record, using XML. - * @param record The record as an XML string - * @return A handle to the new record - */ - public synchronized int addServiceRecordFromXml(String record) throws IOException { - int handle = addServiceRecordFromXmlNative(record); - Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle)); - return handle; - } - private native int addServiceRecordFromXmlNative(String record) - throws IOException; - - /** - * Update an exisiting service record. - * @param handle Handle to exisiting record - * @param record The updated byte[] record - */ - public synchronized void updateServiceRecord(int handle, byte[] record) { - try { - updateServiceRecordNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordNative(int handle, byte[] record) - throws IOException; - - /** - * Update an exisiting record, using XML. - * @param handle Handle to exisiting record - * @param record The record as an XML string. - */ - public synchronized void updateServiceRecordFromXml(int handle, String record) { - try { - updateServiceRecordFromXmlNative(handle, record); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void updateServiceRecordFromXmlNative(int handle, String record) - throws IOException; - - /** - * Remove a service record. - * It is only possible to remove service records that were added by the - * current connection. - * @param handle Handle to exisiting record to be removed - */ - public synchronized void removeServiceRecord(int handle) { - try { - removeServiceRecordNative(handle); - } catch (IOException e) { - Log.e(getClass().toString(), e.getMessage()); - } - } - private native void removeServiceRecordNative(int handle) throws IOException; -} diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index f987ffdd440..e2935c95d32 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -31,7 +31,7 @@ import android.util.Log; * * @hide */ -public class HeadsetBase { +public final class HeadsetBase { private static final String TAG = "Bluetooth HeadsetBase"; private static final boolean DBG = false; @@ -42,8 +42,9 @@ public class HeadsetBase { private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ - private final BluetoothDevice mBluetooth; - private final String mAddress; + private final BluetoothAdapter mAdapter; + private final BluetoothDevice mRemoteDevice; + private final String mAddress; // for native code private final int mRfcommChannel; private int mNativeData; private Thread mEventThread; @@ -73,12 +74,13 @@ public class HeadsetBase { private native void cleanupNativeDataNative(); - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, - int rfcommChannel) { + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, + int rfcommChannel) { mDirection = DIRECTION_OUTGOING; mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; + mAdapter = adapter; + mRemoteDevice = device; + mAddress = device.getAddress(); mRfcommChannel = rfcommChannel; mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); mWakeLock.setReferenceCounted(false); @@ -88,12 +90,13 @@ public class HeadsetBase { } /* Create from an already exisiting rfcomm connection */ - public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd, - int rfcommChannel, Handler handler) { + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, + int socketFd, int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); - mBluetooth = bluetooth; - mAddress = address; + mAdapter = adapter; + mRemoteDevice = device; + mAddress = device.getAddress(); mRfcommChannel = rfcommChannel; mEventThreadHandler = handler; mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); @@ -208,9 +211,10 @@ public class HeadsetBase { */ public boolean connectAsync() { - return connectAsyncNative(); + int ret = connectAsyncNative(); + return (ret == 0) ? true : false; } - private native boolean connectAsyncNative(); + private native int connectAsyncNative(); public int getRemainingAsyncConnectWaitingTimeMs() { return mTimeoutRemainingMs; @@ -252,12 +256,8 @@ public class HeadsetBase { return mEventThread != null; } - public String getAddress() { - return mAddress; - } - - public String getName() { - return mBluetooth.getRemoteName(mAddress); + public BluetoothDevice getRemoteDevice() { + return mRemoteDevice; } public int getDirection() { diff --git a/framework/java/android/bluetooth/IBluetoothDevice.aidl b/framework/java/android/bluetooth/IBluetooth.aidl similarity index 58% rename from framework/java/android/bluetooth/IBluetoothDevice.aidl rename to framework/java/android/bluetooth/IBluetooth.aidl index 6cd792e26a5..7e752af428f 100644 --- a/framework/java/android/bluetooth/IBluetoothDevice.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -16,14 +16,15 @@ package android.bluetooth; -import android.bluetooth.IBluetoothDeviceCallback; +import android.bluetooth.IBluetoothCallback; +import android.os.ParcelUuid; /** * System private API for talking with the Bluetooth service. * * {@hide} */ -interface IBluetoothDevice +interface IBluetooth { boolean isEnabled(); int getBluetoothState(); @@ -33,28 +34,16 @@ interface IBluetoothDevice String getAddress(); String getName(); boolean setName(in String name); - String getVersion(); - String getRevision(); - String getManufacturer(); - String getCompany(); int getScanMode(); - boolean setScanMode(int mode); + boolean setScanMode(int mode, int duration); int getDiscoverableTimeout(); boolean setDiscoverableTimeout(int timeout); - boolean startDiscovery(boolean resolveNames); + boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); - boolean startPeriodicDiscovery(); - boolean stopPeriodicDiscovery(); - boolean isPeriodicDiscovery(); - String[] listRemoteDevices(); - - String[] listAclConnections(); - boolean isAclConnected(in String address); - boolean disconnectRemoteDeviceAcl(in String address); boolean createBond(in String address); boolean cancelBondProcess(in String address); @@ -63,16 +52,19 @@ interface IBluetoothDevice int getBondState(in String address); String getRemoteName(in String address); - String getRemoteVersion(in String address); - String getRemoteRevision(in String address); int getRemoteClass(in String address); - String getRemoteManufacturer(in String address); - String getRemoteCompany(in String address); - boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback); - byte[] getRemoteFeatures(in String adddress); - String lastSeen(in String address); - String lastUsed(in String address); + ParcelUuid[] getRemoteUuids(in String address); + boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback); + int getRemoteServiceChannel(in String address, in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); - boolean cancelPin(in String address); + boolean setPasskey(in String address, int passkey); + boolean setPairingConfirmation(in String address, boolean confirm); + boolean cancelPairingUserInput(in String address); + + boolean setTrust(in String address, in boolean value); + boolean getTrustState(in String address); + + int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); + void removeServiceRecord(int handle); } diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 55ff27f97ec..002cf4efff6 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -16,16 +16,20 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** * System private API for Bluetooth A2DP service * * {@hide} */ interface IBluetoothA2dp { - int connectSink(in String address); - int disconnectSink(in String address); - List listConnectedSinks(); - int getSinkState(in String address); - int setSinkPriority(in String address, int priority); - int getSinkPriority(in String address); + boolean connectSink(in BluetoothDevice device); + boolean disconnectSink(in BluetoothDevice device); + boolean suspendSink(in BluetoothDevice device); + boolean resumeSink(in BluetoothDevice device); + BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports + int getSinkState(in BluetoothDevice device); + boolean setSinkPriority(in BluetoothDevice device, int priority); + int getSinkPriority(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl similarity index 76% rename from framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl rename to framework/java/android/bluetooth/IBluetoothCallback.aidl index d05709330b7..8edb3f4c413 100644 --- a/framework/java/android/bluetooth/IBluetoothDeviceCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, The Android Open Source Project + * Copyright (C) 2009, 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. @@ -17,9 +17,11 @@ package android.bluetooth; /** + * System private API for Bluetooth service callbacks. + * * {@hide} */ -oneway interface IBluetoothDeviceCallback +interface IBluetoothCallback { - void onGetRemoteServiceChannelResult(in String address, int channel); + void onRfcommChannelFound(int channel); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 5f42fd6521b..6cccd506e26 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** * System private API for Bluetooth Headset service * @@ -23,13 +25,13 @@ package android.bluetooth; */ interface IBluetoothHeadset { int getState(); - String getHeadsetAddress(); - boolean connectHeadset(in String address); + BluetoothDevice getCurrentHeadset(); + boolean connectHeadset(in BluetoothDevice device); void disconnectHeadset(); - boolean isConnected(in String address); + boolean isConnected(in BluetoothDevice device); boolean startVoiceRecognition(); boolean stopVoiceRecognition(); - boolean setPriority(in String address, int priority); - int getPriority(in String address); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); int getBatteryUsageHint(); } diff --git a/framework/java/android/bluetooth/BluetoothError.java b/framework/java/android/bluetooth/IBluetoothPbap.aidl similarity index 54% rename from framework/java/android/bluetooth/BluetoothError.java rename to framework/java/android/bluetooth/IBluetoothPbap.aidl index 2554bead0ca..7cc77d110e9 100644 --- a/framework/java/android/bluetooth/BluetoothError.java +++ b/framework/java/android/bluetooth/IBluetoothPbap.aidl @@ -16,27 +16,17 @@ package android.bluetooth; +import android.bluetooth.BluetoothDevice; + /** - * Bluetooth API error codes. - * - * Errors are always negative. + * System private API for Bluetooth pbap service * - * @hide + * {@hide} */ -public class BluetoothError { - /** No error */ - public static final int SUCCESS = 0; - - /** Generic error */ - public static final int ERROR = -1000; - - /** Bluetooth currently disabled */ - public static final int ERROR_DISABLED = -1001; - - /** IPC is not ready, for example service is not yet bound */ - public static final int ERROR_IPC_NOT_READY = -1011; - - /** Some other IPC error, for example a RemoteException */ - public static final int ERROR_IPC = -1012; - +interface IBluetoothPbap { + int getState(); + BluetoothDevice getClient(); + boolean connect(in BluetoothDevice device); + void disconnect(); + boolean isConnected(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/RfcommSocket.java b/framework/java/android/bluetooth/RfcommSocket.java deleted file mode 100644 index a33263f5261..00000000000 --- a/framework/java/android/bluetooth/RfcommSocket.java +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (C) 2007 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 java.io.IOException; -import java.io.FileOutputStream; -import java.io.FileInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.FileDescriptor; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket - * is similar to a normal socket in that it takes an address and a port number. - * The difference is of course that the address is a Bluetooth-device address, - * and the port number is an RFCOMM channel. The API allows for the - * establishment of listening sockets via methods - * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and - * {@link #accept(RfcommSocket, int) accept}, as well as for the making of - * outgoing connections with {@link #connect(String, int) connect}, - * {@link #connectAsync(String, int) connectAsync}, and - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * After constructing a socket, you need to {@link #create() create} it and then - * {@link #destroy() destroy} it when you are done using it. Both - * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return - * a {@link java.io.FileDescriptor FileDescriptor} for the actual data. - * Alternatively, you may call {@link #getInputStream() getInputStream} and - * {@link #getOutputStream() getOutputStream} to retrieve the respective streams - * without going through the FileDescriptor. - * - * @hide - */ -public class RfcommSocket { - - /** - * Used by the native implementation of the class. - */ - private int mNativeData; - - /** - * Used by the native implementation of the class. - */ - private int mPort; - - /** - * Used by the native implementation of the class. - */ - private String mAddress; - - /** - * We save the return value of {@link #create() create} and - * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to - * retrieve the I/O streams. - */ - private FileDescriptor mFd; - - /** - * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect}, - * if the return value is zero, then, the the remaining time left to wait is - * written into this variable (by the native implementation). It is possible - * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before - * the user-specified timeout expires, which is why we save the remaining - * time in this member variable for the user to retrieve by calling method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}. - */ - private int mTimeoutRemainingMs; - - /** - * Set to true when an asynchronous (nonblocking) connect is in progress. - * {@see #connectAsync(String,int)}. - */ - private boolean mIsConnecting; - - /** - * Set to true after a successful call to {@link #bind(String,int) bind} and - * used for error checking in {@link #listen(int) listen}. Reset to false - * on {@link #destroy() destroy}. - */ - private boolean mIsBound = false; - - /** - * Set to true after a successful call to {@link #listen(int) listen} and - * used for error checking in {@link #accept(RfcommSocket,int) accept}. - * Reset to false on {@link #destroy() destroy}. - */ - private boolean mIsListening = false; - - /** - * Used to store the remaining time after an accept with a non-negative - * timeout returns unsuccessfully. It is possible that a blocking - * {@link #accept(int) accept} may wait for less than the time specified by - * the user, which is why we store the remainder in this member variable for - * it to be retrieved with method - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}. - */ - private int mAcceptTimeoutRemainingMs; - - /** - * Maintained by {@link #getInputStream() getInputStream}. - */ - protected FileInputStream mInputStream; - - /** - * Maintained by {@link #getOutputStream() getOutputStream}. - */ - protected FileOutputStream mOutputStream; - - private native void initializeNativeDataNative(); - - /** - * Constructor. - */ - public RfcommSocket() { - initializeNativeDataNative(); - } - - private native void cleanupNativeDataNative(); - - /** - * Called by the GC to clean up the native data that we set up when we - * construct the object. - */ - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private native static void classInitNative(); - - static { - classInitNative(); - } - - /** - * Creates a socket. You need to call this method before performing any - * other operation on a socket. - * - * @return FileDescriptor for the data stream. - * @throws IOException - * @see #destroy() - */ - public FileDescriptor create() throws IOException { - if (mFd == null) { - mFd = createNative(); - } - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - private native FileDescriptor createNative(); - - /** - * Destroys a socket created by {@link #create() create}. Call this - * function when you no longer use the socket in order to release the - * underlying OS resources. - * - * @see #create() - */ - public void destroy() { - synchronized (this) { - destroyNative(); - mFd = null; - mIsBound = false; - mIsListening = false; - } - } - - private native void destroyNative(); - - /** - * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket. - * - * @return the FileDescriptor - * @throws IOException - * when the socket has not been {@link #create() created}. - */ - public FileDescriptor getFileDescriptor() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - return mFd; - } - - /** - * Retrieves the input stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return InputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public InputStream getInputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mInputStream == null) { - mInputStream = new FileInputStream(mFd); - } - - return mInputStream; - } - } - - /** - * Retrieves the output stream from the socket. Alternatively, you can do - * that from the FileDescriptor returned by {@link #create() create} or - * {@link #accept(RfcommSocket, int) accept}. - * - * @return OutputStream - * @throws IOException - * if you have not called {@link #create() create} on the - * socket. - */ - public OutputStream getOutputStream() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - - synchronized (this) { - if (mOutputStream == null) { - mOutputStream = new FileOutputStream(mFd); - } - - return mOutputStream; - } - } - - /** - * Starts a blocking connect to a remote RFCOMM socket. It takes the address - * of a device and the RFCOMM channel (port) to which to connect. - * - * @param address - * is the Bluetooth address of the remote device. - * @param port - * is the RFCOMM channel - * @return true on success, false on failure - * @throws IOException - * if {@link #create() create} has not been called. - * @see #connectAsync(String, int) - */ - public boolean connect(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return connectNative(address, port); - } - } - - private native boolean connectNative(String address, int port); - - /** - * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket. - * It takes the address of the device to connect to, as well as the RFCOMM - * channel (port). On successful return (return value is true), you need to - * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to - * block for up to a specified number of milliseconds while waiting for the - * asyncronous connect to complete. - * - * @param address - * of remote device - * @param port - * the RFCOMM channel - * @return true when the asynchronous connect has successfully started, - * false if there was an error. - * @throws IOException - * is you have not called {@link #create() create} - * @see #waitForAsyncConnect(int) - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connect(String, int) - */ - public boolean connectAsync(String address, int port) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsConnecting = connectAsyncNative(address, port); - return mIsConnecting; - } - } - - private native boolean connectAsyncNative(String address, int port); - - /** - * Interrupts an asynchronous connect in progress. This method does nothing - * when there is no asynchronous connect in progress. - * - * @throws IOException - * if you have not called {@link #create() create}. - * @see #connectAsync(String, int) - */ - public void interruptAsyncConnect() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsConnecting) { - mIsConnecting = !interruptAsyncConnectNative(); - } - } - } - - private native boolean interruptAsyncConnectNative(); - - /** - * Tells you whether there is an asynchronous connect in progress. This - * method returns an undefined value when there is a synchronous connect in - * progress. - * - * @return true if there is an asyc connect in progress, false otherwise - * @see #connectAsync(String, int) - */ - public boolean isConnecting() { - return mIsConnecting; - } - - /** - * Blocks for a specified amount of milliseconds while waiting for an - * asynchronous connect to complete. Returns an integer value to indicate - * one of the following: the connect succeeded, the connect is still in - * progress, or the connect failed. It is possible for this method to block - * for less than the time specified by the user, and still return zero - * (i.e., async connect is still in progress.) For this reason, if the - * return value is zero, you need to call method - * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs} - * to retrieve the remaining time. - * - * @param timeoutMs - * the time to block while waiting for the async connect to - * complete. - * @return a positive value if the connect succeeds; zero, if the connect is - * still in progress, and a negative value if the connect failed. - * - * @throws IOException - * @see #getRemainingAsyncConnectWaitingTimeMs() - * @see #connectAsync(String, int) - */ - public int waitForAsyncConnect(int timeoutMs) throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - int ret = waitForAsyncConnectNative(timeoutMs); - if (ret != 0) { - mIsConnecting = false; - } - return ret; - } - } - - private native int waitForAsyncConnectNative(int timeoutMs); - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #waitForAsyncConnect(int) waitForAsyncConnect}. - * - * It is possible that waitForAsyncConnect() waits for less than the time - * specified by the user, and still returns zero (i.e., async connect is - * still in progress.) For this reason, if the return value is zero, you - * need to call this method to retrieve the remaining time before you call - * waitForAsyncConnect again. - * - * @return the remaining timeout in milliseconds. - * @see #waitForAsyncConnect(int) - * @see #connectAsync(String, int) - */ - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /** - * Shuts down both directions on a socket. - * - * @return true on success, false on failure; if the return value is false, - * the socket might be left in a patially shut-down state (i.e. one - * direction is shut down, but the other is still open.) In this - * case, you should {@link #destroy() destroy} and then - * {@link #create() create} the socket again. - * @throws IOException - * is you have not caled {@link #create() create}. - * @see #shutdownInput() - * @see #shutdownOutput() - */ - public boolean shutdown() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (shutdownNative(true)) { - return shutdownNative(false); - } - - return false; - } - } - - /** - * Shuts down the input stream of the socket, but leaves the output stream - * in its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownOutput() - */ - public boolean shutdownInput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(true); - } - } - - /** - * Shut down the output stream of the socket, but leaves the input stream in - * its current state. - * - * @return true on success, false on failure - * @throws IOException - * is you have not called {@link #create() create} - * @see #shutdown() - * @see #shutdownInput() - */ - public boolean shutdownOutput() throws IOException { - synchronized (this) { - if (mFd == null) { - throw new IOException("socket not created"); - } - return shutdownNative(false); - } - } - - private native boolean shutdownNative(boolean shutdownInput); - - /** - * Tells you whether a socket is connected to another socket. This could be - * for input or output or both. - * - * @return true if connected, false otherwise. - * @see #isInputConnected() - * @see #isOutputConnected() - */ - public boolean isConnected() { - return isConnectedNative() > 0; - } - - /** - * Determines whether input is connected (i.e., whether you can receive data - * on this socket.) - * - * @return true if input is connected, false otherwise. - * @see #isConnected() - * @see #isOutputConnected() - */ - public boolean isInputConnected() { - return (isConnectedNative() & 1) != 0; - } - - /** - * Determines whether output is connected (i.e., whether you can send data - * on this socket.) - * - * @return true if output is connected, false otherwise. - * @see #isConnected() - * @see #isInputConnected() - */ - public boolean isOutputConnected() { - return (isConnectedNative() & 2) != 0; - } - - private native int isConnectedNative(); - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. The port is automatically selected as the first - * available port in the range 12 to 30. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * TODO: Use bind(0) in native code to have the kernel select an unused - * port. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - for (int port = 12; port <= 30; port++) { - if (bindNative(device, port)) { - mIsBound = true; - return true; - } - } - mIsBound = false; - return false; - } - - /** - * Binds a listening socket to the local device, or a non-listening socket - * to a remote device. - * - * NOTE: Currently we ignore the device parameter and always bind the socket - * to the local device, assuming that it is a listening socket. - * - * @param device - * Bluetooth address of device to bind to (currently ignored). - * @param port - * RFCOMM channel to bind socket to. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} - * @see #listen(int) - * @see #accept(RfcommSocket,int) - */ - public boolean bind(String device, int port) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - mIsBound = bindNative(device, port); - return mIsBound; - } - - private native boolean bindNative(String device, int port); - - /** - * Starts listening for incoming connections on this socket, after it has - * been bound to an address and RFCOMM channel with - * {@link #bind(String,int) bind}. - * - * @param backlog - * the number of pending incoming connections to queue for - * {@link #accept(RfcommSocket, int) accept}. - * @return true on success, false on failure - * @throws IOException - * if you have not called {@link #create() create} or if the - * socket has not been bound to a device and RFCOMM channel. - */ - public boolean listen(int backlog) throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsBound) { - throw new IOException("socket not bound"); - } - mIsListening = listenNative(backlog); - return mIsListening; - } - - private native boolean listenNative(int backlog); - - /** - * Accepts incoming-connection requests for a listening socket bound to an - * RFCOMM channel. The user may provide a time to wait for an incoming - * connection. - * - * Note that this method may return null (i.e., no incoming connection) - * before the user-specified timeout expires. For this reason, on a null - * return value, you need to call - * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs} - * in order to see how much time is left to wait, before you call this - * method again. - * - * @param newSock - * is set to the new socket that is created as a result of a - * successful accept. - * @param timeoutMs - * time (in milliseconds) to block while waiting to an - * incoming-connection request. A negative value is an infinite - * wait. - * @return FileDescriptor of newSock on success, null on failure. Failure - * occurs if the timeout expires without a successful connect. - * @throws IOException - * if the socket has not been {@link #create() create}ed, is - * not bound, or is not a listening socket. - * @see #bind(String, int) - * @see #listen(int) - * @see #getRemainingAcceptWaitingTimeMs() - */ - public FileDescriptor accept(RfcommSocket newSock, int timeoutMs) - throws IOException { - synchronized (newSock) { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (mIsListening == false) { - throw new IOException("not listening on socket"); - } - newSock.mFd = acceptNative(newSock, timeoutMs); - return newSock.mFd; - } - } - - /** - * Returns the number of milliseconds left to wait after the last call to - * {@link #accept(RfcommSocket, int) accept}. - * - * Since accept() may return null (i.e., no incoming connection) before the - * user-specified timeout expires, you need to call this method in order to - * see how much time is left to wait, and wait for that amount of time - * before you call accept again. - * - * @return the remaining time, in milliseconds. - */ - public int getRemainingAcceptWaitingTimeMs() { - return mAcceptTimeoutRemainingMs; - } - - private native FileDescriptor acceptNative(RfcommSocket newSock, - int timeoutMs); - - /** - * Get the port (rfcomm channel) associated with this socket. - * - * This is only valid if the port has been set via a successful call to - * {@link #bind(String, int)}, {@link #connect(String, int)} - * or {@link #connectAsync(String, int)}. This can be checked - * with {@link #isListening()} and {@link #isConnected()}. - * @return Port (rfcomm channel) - */ - public int getPort() throws IOException { - if (mFd == null) { - throw new IOException("socket not created"); - } - if (!mIsListening && !isConnected()) { - throw new IOException("not listening or connected on socket"); - } - return mPort; - } - - /** - * Return true if this socket is listening ({@link #listen(int)} - * has been called successfully). - */ - public boolean isListening() { - return mIsListening; - } -} diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java index 1bf786ffd3a..116310ad9d7 100644 --- a/framework/java/android/bluetooth/ScoSocket.java +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -87,7 +87,7 @@ public class ScoSocket { * Does not block. */ public synchronized boolean connect(String address) { - if (VDBG) log("connect() " + this); + if (DBG) log("connect() " + this); if (mState != STATE_READY) { if (DBG) log("connect(): Bad state"); return false; diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 79abf0cb4a0..4f0755e7150 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -1,13 +1,109 @@ -Provides classes that manage Bluetooth functionality on the device. -

-The Bluetooth APIs allow applications can connect and disconnect headsets, or scan -for other kinds of Bluetooth devices and pair them. Further control includes the -ability to write and modify the local Service Discovery Protocol (SDP) database, -query the SDP database of other Bluetooth devices, establish RFCOMM -channels/sockets on Android, and connect to specified sockets on other devices. +Provides classes that manage Bluetooth functionality, such as scanning for +devices, connecting with devices, and managing data transfer between devices. + +

The Bluetooth APIs let applications:

+
    +
  • Scan for other Bluetooth devices
  • +
  • Query the local Bluetooth adapter for paired Bluetooth devices
  • +
  • Establish RFCOMM channels/sockets
  • +
  • Connect to specified sockets on other devices
  • +
  • Transfer data to and from other devices
  • +
+ +

Note: +To perform Bluetooth communication using these APIs, an application must +declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some +additional functionality, such as requesting device discovery and +pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +permission.

-

Remember, not all Android devices are guaranteed to have Bluetooth functionality.

+ +

Overview

+ +

Here's a basic introduction to the Bluetooth classes:

+
+
{@link android.bluetooth.BluetoothAdapter}
+
This represents the local Bluetooth adapter, which is essentially the + entry-point to performing any interaction with Bluetooth. With it, you can + discover other Bluetooth devices, query a list of bonded (paired) devices, + initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC + address, and create a {@link android.bluetooth.BluetoothServerSocket} to + listen for communications from other devices.
+ +
{@link android.bluetooth.BluetoothDevice}
+
This represents a remote Bluetooth device. Use this to request a + connection with a remote device through a + {@link android.bluetooth.BluetoothSocket} + or query information about the device such as its name, address, class, and + bonding state.
+ +
{@link android.bluetooth.BluetoothSocket}
+
This represents the interface for a Bluetooth socket + (similar to a TCP client-side {@link java.net.Socket}). This is the + connection point that allows an app to transfer data with another Bluetooth + device via {@link java.io.InputStream} and {@link java.io.OutputStream}.
+
{@link android.bluetooth.BluetoothServerSocket}
+ +
This represents an open server socket that listens for incoming requests + (similar to a TCP server-side {@link java.net.ServerSocket}). + When attempting to connect two Android devices, one device will need to open + a server socket with this class. When a connection is accepted, a new + {@link android.bluetooth.BluetoothSocket} will be returned, + which can be used to manage the connection and transfer data.
+ +
{@link android.bluetooth.BluetoothClass}
+
This represents the Bluetooth class for a device which describes general + characteristics and capabilities of a device. This class and its subclasses + don't provide any actual functionality. The sub-classes are entirely composed + of constants for the device and service class definitions.
+
+ + +

Example Procedure

+ +

For example, here's an pseudo-code procedure for discovering and +connecting a remote device, and transfering data:

+ +
    +
  1. Register a {@link android.content.BroadcastReceiver} that accepts the + {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.
  2. +
  3. Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to + retrieve the Android system's local + {@link android.bluetooth.BluetoothAdapter}.
  4. +
  5. Call {@link android.bluetooth.BluetoothAdapter#startDiscovery() + BluetoothAdapter.startDiscovery()} to scan for local devices. This is where + the BroadcastReceiver comes in; Android now scans for devices and will + broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent + for each remote device discovered. The + {@link android.content.BroadcastReceiver} + you created will receive each Intent.
  6. +
  7. The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent + includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE} + Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice} + object. Extract this from the Intent and call + {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID) + BluetoothDevice.createRfcommSocketToServiceRecord()} + to open a {@link android.bluetooth.BluetoothSocket} with a chosen + remote device.
  8. +
  9. Call {@link android.bluetooth.BluetoothSocket#connect() + BluetoothSocket.connect()} to connect with the remote device.
  10. +
  11. When successfully connected, call + {@link android.bluetooth.BluetoothSocket#getInputStream() + BluetoothSocket.getInputStream()} and/or + {@link android.bluetooth.BluetoothSocket#getOutputStream() + BluetoothSocket.getOutputStream()} to retreive an + {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively, + which are hooked into the socket.
  12. +
  13. Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and + {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer + data.
  14. +
+ + + +

Note: +Not all Android devices are guaranteed to have Bluetooth functionality.

-- GitLab From a7848ce834a484d194d05afd8097ab3014f9b449 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 19 Nov 2009 17:00:19 -0800 Subject: [PATCH 0082/1408] docs for ESR: add docs to bluetooth explainin that discovery should be cancelled before connecting to a device bug: 2160782,2198463 --- .../android/bluetooth/BluetoothAdapter.java | 21 ++++++++++++++----- .../bluetooth/BluetoothServerSocket.java | 8 ++++++- .../android/bluetooth/BluetoothSocket.java | 9 ++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 595156f71c5..f8cf4f24913 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -130,13 +130,13 @@ public final class BluetoothAdapter { /** * Activity Action: Show a system activity that requests discoverable mode. - *

This activity will also request the user to turn on Bluetooth if it + * This activity will also request the user to turn on Bluetooth if it * is not currently enabled. *

Discoverable mode is equivalent to {@link * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see * this Bluetooth adapter when they perform a discovery. - *

For privacy, Android is not by default discoverable. - *

The sender can optionally use extra field {@link + *

For privacy, Android is not discoverable by default. + *

The sender of this Intent can optionally use extra field {@link * #EXTRA_DISCOVERABLE_DURATION} to request the duration of * discoverability. Currently the default duration is 120 seconds, and * maximum duration is capped at 300 seconds for each request. @@ -146,7 +146,8 @@ public final class BluetoothAdapter { * will be the duration (in seconds) of discoverability, or a negative * value if the user rejected discoverability. *

Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} - * for global notification whenever the scan mode changes. + * for global notification whenever the scan mode changes. For example, an + * application can be notified when the device has ended discoverability. *

Requires {@link android.Manifest.permission#BLUETOOTH} */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -547,7 +548,10 @@ public final class BluetoothAdapter { * remote Bluetooth devices should not be attempted while discovery is in * progress, and existing connections will experience limited bandwidth * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing - * discovery. + * discovery. Discovery is not managed by the Activity, + * but is run as a system service, so an application should always call + * {@link BluetoothAdapter#cancelDiscovery()} even if it + * did not directly request a discovery, just to be sure. *

Device discovery will only find remote devices that are currently * discoverable (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. @@ -565,6 +569,13 @@ public final class BluetoothAdapter { /** * Cancel the current device discovery process. *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

Because discovery is a heavyweight precedure for the Bluetooth + * adapter, this method should always be called before attempting to connect + * to a remote device with {@link + * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by + * the Activity, but is run as a system service, so an application should + * always call cancel discovery even if it did not directly request a + * discovery, just to be sure. * * @return true on success, false on error */ diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 1b23f6c048d..c9c6c0acd9a 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -42,7 +42,11 @@ import java.io.IOException; * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call * {@link #accept()} to listen for incoming connection requests. This call * will block until a connection is established, at which point, it will return - * a {@link BluetoothSocket} to manage the connection. + * a {@link BluetoothSocket} to manage the connection. Once the {@link + * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on + * the {@link BluetoothServerSocket} when it's no longer needed for accepting + * connections. Closing the {@link BluetoothServerSocket} will not + * close the returned {@link BluetoothSocket}. * *

{@link BluetoothServerSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing @@ -105,6 +109,8 @@ public final class BluetoothServerSocket implements Closeable { * Immediately close this socket, and release all associated resources. *

Causes blocked calls on this socket in other threads to immediately * throw an IOException. + *

Closing the {@link BluetoothServerSocket} will not + * close any {@link BluetoothSocket} received from {@link #accept()}. */ public void close() throws IOException { synchronized (this) { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index dbcc758574b..ad033999e0d 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -180,6 +180,15 @@ public final class BluetoothSocket implements Closeable { *

This method will block until a connection is made or the connection * fails. If this method returns without an exception then this socket * is now connected. + *

Creating new connections to + * remote Bluetooth devices should not be attempted while device discovery + * is in progress. Device discovery is a heavyweight procedure on the + * Bluetooth adapter and will significantly slow a device connection. + * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing + * discovery. Discovery is not managed by the Activity, + * but is run as a system service, so an application should always call + * {@link BluetoothAdapter#cancelDiscovery()} even if it + * did not directly request a discovery, just to be sure. *

{@link #close} can be used to abort this call from another thread. * @throws IOException on error, for example connection failure */ -- GitLab From 124c49280eed8e03d3ccb38d666ffafe63b377f0 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 20 Nov 2009 15:21:47 -0800 Subject: [PATCH 0083/1408] Add a new priority for Auto Connection of A2DP. 1. PRIORITY_OFF is when user unchecks A2DP connection profile box. 2. By default, when you bond, it will be PRIORITY_ON. 3. When the profile gets connected, the priority gets set to PRIORITY_AUTO_CONNECT. This means that we will connect automatically to this profile. 4. When the user disconnects, we downgrade the priority to PRIORITY_ON, which means we won't reconnect automatically. a) We need to make a similar change to Handsfree profile. b) We need to rework the profile management design and code which will fix the 6 second timer that we have for A2DP reconnection. --- framework/java/android/bluetooth/BluetoothA2dp.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index e8a69d8c1f7..7df3637f318 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -42,7 +42,7 @@ import java.util.HashSet; * * Currently the BluetoothA2dp service runs in the system server and this * proxy object will be immediately bound to the service on construction. - * + * * Currently this class provides methods to connect to A2DP audio sinks. * * @hide @@ -74,9 +74,12 @@ public final class BluetoothA2dp { /** Playing implies connected */ public static final int STATE_PLAYING = 4; + /** Default priority for a2dp devices that we try to auto-connect + * and allow incoming connections */ + public static final int PRIORITY_AUTO_CONNECT = 1000; /** Default priority for a2dp devices that should allow incoming * connections */ - public static final int PRIORITY_AUTO = 100; + public static final int PRIORITY_ON = 100; /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; -- GitLab From 38ee0b80666557495e1ad7a49c4523a50de664ce Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 25 Nov 2009 16:01:51 -0800 Subject: [PATCH 0084/1408] Add AUTO_CONNECT priority for Headset profile. Also, don't set priority to ON while disconnecting. This logic has been pushed up to the Settings app. --- .../java/android/bluetooth/BluetoothHeadset.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 90cff6b8c90..5eb655a2793 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -100,9 +100,14 @@ public final class BluetoothHeadset { /** Connection canceled before completetion. */ public static final int RESULT_CANCELED = 2; - /** Default priority for headsets that should be auto-connected */ - public static final int PRIORITY_AUTO = 100; - /** Default priority for headsets that should not be auto-connected */ + /** Default priority for headsets that for which we will accept + * inconing connections and auto-connect */ + public static final int PRIORITY_AUTO_CONNECT = 1000; + /** Default priority for headsets that for which we will accept + * inconing connections but not auto-connect */ + public static final int PRIORITY_ON = 100; + /** Default priority for headsets that should not be auto-connected + * and not allow incoming connections. */ public static final int PRIORITY_OFF = 0; /** The voice dialer 'works' but the user experience is poor. The voice -- GitLab From 2fafca37365bbc0e6858f277c4000106fd76f3b3 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 20 Nov 2009 15:21:47 -0800 Subject: [PATCH 0085/1408] Add a new priority for Auto Connection of A2DP. DO NOT MERGE. 1. PRIORITY_OFF is when user unchecks A2DP connection profile box. 2. By default, when you bond, it will be PRIORITY_ON. 3. When the profile gets connected, the priority gets set to PRIORITY_AUTO_CONNECT. This means that we will connect automatically to this profile. 4. When the user disconnects, we downgrade the priority to PRIORITY_ON, which means we won't reconnect automatically. a) We need to make a similar change to Handsfree profile. b) We need to rework the profile management design and code which will fix the 6 second timer that we have for A2DP reconnection. Add AUTO_CONNECT priority for Headset profile. Also, don't set priority to ON while disconnecting. This logic has been pushed up to the Settings app. Dr No: Eastham Bug: 2133530 --- framework/java/android/bluetooth/BluetoothA2dp.java | 7 +++++-- .../java/android/bluetooth/BluetoothHeadset.java | 11 ++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index e8a69d8c1f7..7df3637f318 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -42,7 +42,7 @@ import java.util.HashSet; * * Currently the BluetoothA2dp service runs in the system server and this * proxy object will be immediately bound to the service on construction. - * + * * Currently this class provides methods to connect to A2DP audio sinks. * * @hide @@ -74,9 +74,12 @@ public final class BluetoothA2dp { /** Playing implies connected */ public static final int STATE_PLAYING = 4; + /** Default priority for a2dp devices that we try to auto-connect + * and allow incoming connections */ + public static final int PRIORITY_AUTO_CONNECT = 1000; /** Default priority for a2dp devices that should allow incoming * connections */ - public static final int PRIORITY_AUTO = 100; + public static final int PRIORITY_ON = 100; /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 90cff6b8c90..5eb655a2793 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -100,9 +100,14 @@ public final class BluetoothHeadset { /** Connection canceled before completetion. */ public static final int RESULT_CANCELED = 2; - /** Default priority for headsets that should be auto-connected */ - public static final int PRIORITY_AUTO = 100; - /** Default priority for headsets that should not be auto-connected */ + /** Default priority for headsets that for which we will accept + * inconing connections and auto-connect */ + public static final int PRIORITY_AUTO_CONNECT = 1000; + /** Default priority for headsets that for which we will accept + * inconing connections but not auto-connect */ + public static final int PRIORITY_ON = 100; + /** Default priority for headsets that should not be auto-connected + * and not allow incoming connections. */ public static final int PRIORITY_OFF = 0; /** The voice dialer 'works' but the user experience is poor. The voice -- GitLab From 33e229a7b18199d32ddf1c17e3f0edb1b3d63945 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 2 Dec 2009 17:28:38 -0800 Subject: [PATCH 0086/1408] Add support for Car Dock. Dr No: Eastham Bug: 2133530 --- framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++++++ framework/java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6cb9770a610..cf9c58f1228 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -624,6 +624,14 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ + public boolean isBluetoothDock() { + try { + return sService.isBluetoothDock(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 7e752af428f..08687795d93 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -64,6 +64,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + boolean isBluetoothDock(in String address); int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); void removeServiceRecord(int handle); -- GitLab From 42e5dc7476e7ddccdf2f68030aaff48b35394e4e Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 4 Dec 2009 15:10:54 -0800 Subject: [PATCH 0087/1408] Add API to get Active Sinks. Allow incoming connections only when there are no active sinks. Dr No: Eastham Bug: 2133530 --- .../java/android/bluetooth/BluetoothA2dp.java | 16 ++++++++++++++++ .../java/android/bluetooth/IBluetoothA2dp.aidl | 1 + 2 files changed, 17 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7df3637f318..fda9b81c8f0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -199,6 +199,22 @@ public final class BluetoothA2dp { } } + /** Check if any A2DP sink is in Non Disconnected state + * i.e playing, connected, connecting, disconnecting. + * @return a unmodifiable set of connected A2DP sinks, or null on error. + * @hide + */ + public Set getNonDisconnectedSinks() { + if (DBG) log("getNonDisconnectedSinks()"); + try { + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(mService.getNonDisconnectedSinks()))); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + /** Get the state of an A2DP sink * @param device Remote BT device. * @return State code, one of STATE_ diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 002cf4efff6..168fe3b252d 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -29,6 +29,7 @@ interface IBluetoothA2dp { boolean suspendSink(in BluetoothDevice device); boolean resumeSink(in BluetoothDevice device); BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports + BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports int getSinkState(in BluetoothDevice device); boolean setSinkPriority(in BluetoothDevice device, int priority); int getSinkPriority(in BluetoothDevice device); -- GitLab From cdd8b7ec707d0005c3176b4481cab730a92df236 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 9 Dec 2009 16:07:39 -0800 Subject: [PATCH 0088/1408] docs: add the Bluetooth developer guide, and make some revisions to the BT javadocs --- .../android/bluetooth/BluetoothAdapter.java | 23 +++++++++++++++---- .../android/bluetooth/BluetoothClass.java | 4 ---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bf561ef94ed..8eda844380e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -370,9 +370,17 @@ public final class BluetoothAdapter { } /** - * Turn on the local Bluetooth adapter. + * Turn on the local Bluetooth adapter—do not use without explicit + * user action to turn on Bluetooth. *

This powers on the underlying Bluetooth hardware, and starts all * Bluetooth system services. + *

Bluetooth should never be enabled without + * direct user consent. If you want to turn on Bluetooth in order + * to create a wireless connection, you should use the {@link + * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests + * user permission to turn on Bluetooth. The {@link #enable()} method is + * provided only for applications that include a user interface for changing + * system settings, such as a "power manager" app.

*

This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns @@ -382,7 +390,8 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned on - * such as Airplane mode, or the adapter is already turned on. - *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission * * @return true to indicate adapter startup has begun, or false on * immediate error @@ -395,9 +404,14 @@ public final class BluetoothAdapter { } /** - * Turn off the local Bluetooth adapter. + * Turn off the local Bluetooth adapter—do not use without explicit + * user action to turn off Bluetooth. *

This gracefully shuts down all Bluetooth connections, stops Bluetooth * system services, and powers down the underlying Bluetooth hardware. + *

Bluetooth should never be disbled without + * direct user consent. The {@link #disable()} method is + * provided only for applications that include a user interface for changing + * system settings, such as a "power manager" app.

*

This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns @@ -407,7 +421,8 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned off - * such as the adapter already being turned off. - *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission * * @return true to indicate adapter shutdown has begun, or false on * immediate error diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index bc067130584..c7fea9e1dad 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -25,10 +25,6 @@ import android.os.Parcelable; * specify the general device type such as a phone, a computer, or * headset, and whether it's capable of services such as audio or telephony. * - *

The Bluetooth class is useful as a hint to roughly describe a device (for example to - * show an icon in the UI), but does not reliably describe which Bluetooth - * profiles or services are actually supported by a device. - * *

Every Bluetooth class is composed of zero or more service classes, and * exactly one device class. The device class is further broken down into major * and minor device class components. -- GitLab From 3227286aa0370c727b403562362e7b715385ab68 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 11 Dec 2009 12:00:31 -0800 Subject: [PATCH 0089/1408] Use UNDEFINED priorities when unpaired. For the docks, we can set if a device is preferred or not before pairing process. This was getting overridden when we pair. This problem doesn't happen with normal headsets. Dr No: Eastham Bug: 2318290 --- framework/java/android/bluetooth/BluetoothA2dp.java | 2 ++ framework/java/android/bluetooth/BluetoothHeadset.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index fda9b81c8f0..7e5f858f050 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -83,6 +83,8 @@ public final class BluetoothA2dp { /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; + /** Default priority when not set or when the device is unpaired */ + public static final int PRIORITY_UNDEFINED = -1; private final IBluetoothA2dp mService; private final Context mContext; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 5eb655a2793..b7929658156 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -109,6 +109,8 @@ public final class BluetoothHeadset { /** Default priority for headsets that should not be auto-connected * and not allow incoming connections. */ public static final int PRIORITY_OFF = 0; + /** Default priority when not set or when the device is unpaired */ + public static final int PRIORITY_UNDEFINED = -1; /** The voice dialer 'works' but the user experience is poor. The voice * recognizer has trouble dealing with the 8kHz SCO signal, and it still -- GitLab From d8edb3b4a4c9ccc1ca5b688d66de7b4903ec7fdb Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 16 Dec 2009 18:32:55 -0800 Subject: [PATCH 0090/1408] docs: update the Bluetooth guide with links to the sample app, scrub the sibling files, and revise the Bluetooth package summary to point to the BT dev guide. --- framework/java/android/bluetooth/package.html | 90 ++----------------- 1 file changed, 5 insertions(+), 85 deletions(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 4f0755e7150..5ff240c706c 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -12,96 +12,16 @@ devices, connecting with devices, and managing data transfer between devices.

  • Transfer data to and from other devices
  • -

    Note: +

    To perform Bluetooth communication using these APIs, an application must declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some -additional functionality, such as requesting device discovery and -pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +additional functionality, such as requesting device discovery, +also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.

    -

    Overview

    - -

    Here's a basic introduction to the Bluetooth classes:

    -
    -
    {@link android.bluetooth.BluetoothAdapter}
    -
    This represents the local Bluetooth adapter, which is essentially the - entry-point to performing any interaction with Bluetooth. With it, you can - discover other Bluetooth devices, query a list of bonded (paired) devices, - initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC - address, and create a {@link android.bluetooth.BluetoothServerSocket} to - listen for communications from other devices.
    - -
    {@link android.bluetooth.BluetoothDevice}
    -
    This represents a remote Bluetooth device. Use this to request a - connection with a remote device through a - {@link android.bluetooth.BluetoothSocket} - or query information about the device such as its name, address, class, and - bonding state.
    - -
    {@link android.bluetooth.BluetoothSocket}
    -
    This represents the interface for a Bluetooth socket - (similar to a TCP client-side {@link java.net.Socket}). This is the - connection point that allows an app to transfer data with another Bluetooth - device via {@link java.io.InputStream} and {@link java.io.OutputStream}.
    -
    {@link android.bluetooth.BluetoothServerSocket}
    - -
    This represents an open server socket that listens for incoming requests - (similar to a TCP server-side {@link java.net.ServerSocket}). - When attempting to connect two Android devices, one device will need to open - a server socket with this class. When a connection is accepted, a new - {@link android.bluetooth.BluetoothSocket} will be returned, - which can be used to manage the connection and transfer data.
    - -
    {@link android.bluetooth.BluetoothClass}
    -
    This represents the Bluetooth class for a device which describes general - characteristics and capabilities of a device. This class and its subclasses - don't provide any actual functionality. The sub-classes are entirely composed - of constants for the device and service class definitions.
    -
    - - -

    Example Procedure

    - -

    For example, here's an pseudo-code procedure for discovering and -connecting a remote device, and transfering data:

    - -
      -
    1. Register a {@link android.content.BroadcastReceiver} that accepts the - {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.
    2. -
    3. Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to - retrieve the Android system's local - {@link android.bluetooth.BluetoothAdapter}.
    4. -
    5. Call {@link android.bluetooth.BluetoothAdapter#startDiscovery() - BluetoothAdapter.startDiscovery()} to scan for local devices. This is where - the BroadcastReceiver comes in; Android now scans for devices and will - broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent - for each remote device discovered. The - {@link android.content.BroadcastReceiver} - you created will receive each Intent.
    6. -
    7. The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent - includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE} - Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice} - object. Extract this from the Intent and call - {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID) - BluetoothDevice.createRfcommSocketToServiceRecord()} - to open a {@link android.bluetooth.BluetoothSocket} with a chosen - remote device.
    8. -
    9. Call {@link android.bluetooth.BluetoothSocket#connect() - BluetoothSocket.connect()} to connect with the remote device.
    10. -
    11. When successfully connected, call - {@link android.bluetooth.BluetoothSocket#getInputStream() - BluetoothSocket.getInputStream()} and/or - {@link android.bluetooth.BluetoothSocket#getOutputStream() - BluetoothSocket.getOutputStream()} to retreive an - {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively, - which are hooked into the socket.
    12. -
    13. Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and - {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer - data.
    14. -
    - - +

    For a detailed guide to using the Bluetooth APIs, see the Bluetooth Dev Guide topic.

    Note: Not all Android devices are guaranteed to have Bluetooth functionality.

    -- GitLab From d73c68f05c8ba7aa65bf6e7b4298a1f19c879814 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 12 Jan 2010 15:18:53 -0800 Subject: [PATCH 0091/1408] android-2.1_r1 snapshot --- .../java/android/bluetooth/BluetoothA2dp.java | 23 +++++++++- .../android/bluetooth/BluetoothAdapter.java | 44 +++++++++++++++---- .../android/bluetooth/BluetoothClass.java | 4 -- .../android/bluetooth/BluetoothDevice.java | 8 ++++ .../android/bluetooth/BluetoothHeadset.java | 11 +++-- .../bluetooth/BluetoothServerSocket.java | 8 +++- .../android/bluetooth/BluetoothSocket.java | 9 ++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + .../android/bluetooth/IBluetoothA2dp.aidl | 1 + 9 files changed, 90 insertions(+), 19 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index e8a69d8c1f7..fda9b81c8f0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -42,7 +42,7 @@ import java.util.HashSet; * * Currently the BluetoothA2dp service runs in the system server and this * proxy object will be immediately bound to the service on construction. - * + * * Currently this class provides methods to connect to A2DP audio sinks. * * @hide @@ -74,9 +74,12 @@ public final class BluetoothA2dp { /** Playing implies connected */ public static final int STATE_PLAYING = 4; + /** Default priority for a2dp devices that we try to auto-connect + * and allow incoming connections */ + public static final int PRIORITY_AUTO_CONNECT = 1000; /** Default priority for a2dp devices that should allow incoming * connections */ - public static final int PRIORITY_AUTO = 100; + public static final int PRIORITY_ON = 100; /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; @@ -196,6 +199,22 @@ public final class BluetoothA2dp { } } + /** Check if any A2DP sink is in Non Disconnected state + * i.e playing, connected, connecting, disconnecting. + * @return a unmodifiable set of connected A2DP sinks, or null on error. + * @hide + */ + public Set getNonDisconnectedSinks() { + if (DBG) log("getNonDisconnectedSinks()"); + try { + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(mService.getNonDisconnectedSinks()))); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + /** Get the state of an A2DP sink * @param device Remote BT device. * @return State code, one of STATE_ diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bd5b07cff64..8eda844380e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -130,13 +130,13 @@ public final class BluetoothAdapter { /** * Activity Action: Show a system activity that requests discoverable mode. - *

    This activity will also request the user to turn on Bluetooth if it + * This activity will also request the user to turn on Bluetooth if it * is not currently enabled. *

    Discoverable mode is equivalent to {@link * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see * this Bluetooth adapter when they perform a discovery. - *

    For privacy, Android is not by default discoverable. - *

    The sender can optionally use extra field {@link + *

    For privacy, Android is not discoverable by default. + *

    The sender of this Intent can optionally use extra field {@link * #EXTRA_DISCOVERABLE_DURATION} to request the duration of * discoverability. Currently the default duration is 120 seconds, and * maximum duration is capped at 300 seconds for each request. @@ -147,7 +147,8 @@ public final class BluetoothAdapter { * {@link android.app.Activity#RESULT_CANCELED} if the user rejected * discoverability or an error has occurred. *

    Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} - * for global notification whenever the scan mode changes. + * for global notification whenever the scan mode changes. For example, an + * application can be notified when the device has ended discoverability. *

    Requires {@link android.Manifest.permission#BLUETOOTH} */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -369,9 +370,17 @@ public final class BluetoothAdapter { } /** - * Turn on the local Bluetooth adapter. + * Turn on the local Bluetooth adapter—do not use without explicit + * user action to turn on Bluetooth. *

    This powers on the underlying Bluetooth hardware, and starts all * Bluetooth system services. + *

    Bluetooth should never be enabled without + * direct user consent. If you want to turn on Bluetooth in order + * to create a wireless connection, you should use the {@link + * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests + * user permission to turn on Bluetooth. The {@link #enable()} method is + * provided only for applications that include a user interface for changing + * system settings, such as a "power manager" app.

    *

    This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns @@ -381,7 +390,8 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned on - * such as Airplane mode, or the adapter is already turned on. - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

    Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission * * @return true to indicate adapter startup has begun, or false on * immediate error @@ -394,9 +404,14 @@ public final class BluetoothAdapter { } /** - * Turn off the local Bluetooth adapter. + * Turn off the local Bluetooth adapter—do not use without explicit + * user action to turn off Bluetooth. *

    This gracefully shuts down all Bluetooth connections, stops Bluetooth * system services, and powers down the underlying Bluetooth hardware. + *

    Bluetooth should never be disbled without + * direct user consent. The {@link #disable()} method is + * provided only for applications that include a user interface for changing + * system settings, such as a "power manager" app.

    *

    This is an asynchronous call: it will return immediately, and * clients should listen for {@link #ACTION_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns @@ -406,7 +421,8 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned off - * such as the adapter already being turned off. - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

    Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission * * @return true to indicate adapter shutdown has begun, or false on * immediate error @@ -549,7 +565,10 @@ public final class BluetoothAdapter { * remote Bluetooth devices should not be attempted while discovery is in * progress, and existing connections will experience limited bandwidth * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing - * discovery. + * discovery. Discovery is not managed by the Activity, + * but is run as a system service, so an application should always call + * {@link BluetoothAdapter#cancelDiscovery()} even if it + * did not directly request a discovery, just to be sure. *

    Device discovery will only find remote devices that are currently * discoverable (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. @@ -567,6 +586,13 @@ public final class BluetoothAdapter { /** * Cancel the current device discovery process. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

    Because discovery is a heavyweight precedure for the Bluetooth + * adapter, this method should always be called before attempting to connect + * to a remote device with {@link + * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by + * the Activity, but is run as a system service, so an application should + * always call cancel discovery even if it did not directly request a + * discovery, just to be sure. * * @return true on success, false on error */ diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index bc067130584..c7fea9e1dad 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -25,10 +25,6 @@ import android.os.Parcelable; * specify the general device type such as a phone, a computer, or * headset, and whether it's capable of services such as audio or telephony. * - *

    The Bluetooth class is useful as a hint to roughly describe a device (for example to - * show an icon in the UI), but does not reliably describe which Bluetooth - * profiles or services are actually supported by a device. - * *

    Every Bluetooth class is composed of zero or more service classes, and * exactly one device class. The device class is further broken down into major * and minor device class components. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6cb9770a610..cf9c58f1228 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -624,6 +624,14 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ + public boolean isBluetoothDock() { + try { + return sService.isBluetoothDock(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 90cff6b8c90..5eb655a2793 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -100,9 +100,14 @@ public final class BluetoothHeadset { /** Connection canceled before completetion. */ public static final int RESULT_CANCELED = 2; - /** Default priority for headsets that should be auto-connected */ - public static final int PRIORITY_AUTO = 100; - /** Default priority for headsets that should not be auto-connected */ + /** Default priority for headsets that for which we will accept + * inconing connections and auto-connect */ + public static final int PRIORITY_AUTO_CONNECT = 1000; + /** Default priority for headsets that for which we will accept + * inconing connections but not auto-connect */ + public static final int PRIORITY_ON = 100; + /** Default priority for headsets that should not be auto-connected + * and not allow incoming connections. */ public static final int PRIORITY_OFF = 0; /** The voice dialer 'works' but the user experience is poor. The voice diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 1b23f6c048d..c9c6c0acd9a 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -42,7 +42,11 @@ import java.io.IOException; * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call * {@link #accept()} to listen for incoming connection requests. This call * will block until a connection is established, at which point, it will return - * a {@link BluetoothSocket} to manage the connection. + * a {@link BluetoothSocket} to manage the connection. Once the {@link + * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on + * the {@link BluetoothServerSocket} when it's no longer needed for accepting + * connections. Closing the {@link BluetoothServerSocket} will not + * close the returned {@link BluetoothSocket}. * *

    {@link BluetoothServerSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing @@ -105,6 +109,8 @@ public final class BluetoothServerSocket implements Closeable { * Immediately close this socket, and release all associated resources. *

    Causes blocked calls on this socket in other threads to immediately * throw an IOException. + *

    Closing the {@link BluetoothServerSocket} will not + * close any {@link BluetoothSocket} received from {@link #accept()}. */ public void close() throws IOException { synchronized (this) { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index dbcc758574b..ad033999e0d 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -180,6 +180,15 @@ public final class BluetoothSocket implements Closeable { *

    This method will block until a connection is made or the connection * fails. If this method returns without an exception then this socket * is now connected. + *

    Creating new connections to + * remote Bluetooth devices should not be attempted while device discovery + * is in progress. Device discovery is a heavyweight procedure on the + * Bluetooth adapter and will significantly slow a device connection. + * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing + * discovery. Discovery is not managed by the Activity, + * but is run as a system service, so an application should always call + * {@link BluetoothAdapter#cancelDiscovery()} even if it + * did not directly request a discovery, just to be sure. *

    {@link #close} can be used to abort this call from another thread. * @throws IOException on error, for example connection failure */ diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 7e752af428f..08687795d93 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -64,6 +64,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + boolean isBluetoothDock(in String address); int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); void removeServiceRecord(int handle); diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 002cf4efff6..168fe3b252d 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -29,6 +29,7 @@ interface IBluetoothA2dp { boolean suspendSink(in BluetoothDevice device); boolean resumeSink(in BluetoothDevice device); BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports + BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports int getSinkState(in BluetoothDevice device); boolean setSinkPriority(in BluetoothDevice device, int priority); int getSinkPriority(in BluetoothDevice device); -- GitLab From a81ea3b02fe644db8c8fe197c1e43007f19ac75d Mon Sep 17 00:00:00 2001 From: mah Date: Mon, 8 Feb 2010 17:50:14 -0800 Subject: [PATCH 0092/1408] Enable bluetooth voice dialing in BluetoothHeadset.java. Bug: 243220 This just sets DISABLE_BT_VOICE_DIALING to false. A different checking to packages/apps/VoiceDialer actually makes it work properly. --- framework/java/android/bluetooth/BluetoothHeadset.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index b7929658156..251813eed6f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -112,11 +112,9 @@ public final class BluetoothHeadset { /** Default priority when not set or when the device is unpaired */ public static final int PRIORITY_UNDEFINED = -1; - /** The voice dialer 'works' but the user experience is poor. The voice - * recognizer has trouble dealing with the 8kHz SCO signal, and it still - * requires visual confirmation. Disable for cupcake. - */ - public static final boolean DISABLE_BT_VOICE_DIALING = true; + /** Set this to true to prevent the bluetooth headset from + * activating the VoiceDialer. */ + public static final boolean DISABLE_BT_VOICE_DIALING = false; /** * An interface for notifying BluetoothHeadset IPC clients when they have -- GitLab From b5faac489458fa666abbe8de41eb3ecb06a8fb90 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 24 Feb 2010 11:19:10 -0800 Subject: [PATCH 0093/1408] Add documentation to help developers make an RFCOMM conection to a Bluetooth serial board. They all use a well-known UUID that is not really explained anywhere official, and this always trips developers up. Change-Id: I53bda44b580f472b1ff1ad196b25485b68f4b5d5 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cf9c58f1228..e77e76f79b2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -664,6 +664,10 @@ public final class BluetoothDevice implements Parcelable { * determine which channel to connect to. *

    The remote device will be authenticated and communication on this * socket will be encrypted. + *

    Hint: If you are connecting to a Bluetooth serial board then try + * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. + * However if you are connecting to an Android peer then please generate + * your own unique UUID. *

    Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel -- GitLab From e3a83f7603a68b027af54a29c51ad839f38f7aac Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 11 Mar 2010 18:20:12 -0800 Subject: [PATCH 0094/1408] Add correct copyright headers to multiple files Format for the list of changes shows the origin commit reference followed by the file name. 33931-p9 awt/org/apache/harmony/awt/gl/font/AndroidGlyphVector.java 33931-p9 awt/org/apache/harmony/awt/gl/image/PngDecoderJava.java 133776-p9 core/java/android/app/IntentService.java 127013-p9 core/java/android/appwidget/AppWidgetHost.java 27863-p9 core/java/android/bluetooth/BluetoothAudioGateway.java 60765-p9 core/java/android/content/SyncResult.java 43920-p9 core/java/android/content/pm/ActivityInfo.java 43920-p9 core/java/android/content/pm/ApplicationInfo.java 43920-p9 core/java/android/content/pm/InstrumentationInfo.java 43920-p9 core/java/android/content/pm/PackageInfo.java 44103-p9 core/java/android/content/pm/PackageItemInfo.java 68960-p9 core/java/android/content/pm/PackageStats.java 43920-p9 core/java/android/content/pm/ResolveInfo.java 43920-p9 core/java/android/content/pm/ServiceInfo.java 60641-p9 core/java/android/content/res/Configuration.java 60734-p9 core/java/android/content/res/TypedArray.java 137672-p9 core/java/android/inputmethodservice/ExtractButton.java 123112-p9 core/java/android/inputmethodservice/ExtractEditText.java 119291-p9 core/java/android/inputmethodservice/IInputMethodSessionWrapper.java 112946-p9 core/java/android/inputmethodservice/IInputMethodWrapper.java 115078-p9 core/java/android/os/BatteryStats.java 124790-p9 core/java/android/text/style/UpdateAppearance.java 45083-p9 core/java/android/view/RawInputEvent.java 101491-p9 core/java/android/view/inputmethod/EditorInfo.java 114701-p9 core/java/android/view/inputmethod/ExtractedText.java 123112-p9 core/java/android/view/inputmethod/ExtractedTextRequest.java 119291-p9 core/java/com/android/internal/os/HandlerCaller.java 129279-p9 core/java/com/android/internal/os/PkgUsageStats.java 114701-p9 core/java/com/android/internal/view/IInputConnectionWrapper.java 114701-p9 core/java/com/android/internal/view/InputConnectionWrapper.java 84364-p9 opengl/java/android/opengl/EGLLogWrapper.java 11355-p9 opengl/tools/glgen/src/CFunc.java 11355-p9 opengl/tools/glgen/src/CType.java 11355-p9 opengl/tools/glgen/src/CodeEmitter.java 11355-p9 opengl/tools/glgen/src/GenerateGL.java 11355-p9 opengl/tools/glgen/src/JFunc.java 11355-p9 opengl/tools/glgen/src/JType.java 11355-p9 opengl/tools/glgen/src/JniCodeEmitter.java 11355-p9 opengl/tools/glgen/src/ParameterChecker.java 57236-p9 services/java/com/android/server/status/AnimatedImageView.java 66754-p9 services/java/com/android/server/status/CloseDragHandle.java 57188-p9 services/java/com/android/server/status/DateView.java 46928-p9 services/java/com/android/server/status/ExpandedView.java 70590-p9 services/java/com/android/server/status/FixedSizeDrawable.java 45968-p9 services/java/com/android/server/status/IconData.java 57470-p9 services/java/com/android/server/status/IconMerger.java 82719-p9 services/java/com/android/server/status/LatestItemView.java 45968-p9 services/java/com/android/server/status/NotificationData.java 66754-p9 services/java/com/android/server/status/NotificationLinearLayout.java 57458-p9 services/java/com/android/server/status/NotificationViewList.java 45968-p9 services/java/com/android/server/status/StatusBarException.java 45968-p9 services/java/com/android/server/status/StatusBarIcon.java 46130-p9 services/java/com/android/server/status/StatusBarNotification.java 45968-p9 services/java/com/android/server/status/StatusBarView.java 46199-p9 services/java/com/android/server/status/Ticker.java 62286-p9 services/java/com/android/server/status/TickerView.java 57188-p9 services/java/com/android/server/status/TrackingView.java 86041-p9 telephony/java/android/telephony/PhoneStateListener.java 87020-p9 telephony/java/com/android/internal/telephony/TelephonyIntents.java 136269-p9 telephony/java/com/android/internal/telephony/gsm/SpnOverride.java 34409-p9 tests/FrameworkTest/src/com/android/frameworktest/FrameworkTestApplication.java 55717-p9 tests/FrameworkTest/src/com/android/frameworktest/performance/InvalidateCycle.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java 129372-p9 tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java 129372-p9 tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java 129372-p9 tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java 129372-p9 tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java 127341-p9 tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java 129347-p9 tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java 129372-p9 tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java 128994-p9 tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java 25959-p9 tests/framework-tests/src/android/test/FrameworkTests.java 46162-p9 tests/framework-tests/src/com/android/internal/http/multipart/MultipartTest.java 77101-p9 tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java 9788976b1465ce982b5ae7c741345edd0ecd9322 core/java/android/accounts/AuthenticatorDescription.java 53332883543868fb83e111a07306368b7772b340 core/java/android/app/UiModeManager.java 93e7e22ec91dbc641d10ca6d70423e1357a95bba core/java/android/app/FullBackupAgent.java 328c0e7986aa6bb7752ec6de3da9c999920bb55f core/java/android/content/CursorEntityIterator.java 307da1a46b4c9b711bafe8fbaaa6b98e8868c18e core/java/android/content/SyncQueue.java 307da1a46b4c9b711bafe8fbaaa6b98e8868c18e core/java/android/content/SyncOperation.java eb034652c2037a47ebfd99779e8383bb8bb528af core/java/android/content/pm/LabeledIntent.java 49237345d83e62fdb9eb8d50b13ad086636a04fa core/java/android/content/pm/FeatureInfo.java a2b6c3775ed6b8924232d6a01bae4a19740a15f8 core/java/android/content/pm/PackageInfoLite.java 3ecd5f437580e49d80beecd29489d5fb1f7a7db0 core/java/android/content/pm/RegisteredServicesCacheListener.java 5ebbb4a6b3e16f711735ae0615b9a9ea64faad38 core/java/android/content/pm/XmlSerializerAndParser.java c4516a7b62de525e3d6d5e76851bdfaf12c11f05 core/java/android/database/sqlite/SQLiteTransactionListener.java 9bbc21a773cbdfbef2876a75c32bda5839647751 core/java/com/android/internal/backup/LocalTransport.java 21f1bd17b2dfe361acbb28453b3f3b1a110932fa core/java/com/android/internal/content/PackageMonitor.java 4c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3 core/java/com/android/internal/view/BaseSurfaceHolder.java 4c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3 core/java/com/android/internal/view/BaseIWindow.java e540833fdff4d58e37c9ba859388e24e2945ed45 core/java/com/android/internal/os/SamplingProfilerIntegration.java 192ab903887bbb8e7c7b6da5c581573850e30f46 core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java 1619367ab823150fa8856d419abe02ceb75886f1 media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java 27f8002e591b5c579f75b2580183b5d1c4219cd4 opengl/tools/glgen/stubs/gles11/glGetString.java 560814f6b11abe83ff0c4ed18cac015c276b3181 opengl/tools/glgen/stubs/gles11/glGetProgramInfoLog.java 560814f6b11abe83ff0c4ed18cac015c276b3181 opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.java 560814f6b11abe83ff0c4ed18cac015c276b3181 opengl/tools/glgen/stubs/gles11/glShaderSource.java 1c4907ee77392afb768c2f088e0dedbe4239f6fb opengl/tools/glgen/src/GenerateGLES.java 1c4907ee77392afb768c2f088e0dedbe4239f6fb opengl/tools/glgen/src/Jsr239CodeEmitter.java 1c4907ee77392afb768c2f088e0dedbe4239f6fb opengl/tools/glgen/src/GLESCodeEmitter.java 69e21f5f6e0d04539cd92848ea009dd615d88c2c opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java c028be4f3b8c7476b46859f66c3f33d528adf181 packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java 7c6efa13f129dbae5319f0981a430d4662f43354 tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java 7c6efa13f129dbae5319f0981a430d4662f43354 tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestActivity.java 7c6efa13f129dbae5319f0981a430d4662f43354 tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestRunner.java df8a3f31d871db25e952972c2eb346a71186e9e3 tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java cfaef699e1dfb3a75d5b51f3b15816f13670fd51 tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java cfaef699e1dfb3a75d5b51f3b15816f13670fd51 tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java cfaef699e1dfb3a75d5b51f3b15816f13670fd51 tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java Copyright header moved to top in following file: core/tests/coretests/src/android/widget/ListViewTest.java Change-Id: I3c3198be5a0ba36e18679ed834170432bf0b8418 --- .../android/bluetooth/BluetoothAudioGateway.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java index abd7723c57c..bc3206042e1 100644 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 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 java.lang.Thread; -- GitLab From 2e66fa275299219292f68bfd52a8244e9dde2e2b Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 17 Mar 2010 14:59:27 -0700 Subject: [PATCH 0095/1408] Added a configuration option indicating if the platform supports use of bluetooth SCO for off call use cases. Added method to BluetoothHeadset API to check if voice dialer over bluetooth is supported. Replaces direct use of DISABLE_BT_VOICE_DIALING and takes platform capabilities into account. Needed for issue 2416481: Support Voice Dialer over BT SCO. Change-Id: I237d5a984dbc4cbc88e98b85d710a6c87ba1b315 --- .../java/android/bluetooth/BluetoothHeadset.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 251813eed6f..fff75ca9aff 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -112,10 +112,6 @@ public final class BluetoothHeadset { /** Default priority when not set or when the device is unpaired */ public static final int PRIORITY_UNDEFINED = -1; - /** Set this to true to prevent the bluetooth headset from - * activating the VoiceDialer. */ - public static final boolean DISABLE_BT_VOICE_DIALING = false; - /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. @@ -384,6 +380,16 @@ public final class BluetoothHeadset { return -1; } + /** + * Indicates if current platform supports voice dialing over bluetooth SCO. + * @return true if voice dialing over bluetooth is supported, false otherwise. + * @hide + */ + public static boolean isBluetoothVoiceDialingEnabled(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_bluetooth_sco_off_call); + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); -- GitLab From 7207fc43878397c53f52b05030bf5a0af318278a Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Thu, 18 Mar 2010 18:08:02 -0700 Subject: [PATCH 0096/1408] Add the blacklist for the SCO connections Change-Id: Ibfdfb21fb2f27d01d2b8bff48041398aa4308353 Signed-off-by: Eric Olsen --- framework/java/android/bluetooth/ScoSocket.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java index 116310ad9d7..b65a99a048e 100644 --- a/framework/java/android/bluetooth/ScoSocket.java +++ b/framework/java/android/bluetooth/ScoSocket.java @@ -86,14 +86,14 @@ public class ScoSocket { /** Connect this SCO socket to the given BT address. * Does not block. */ - public synchronized boolean connect(String address) { + public synchronized boolean connect(String address, String name) { if (DBG) log("connect() " + this); if (mState != STATE_READY) { if (DBG) log("connect(): Bad state"); return false; } acquireWakeLock(); - if (connectNative(address)) { + if (connectNative(address, name)) { mState = STATE_CONNECTING; return true; } else { @@ -102,7 +102,7 @@ public class ScoSocket { return false; } } - private native boolean connectNative(String address); + private native boolean connectNative(String address, String name); /** Accept incoming SCO connections. * Does not block. -- GitLab From f48cda5c0223335cacf8ad1352b482727f57b1f2 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 2 Apr 2010 14:44:43 -0700 Subject: [PATCH 0097/1408] Add an extra for the Disconnect Headset State. This extra tells as to who initiated the disconnection. Bug:2568119 Change-Id: Iea706ad69f79ae0257604dfa84d49126fb851cca --- .../java/android/bluetooth/BluetoothHeadset.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index fff75ca9aff..95e61b6f6ac 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -73,6 +73,17 @@ public final class BluetoothHeadset { public static final String EXTRA_AUDIO_STATE = "android.bluetooth.headset.extra.AUDIO_STATE"; + /** Extra to be used with the Headset State change intent. + * This will be used only when Headset state changes to + * {@link #STATE_DISCONNECTED} from any previous state. + * This extra field is optional and will be used when + * we have deterministic information regarding whether + * the disconnect was initiated by the remote device or + * by the local adapter. + */ + public static final String EXTRA_DISCONNECT_INITIATOR = + "android.bluetooth.headset.extra.DISCONNECT_INITIATOR"; + /** * TODO(API release): Consider incorporating as new state in * HEADSET_STATE_CHANGED @@ -100,6 +111,11 @@ public final class BluetoothHeadset { /** Connection canceled before completetion. */ public static final int RESULT_CANCELED = 2; + /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */ + public static final int REMOTE_DISCONNECT = 0; + public static final int LOCAL_DISCONNECT = 1; + + /** Default priority for headsets that for which we will accept * inconing connections and auto-connect */ public static final int PRIORITY_AUTO_CONNECT = 1000; -- GitLab From 9ad892c51e2e37a5506416bb45d43e191003b3fa Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 18 May 2010 14:36:48 -0700 Subject: [PATCH 0098/1408] Removing STOPSHIP logs. Bug: 2694602 Change-Id: Id56e1ddcf5ea76de32238cd6761f2caf053f1fa1 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 42d87f4118c..8eda844380e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -63,7 +63,7 @@ import java.util.UUID; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - private static final boolean DBG = true; //STOPSHIP: Remove excess logging + private static final boolean DBG = false; /** * Sentinel error value for this class. Guaranteed to not equal any other -- GitLab From b84bbd96842b945e89d6f3f1b8a5ec3cd8748364 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 2 Jun 2010 14:36:14 -0700 Subject: [PATCH 0099/1408] Add a new state machine for handling the incoming / outgoing profile connections. Change-Id: I5fc9170b5e24c4a52a6f2ef4ca7a8bac65271941 --- .../android/bluetooth/BluetoothHeadset.java | 87 ++- .../BluetoothProfileConnectionState.java | 641 ++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 + .../android/bluetooth/IBluetoothA2dp.aidl | 3 + .../android/bluetooth/IBluetoothHeadset.aidl | 6 + 5 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothProfileConnectionState.java diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 95e61b6f6ac..9d633fe6772 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -395,7 +395,6 @@ public final class BluetoothHeadset { } return -1; } - /** * Indicates if current platform supports voice dialing over bluetooth SCO. * @return true if voice dialing over bluetooth is supported, false otherwise. @@ -406,6 +405,92 @@ public final class BluetoothHeadset { com.android.internal.R.bool.config_bluetooth_sco_off_call); } + /** + * Cancel the outgoing connection. + * @hide + */ + public boolean cancelConnectThread() { + if (DBG) log("cancelConnectThread"); + if (mService != null) { + try { + return mService.cancelConnectThread(); + } 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; + } + + /** + * Accept the incoming connection. + * @hide + */ + public boolean acceptIncomingConnect(BluetoothDevice device) { + if (DBG) log("acceptIncomingConnect"); + if (mService != null) { + try { + return mService.acceptIncomingConnect(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; + } + + /** + * Create the connect thread the incoming connection. + * @hide + */ + public boolean createIncomingConnect(BluetoothDevice device) { + if (DBG) log("createIncomingConnect"); + if (mService != null) { + try { + return mService.createIncomingConnect(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; + } + + /** + * Connect to a Bluetooth Headset. + * Note: This is an internal function and shouldn't be exposed + * @hide + */ + public boolean connectHeadsetInternal(BluetoothDevice device) { + if (DBG) log("connectHeadsetInternal"); + if (mService != null) { + try { + return mService.connectHeadsetInternal(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; + } + + /** + * Disconnect a Bluetooth Headset. + * Note: This is an internal function and shouldn't be exposed + * @hide + */ + public boolean disconnectHeadsetInternal(BluetoothDevice device) { + if (DBG) log("disconnectHeadsetInternal"); + if (mService != null) { + try { + return mService.disconnectHeadsetInternal(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; + } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothProfileConnectionState.java b/framework/java/android/bluetooth/BluetoothProfileConnectionState.java new file mode 100644 index 00000000000..a58b85835d2 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothProfileConnectionState.java @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2009 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.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Message; +import android.server.BluetoothA2dpService; +import android.server.BluetoothService; +import android.util.Log; + +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; + +/** + * This class is the Profile connection state machine associated with a remote + * device. When the device bonds an instance of this class is created. + * This tracks incoming and outgoing connections of all the profiles. Incoming + * connections are preferred over outgoing connections and HFP preferred over + * A2DP. When the device is unbonded, the instance is removed. + * + * States: + * {@link BondedDevice}: This state represents a bonded device. When in this + * state none of the profiles are in transition states. + * + * {@link OutgoingHandsfree}: Handsfree profile connection is in a transition + * state because of a outgoing Connect or Disconnect. + * + * {@link IncomingHandsfree}: Handsfree profile connection is in a transition + * state because of a incoming Connect or Disconnect. + * + * {@link IncomingA2dp}: A2dp profile connection is in a transition + * state because of a incoming Connect or Disconnect. + * + * {@link OutgoingA2dp}: A2dp profile connection is in a transition + * state because of a outgoing Connect or Disconnect. + * + * Todo(): Write tests for this class, when the Android Mock support is completed. + * @hide + */ +public final class BluetoothProfileConnectionState extends HierarchicalStateMachine { + private static final String TAG = "BluetoothProfileConnectionState"; + private static final boolean DBG = true; //STOPSHIP - Change to false + + public static final int CONNECT_HFP_OUTGOING = 1; + public static final int CONNECT_HFP_INCOMING = 2; + public static final int CONNECT_A2DP_OUTGOING = 3; + public static final int CONNECT_A2DP_INCOMING = 4; + + public static final int DISCONNECT_HFP_OUTGOING = 5; + private static final int DISCONNECT_HFP_INCOMING = 6; + public static final int DISCONNECT_A2DP_OUTGOING = 7; + public static final int DISCONNECT_A2DP_INCOMING = 8; + + public static final int UNPAIR = 9; + public static final int AUTO_CONNECT_PROFILES = 10; + public static final int TRANSITION_TO_STABLE = 11; + + private static final int AUTO_CONNECT_DELAY = 8000; // 8 secs + + private BondedDevice mBondedDevice = new BondedDevice(); + private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); + private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree(); + private IncomingA2dp mIncomingA2dp = new IncomingA2dp(); + private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp(); + + private Context mContext; + private BluetoothService mService; + private BluetoothA2dpService mA2dpService; + private BluetoothHeadset mHeadsetService; + private boolean mHeadsetServiceConnected; + + private BluetoothDevice mDevice; + private int mHeadsetState; + private int mA2dpState; + + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (!device.equals(mDevice)) return; + + if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0); + int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0); + int initiator = intent.getIntExtra( + BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR, + BluetoothHeadset.LOCAL_DISCONNECT); + mHeadsetState = newState; + if (newState == BluetoothHeadset.STATE_DISCONNECTED && + initiator == BluetoothHeadset.REMOTE_DISCONNECT) { + sendMessage(DISCONNECT_HFP_INCOMING); + } + if (newState == BluetoothHeadset.STATE_CONNECTED || + newState == BluetoothHeadset.STATE_DISCONNECTED) { + sendMessage(TRANSITION_TO_STABLE); + } + } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); + int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0); + mA2dpState = newState; + if ((oldState == BluetoothA2dp.STATE_CONNECTED || + oldState == BluetoothA2dp.STATE_PLAYING) && + newState == BluetoothA2dp.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_A2DP_INCOMING); + } + if (newState == BluetoothA2dp.STATE_CONNECTED || + newState == BluetoothA2dp.STATE_DISCONNECTED) { + sendMessage(TRANSITION_TO_STABLE); + } + } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { + if (!getCurrentState().equals(mBondedDevice)) { + Log.e(TAG, "State is: " + getCurrentState()); + return; + } + Message msg = new Message(); + msg.what = AUTO_CONNECT_PROFILES; + sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } + } + }; + + public BluetoothProfileConnectionState(Context context, String address, + BluetoothService service, BluetoothA2dpService a2dpService) { + super(address); + mContext = context; + mDevice = new BluetoothDevice(address); + mService = service; + mA2dpService = a2dpService; + + addState(mBondedDevice); + addState(mOutgoingHandsfree); + addState(mIncomingHandsfree); + addState(mIncomingA2dp); + addState(mOutgoingA2dp); + setInitialState(mBondedDevice); + + IntentFilter filter = new IntentFilter(); + // Fine-grained state broadcasts + filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); + filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + + mContext.registerReceiver(mBroadcastReceiver, filter); + + HeadsetServiceListener l = new HeadsetServiceListener(); + } + + private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener { + public HeadsetServiceListener() { + mHeadsetService = new BluetoothHeadset(mContext, this); + } + public void onServiceConnected() { + synchronized(BluetoothProfileConnectionState.this) { + mHeadsetServiceConnected = true; + } + } + public void onServiceDisconnected() { + synchronized(BluetoothProfileConnectionState.this) { + mHeadsetServiceConnected = false; + } + } + } + + private class BondedDevice extends HierarchicalState { + @Override + protected void enter() { + log("Entering ACL Connected state with: " + getCurrentMessage().what); + Message m = new Message(); + m.copyFrom(getCurrentMessage()); + sendMessageAtFrontOfQueue(m); + } + @Override + protected boolean processMessage(Message message) { + log("ACL Connected State -> Processing Message: " + message.what); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + case DISCONNECT_HFP_OUTGOING: + transitionTo(mOutgoingHandsfree); + break; + case CONNECT_HFP_INCOMING: + transitionTo(mIncomingHandsfree); + break; + case DISCONNECT_HFP_INCOMING: + transitionTo(mIncomingHandsfree); + break; + case CONNECT_A2DP_OUTGOING: + case DISCONNECT_A2DP_OUTGOING: + transitionTo(mOutgoingA2dp); + break; + case CONNECT_A2DP_INCOMING: + case DISCONNECT_A2DP_INCOMING: + transitionTo(mIncomingA2dp); + break; + case UNPAIR: + if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HFP_OUTGOING); + deferMessage(message); + break; + } else if (mA2dpState != BluetoothA2dp.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_A2DP_OUTGOING); + deferMessage(message); + break; + } + processCommand(UNPAIR); + break; + case AUTO_CONNECT_PROFILES: + if (!mHeadsetServiceConnected) { + deferMessage(message); + } else { + if (mHeadsetService.getPriority(mDevice) == + BluetoothHeadset.PRIORITY_AUTO_CONNECT) { + mHeadsetService.connectHeadset(mDevice); + } + if (mA2dpService != null && + mA2dpService.getSinkPriority(mDevice) == + BluetoothA2dp.PRIORITY_AUTO_CONNECT) { + mA2dpService.connectSink(mDevice); + } + } + break; + case TRANSITION_TO_STABLE: + // ignore. + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class OutgoingHandsfree extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering OutgoingHandsfree state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HFP_OUTGOING && + mCommand != DISCONNECT_HFP_OUTGOING) { + Log.e(TAG, "Error: OutgoingHandsfree state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("OutgoingHandsfree State -> Processing Message: " + message.what); + Message deferMsg = new Message(); + int command = message.what; + switch(command) { + case CONNECT_HFP_OUTGOING: + if (command != mCommand) { + // Disconnect followed by a connect - defer + deferMessage(message); + } + break; + case CONNECT_HFP_INCOMING: + if (mCommand == CONNECT_HFP_OUTGOING) { + // Cancel outgoing connect, accept incoming + cancelCommand(CONNECT_HFP_OUTGOING); + transitionTo(mIncomingHandsfree); + } else { + // We have done the disconnect but we are not + // sure which state we are in at this point. + deferMessage(message); + } + break; + case CONNECT_A2DP_INCOMING: + // accept incoming A2DP, retry HFP_OUTGOING + transitionTo(mIncomingA2dp); + + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case CONNECT_A2DP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_OUTGOING: + if (mCommand == CONNECT_HFP_OUTGOING) { + // Cancel outgoing connect + cancelCommand(CONNECT_HFP_OUTGOING); + processCommand(DISCONNECT_HFP_OUTGOING); + } + // else ignore + break; + case DISCONNECT_HFP_INCOMING: + // When this happens the socket would be closed and the headset + // state moved to DISCONNECTED, cancel the outgoing thread. + // if it still is in CONNECTING state + cancelCommand(CONNECT_HFP_OUTGOING); + break; + case DISCONNECT_A2DP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_A2DP_INCOMING: + // Bluez will handle the disconnect. If because of this the outgoing + // handsfree connection has failed, then retry. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class IncomingHandsfree extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering IncomingHandsfree state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HFP_INCOMING && + mCommand != DISCONNECT_HFP_INCOMING) { + Log.e(TAG, "Error: IncomingHandsfree state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("IncomingHandsfree State -> Processing Message: " + message.what); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + deferMessage(message); + break; + case CONNECT_HFP_INCOMING: + // Ignore + Log.e(TAG, "Error: Incoming connection with a pending incoming connection"); + break; + case CONNECT_A2DP_INCOMING: + // Serialize the commands. + deferMessage(message); + break; + case CONNECT_A2DP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_OUTGOING: + // We don't know at what state we are in the incoming HFP connection state. + // We can be changing from DISCONNECTED to CONNECTING, or + // from CONNECTING to CONNECTED, so serializing this command is + // the safest option. + deferMessage(message); + break; + case DISCONNECT_HFP_INCOMING: + // Nothing to do here, we will already be DISCONNECTED + // by this point. + break; + case DISCONNECT_A2DP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_A2DP_INCOMING: + // Bluez handles incoming A2DP disconnect. + // If this causes incoming HFP to fail, it is more of a headset problem + // since both connections are incoming ones. + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class OutgoingA2dp extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering OutgoingA2dp state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_A2DP_OUTGOING && + mCommand != DISCONNECT_A2DP_OUTGOING) { + Log.e(TAG, "Error: OutgoingA2DP state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("OutgoingA2dp State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + processCommand(CONNECT_HFP_OUTGOING); + + // Don't cancel A2DP outgoing as there is no guarantee it + // will get canceled. + // It might already be connected but we might not have got the + // A2DP_SINK_STATE_CHANGE. Hence, no point disconnecting here. + // The worst case, the connection will fail, retry. + // The same applies to Disconnecting an A2DP connection. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case CONNECT_HFP_INCOMING: + processCommand(CONNECT_HFP_INCOMING); + + // Don't cancel A2DP outgoing as there is no guarantee + // it will get canceled. + // The worst case, the connection will fail, retry. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case CONNECT_A2DP_INCOMING: + // Bluez will take care of conflicts between incoming and outgoing + // connections. + transitionTo(mIncomingA2dp); + break; + case CONNECT_A2DP_OUTGOING: + // Ignore + break; + case DISCONNECT_HFP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_INCOMING: + // At this point, we are already disconnected + // with HFP. Sometimes A2DP connection can + // fail due to the disconnection of HFP. So add a retry + // for the A2DP. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_A2DP_OUTGOING: + processCommand(DISCONNECT_A2DP_OUTGOING); + break; + case DISCONNECT_A2DP_INCOMING: + // Ignore, will be handled by Bluez + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class IncomingA2dp extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering IncomingA2dp state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_A2DP_INCOMING && + mCommand != DISCONNECT_A2DP_INCOMING) { + Log.e(TAG, "Error: IncomingA2DP state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("IncomingA2dp State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + deferMessage(message); + break; + case CONNECT_HFP_INCOMING: + // Shouldn't happen, but serialize the commands. + deferMessage(message); + break; + case CONNECT_A2DP_INCOMING: + // ignore + break; + case CONNECT_A2DP_OUTGOING: + // Defer message and retry + deferMessage(message); + break; + case DISCONNECT_HFP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_INCOMING: + // Shouldn't happen but if does, we can handle it. + // Depends if the headset can handle it. + // Incoming A2DP will be handled by Bluez, Disconnect HFP + // the socket would have already been closed. + // ignore + break; + case DISCONNECT_A2DP_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_A2DP_INCOMING: + // Ignore, will be handled by Bluez + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + + + synchronized void cancelCommand(int command) { + if (command == CONNECT_HFP_OUTGOING ) { + // Cancel the outgoing thread. + if (mHeadsetServiceConnected) { + mHeadsetService.cancelConnectThread(); + } + // HeadsetService is down. Phone process most likely crashed. + // The thread would have got killed. + } + } + + synchronized void deferHeadsetMessage(int command) { + Message msg = new Message(); + msg.what = command; + deferMessage(msg); + } + + synchronized boolean processCommand(int command) { + log("Processing command:" + command); + switch(command) { + case CONNECT_HFP_OUTGOING: + if (mHeadsetService != null) { + return mHeadsetService.connectHeadsetInternal(mDevice); + } + break; + case CONNECT_HFP_INCOMING: + if (!mHeadsetServiceConnected) { + deferHeadsetMessage(command); + } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { + return mHeadsetService.acceptIncomingConnect(mDevice); + } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + return mHeadsetService.createIncomingConnect(mDevice); + } + break; + case CONNECT_A2DP_OUTGOING: + if (mA2dpService != null) { + return mA2dpService.connectSinkInternal(mDevice); + } + break; + case CONNECT_A2DP_INCOMING: + // ignore, Bluez takes care + return true; + case DISCONNECT_HFP_OUTGOING: + if (!mHeadsetServiceConnected) { + deferHeadsetMessage(command); + } else { + if (mHeadsetService.getPriority(mDevice) == + BluetoothHeadset.PRIORITY_AUTO_CONNECT) { + mHeadsetService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); + } + return mHeadsetService.disconnectHeadsetInternal(mDevice); + } + break; + case DISCONNECT_HFP_INCOMING: + // ignore + return true; + case DISCONNECT_A2DP_INCOMING: + // ignore + return true; + case DISCONNECT_A2DP_OUTGOING: + if (mA2dpService != null) { + if (mA2dpService.getSinkPriority(mDevice) == + BluetoothA2dp.PRIORITY_AUTO_CONNECT) { + mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON); + } + return mA2dpService.disconnectSinkInternal(mDevice); + } + break; + case UNPAIR: + return mService.removeBondInternal(mDevice.getAddress()); + default: + Log.e(TAG, "Error: Unknown Command"); + } + return false; + } + + private void log(String message) { + if (DBG) { + Log.i(TAG, "Device:" + mDevice + " Message:" + message); + } + } +} diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 08687795d93..ea71034f5b4 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -68,4 +68,8 @@ interface IBluetooth int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); void removeServiceRecord(int handle); + + boolean connectHeadset(String address); + boolean disconnectHeadset(String address); + boolean notifyIncomingConnection(String address); } diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 168fe3b252d..40f10583b63 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -33,4 +33,7 @@ interface IBluetoothA2dp { int getSinkState(in BluetoothDevice device); boolean setSinkPriority(in BluetoothDevice device, int priority); int getSinkPriority(in BluetoothDevice device); + + boolean connectSinkInternal(in BluetoothDevice device); + boolean disconnectSinkInternal(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 6cccd506e26..57a9c066130 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -34,4 +34,10 @@ interface IBluetoothHeadset { boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); int getBatteryUsageHint(); + + boolean createIncomingConnect(in BluetoothDevice device); + boolean acceptIncomingConnect(in BluetoothDevice device); + boolean cancelConnectThread(); + boolean connectHeadsetInternal(in BluetoothDevice device); + boolean disconnectHeadsetInternal(in BluetoothDevice device); } -- GitLab From 4802a63ede5cd52ad524db22b73884d45b612b2e Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 2 Jun 2010 12:33:53 -0700 Subject: [PATCH 0100/1408] Pass BluetoothDevice to the Bluetooth Headset calls. This is to support multiple headsets at the same time, atleast at the framework level. Change-Id: I91d05c6c2828c9a09d00806d5e79f1e9c9c7cf84 --- framework/java/android/bluetooth/BluetoothHeadset.java | 8 ++++---- framework/java/android/bluetooth/IBluetoothHeadset.aidl | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 9d633fe6772..4a91a8c9072 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -189,11 +189,11 @@ public final class BluetoothHeadset { * @return One of the STATE_ return codes, or STATE_ERROR if this proxy * object is currently not connected to the Headset service. */ - public int getState() { + public int getState(BluetoothDevice device) { if (DBG) log("getState()"); if (mService != null) { try { - return mService.getState(); + return mService.getState(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -271,11 +271,11 @@ public final class BluetoothHeadset { * be made asynchornous. Returns false if this proxy object is * not currently connected to the Headset service. */ - public boolean disconnectHeadset() { + public boolean disconnectHeadset(BluetoothDevice device) { if (DBG) log("disconnectHeadset()"); if (mService != null) { try { - mService.disconnectHeadset(); + mService.disconnectHeadset(device); return true; } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 57a9c066130..d96f0ca0de8 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -24,10 +24,10 @@ import android.bluetooth.BluetoothDevice; * {@hide} */ interface IBluetoothHeadset { - int getState(); + int getState(in BluetoothDevice device); BluetoothDevice getCurrentHeadset(); boolean connectHeadset(in BluetoothDevice device); - void disconnectHeadset(); + void disconnectHeadset(in BluetoothDevice device); boolean isConnected(in BluetoothDevice device); boolean startVoiceRecognition(); boolean stopVoiceRecognition(); -- GitLab From 8f1c611c19178bc795ed399804bc3b34805b1b7c Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 2 Jun 2010 20:30:34 -0700 Subject: [PATCH 0101/1408] Serialize all commands for a particular profile. Change-Id: I843ea9ab0bb2372c8316e99e8c083a9939ad774a --- ....java => BluetoothDeviceProfileState.java} | 46 ++++-- .../bluetooth/BluetoothProfileState.java | 144 ++++++++++++++++++ 2 files changed, 179 insertions(+), 11 deletions(-) rename framework/java/android/bluetooth/{BluetoothProfileConnectionState.java => BluetoothDeviceProfileState.java} (93%) create mode 100644 framework/java/android/bluetooth/BluetoothProfileState.java diff --git a/framework/java/android/bluetooth/BluetoothProfileConnectionState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java similarity index 93% rename from framework/java/android/bluetooth/BluetoothProfileConnectionState.java rename to framework/java/android/bluetooth/BluetoothDeviceProfileState.java index a58b85835d2..8e655e213d6 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnectionState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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. @@ -54,8 +54,8 @@ import com.android.internal.util.HierarchicalStateMachine; * Todo(): Write tests for this class, when the Android Mock support is completed. * @hide */ -public final class BluetoothProfileConnectionState extends HierarchicalStateMachine { - private static final String TAG = "BluetoothProfileConnectionState"; +public final class BluetoothDeviceProfileState extends HierarchicalStateMachine { + private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = true; //STOPSHIP - Change to false public static final int CONNECT_HFP_OUTGOING = 1; @@ -72,7 +72,7 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach public static final int AUTO_CONNECT_PROFILES = 10; public static final int TRANSITION_TO_STABLE = 11; - private static final int AUTO_CONNECT_DELAY = 8000; // 8 secs + private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -137,7 +137,22 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach } }; - public BluetoothProfileConnectionState(Context context, String address, + private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) { + // This works only because these broadcast intents are "sticky" + Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); + if (i != null) { + int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (device != null && autoConnectDevice.equals(device)) { + return true; + } + } + } + return false; + } + + public BluetoothDeviceProfileState(Context context, String address, BluetoothService service, BluetoothA2dpService a2dpService) { super(address); mContext = context; @@ -168,12 +183,12 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach mHeadsetService = new BluetoothHeadset(mContext, this); } public void onServiceConnected() { - synchronized(BluetoothProfileConnectionState.this) { + synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = true; } } public void onServiceDisconnected() { - synchronized(BluetoothProfileConnectionState.this) { + synchronized(BluetoothDeviceProfileState.this) { mHeadsetServiceConnected = false; } } @@ -222,16 +237,21 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach processCommand(UNPAIR); break; case AUTO_CONNECT_PROFILES: - if (!mHeadsetServiceConnected) { + if (isPhoneDocked(mDevice)) { + // Don't auto connect to docks. + break; + } else if (!mHeadsetServiceConnected) { deferMessage(message); } else { if (mHeadsetService.getPriority(mDevice) == - BluetoothHeadset.PRIORITY_AUTO_CONNECT) { + BluetoothHeadset.PRIORITY_AUTO_CONNECT && + !mHeadsetService.isConnected(mDevice)) { mHeadsetService.connectHeadset(mDevice); } if (mA2dpService != null && - mA2dpService.getSinkPriority(mDevice) == - BluetoothA2dp.PRIORITY_AUTO_CONNECT) { + mA2dpService.getSinkPriority(mDevice) == + BluetoothA2dp.PRIORITY_AUTO_CONNECT && + mA2dpService.getConnectedSinks().length == 0) { mA2dpService.connectSink(mDevice); } } @@ -633,6 +653,10 @@ public final class BluetoothProfileConnectionState extends HierarchicalStateMach return false; } + /*package*/ BluetoothDevice getDevice() { + return mDevice; + } + private void log(String message) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java new file mode 100644 index 00000000000..946dcaa01c8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2010 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.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Message; +import android.util.Log; + +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; + +/** + * This state machine is used to serialize the connections + * to a particular profile. Currently, we only allow one device + * to be connected to a particular profile. + * States: + * {@link StableState} : No pending commands. Send the + * command to the appropriate remote device specific state machine. + * + * {@link PendingCommandState} : A profile connection / disconnection + * command is being executed. This will result in a profile state + * change. Defer all commands. + * @hide + */ + +public class BluetoothProfileState extends HierarchicalStateMachine { + private static final boolean DBG = true; // STOPSHIP - change to false. + private static final String TAG = "BluetoothProfileState"; + + public static int HFP = 0; + public static int A2DP = 1; + + private static int TRANSITION_TO_STABLE = 100; + + private int mProfile; + private BluetoothDevice mPendingDevice; + private PendingCommandState mPendingCommandState = new PendingCommandState(); + private StableState mStableState = new StableState(); + + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0); + if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED || + newState == BluetoothHeadset.STATE_DISCONNECTED)) { + sendMessage(TRANSITION_TO_STABLE); + } + } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); + if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED || + newState == BluetoothA2dp.STATE_DISCONNECTED)) { + sendMessage(TRANSITION_TO_STABLE); + } + } + } + }; + + public BluetoothProfileState(Context context, int profile) { + super("BluetoothProfileState:" + profile); + mProfile = profile; + addState(mStableState); + addState(mPendingCommandState); + setInitialState(mStableState); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); + filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + context.registerReceiver(mBroadcastReceiver, filter); + } + + private class StableState extends HierarchicalState { + @Override + protected void enter() { + log("Entering Stable State"); + mPendingDevice = null; + } + + @Override + protected boolean processMessage(Message msg) { + if (msg.what != TRANSITION_TO_STABLE) { + transitionTo(mPendingCommandState); + } + return true; + } + } + + private class PendingCommandState extends HierarchicalState { + @Override + protected void enter() { + log("Entering PendingCommandState State"); + dispatchMessage(getCurrentMessage()); + } + + @Override + protected boolean processMessage(Message msg) { + if (msg.what == TRANSITION_TO_STABLE) { + transitionTo(mStableState); + } else { + dispatchMessage(msg); + } + return true; + } + + private void dispatchMessage(Message msg) { + BluetoothDeviceProfileState deviceProfileMgr = + (BluetoothDeviceProfileState)msg.obj; + int cmd = msg.arg1; + if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) { + mPendingDevice = deviceProfileMgr.getDevice(); + deviceProfileMgr.sendMessage(cmd); + } else { + Message deferMsg = new Message(); + deferMsg.arg1 = cmd; + deferMsg.obj = deviceProfileMgr; + deferMessage(deferMsg); + } + } + } + + private void log(String message) { + if (DBG) { + Log.i(TAG, "Message:" + message); + } + } +} -- GitLab From 9f23976a7c9dad89a1553d0a016f3c0e69f6dbe1 Mon Sep 17 00:00:00 2001 From: Danica Chang Date: Tue, 15 Jun 2010 15:54:21 -0700 Subject: [PATCH 0102/1408] deleted ScoSocket.java and android_bluetooth_ScoSocket.cpp Change-Id: I2bc63979bada9e18bcfeb9e740fab8b37c6868b9 --- .../java/android/bluetooth/ScoSocket.java | 206 ------------------ 1 file changed, 206 deletions(-) delete mode 100644 framework/java/android/bluetooth/ScoSocket.java diff --git a/framework/java/android/bluetooth/ScoSocket.java b/framework/java/android/bluetooth/ScoSocket.java deleted file mode 100644 index b65a99a048e..00000000000 --- a/framework/java/android/bluetooth/ScoSocket.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2008 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.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * Simple SCO Socket. - * Currently in Android, there is no support for sending data over a SCO - * socket - this is managed by the hardware link to the Bluetooth Chip. This - * class is instead intended for management of the SCO socket lifetime, - * and is tailored for use with the headset / handsfree profiles. - * @hide - */ -public class ScoSocket { - private static final String TAG = "ScoSocket"; - private static final boolean DBG = true; - private static final boolean VDBG = false; // even more logging - - public static final int STATE_READY = 1; // Ready for use. No threads or sockets - public static final int STATE_ACCEPT = 2; // accept() thread running - public static final int STATE_CONNECTING = 3; // connect() thread running - public static final int STATE_CONNECTED = 4; // connected, waiting for close() - public static final int STATE_CLOSED = 5; // was connected, now closed. - - private int mState; - private int mNativeData; - private Handler mHandler; - private int mAcceptedCode; - private int mConnectedCode; - private int mClosedCode; - - private WakeLock mWakeLock; // held while in STATE_CONNECTING - - static { - classInitNative(); - } - private native static void classInitNative(); - - public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode, - int closedCode) { - initNative(); - mState = STATE_READY; - mHandler = handler; - mAcceptedCode = acceptedCode; - mConnectedCode = connectedCode; - mClosedCode = closedCode; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket"); - mWakeLock.setReferenceCounted(false); - if (VDBG) log(this + " SCO OBJECT CTOR"); - } - private native void initNative(); - - protected void finalize() throws Throwable { - try { - if (VDBG) log(this + " SCO OBJECT DTOR"); - destroyNative(); - releaseWakeLockNow(); - } finally { - super.finalize(); - } - } - private native void destroyNative(); - - /** Connect this SCO socket to the given BT address. - * Does not block. - */ - public synchronized boolean connect(String address, String name) { - if (DBG) log("connect() " + this); - if (mState != STATE_READY) { - if (DBG) log("connect(): Bad state"); - return false; - } - acquireWakeLock(); - if (connectNative(address, name)) { - mState = STATE_CONNECTING; - return true; - } else { - mState = STATE_CLOSED; - releaseWakeLockNow(); - return false; - } - } - private native boolean connectNative(String address, String name); - - /** Accept incoming SCO connections. - * Does not block. - */ - public synchronized boolean accept() { - if (VDBG) log("accept() " + this); - if (mState != STATE_READY) { - if (DBG) log("Bad state"); - return false; - } - if (acceptNative()) { - mState = STATE_ACCEPT; - return true; - } else { - mState = STATE_CLOSED; - return false; - } - } - private native boolean acceptNative(); - - public synchronized void close() { - if (DBG) log(this + " SCO OBJECT close() mState = " + mState); - acquireWakeLock(); - mState = STATE_CLOSED; - closeNative(); - releaseWakeLock(); - } - private native void closeNative(); - - public synchronized int getState() { - return mState; - } - - private synchronized void onConnected(int result) { - if (VDBG) log(this + " onConnected() mState = " + mState + " " + this); - if (mState != STATE_CONNECTING) { - if (DBG) log("Strange state, closing " + mState + " " + this); - return; - } - if (result >= 0) { - mState = STATE_CONNECTED; - } else { - mState = STATE_CLOSED; - } - mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget(); - releaseWakeLockNow(); - } - - private synchronized void onAccepted(int result) { - if (VDBG) log("onAccepted() " + this); - if (mState != STATE_ACCEPT) { - if (DBG) log("Strange state " + this); - return; - } - if (result >= 0) { - mState = STATE_CONNECTED; - } else { - mState = STATE_CLOSED; - } - mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget(); - } - - private synchronized void onClosed() { - if (DBG) log("onClosed() " + this); - if (mState != STATE_CLOSED) { - mState = STATE_CLOSED; - mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget(); - releaseWakeLock(); - } - } - - private void acquireWakeLock() { - if (!mWakeLock.isHeld()) { - mWakeLock.acquire(); - if (VDBG) log("mWakeLock.acquire() " + this); - } - } - - private void releaseWakeLock() { - if (mWakeLock.isHeld()) { - // Keep apps processor awake for a further 2 seconds. - // This is a hack to resolve issue http://b/1616263 - in which - // we are left in a 80 mA power state when remotely terminating a - // call while connected to BT headset "HTC BH S100 " with A2DP and - // HFP profiles. - if (VDBG) log("mWakeLock.release() in 2 sec" + this); - mWakeLock.acquire(2000); - } - } - - private void releaseWakeLockNow() { - if (mWakeLock.isHeld()) { - if (VDBG) log("mWakeLock.release() now" + this); - mWakeLock.release(); - } - } - - private void log(String msg) { - Log.d(TAG, msg); - } -} -- GitLab From 5d0b83e28567723a8a65677ef24d8406a532055b Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 4 Jun 2010 10:23:03 -0700 Subject: [PATCH 0103/1408] HID profile. Change-Id: I52e965a6537bce02c751ba26fe7b44dd03832510 --- .../bluetooth/BluetoothInputDevice.java | 241 ++++++++++++++++++ .../java/android/bluetooth/BluetoothUuid.java | 6 + .../java/android/bluetooth/IBluetooth.aidl | 9 + 3 files changed, 256 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothInputDevice.java diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java new file mode 100644 index 00000000000..179383827dc --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2010 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Public API for controlling the Bluetooth HID (Input Device) Profile + * + * BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service + * which handles the HID profile. + * + * Creating a BluetoothInputDevice object will initiate a binding with the + * Bluetooth service. Users of this object should call close() when they + * are finished, so that this proxy object can unbind from the service. + * + * Currently the Bluetooth service runs in the system server and this + * proxy object will be immediately bound to the service on construction. + * + * @hide + */ +public final class BluetoothInputDevice { + private static final String TAG = "BluetoothInputDevice"; + private static final boolean DBG = false; + + /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ + public static final String EXTRA_INPUT_DEVICE_STATE = + "android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE"; + /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ + public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE = + "android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE"; + + /** Indicates the state of an input device has changed. + * This intent will always contain EXTRA_INPUT_DEVICE_STATE, + * EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE + * extras. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_INPUT_DEVICE_STATE_CHANGED = + "android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED"; + + public static final int STATE_DISCONNECTED = 0; + public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTED = 2; + public static final int STATE_DISCONNECTING = 3; + + /** + * Auto connection, incoming and outgoing connection are allowed at this + * priority level. + */ + public static final int PRIORITY_AUTO_CONNECT = 1000; + /** + * Incoming and outgoing connection are allowed at this priority level + */ + public static final int PRIORITY_ON = 100; + /** + * Connections to the device are not allowed at this priority level. + */ + public static final int PRIORITY_OFF = 0; + /** + * Default priority level when the device is unpaired. + */ + public static final int PRIORITY_UNDEFINED = -1; + + private final IBluetooth mService; + private final Context mContext; + + /** + * Create a BluetoothInputDevice proxy object for interacting with the local + * Bluetooth Service which handle the HID profile. + * @param c Context + */ + public BluetoothInputDevice(Context c) { + mContext = c; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b != null) { + mService = IBluetooth.Stub.asInterface(b); + } else { + Log.w(TAG, "Bluetooth Service not available!"); + + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later when it is actually used. + mService = null; + } + } + + /** Initiate a connection to an Input device. + * + * This function returns false on error and true if the connection + * attempt is being made. + * + * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the + * connection is completed. + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean connectInputDevice(BluetoothDevice device) { + if (DBG) log("connectInputDevice(" + device + ")"); + try { + return mService.connectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Initiate disconnect from an Input Device. + * This function return false on error and true if the disconnection + * attempt is being made. + * + * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when + * disconnect is completed. + * + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean disconnectInputDevice(BluetoothDevice device) { + if (DBG) log("disconnectInputDevice(" + device + ")"); + try { + return mService.disconnectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Check if a specified InputDevice is connected. + * + * @param device Remote BT device. + * @return True if connected , false otherwise and on error. + * @hide + */ + public boolean isInputDeviceConnected(BluetoothDevice device) { + if (DBG) log("isInputDeviceConnected(" + device + ")"); + int state = getInputDeviceState(device); + if (state == STATE_CONNECTED) return true; + return false; + } + + /** Check if any Input Device is connected. + * + * @return a unmodifiable set of connected Input Devices, or null on error. + * @hide + */ + public Set getConnectedInputDevices() { + if (DBG) log("getConnectedInputDevices()"); + try { + return Collections.unmodifiableSet( + new HashSet( + Arrays.asList(mService.getConnectedInputDevices()))); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + + /** Get the state of an Input Device. + * + * @param device Remote BT device. + * @return The current state of the Input Device + * @hide + */ + public int getInputDeviceState(BluetoothDevice device) { + if (DBG) log("getInputDeviceState(" + device + ")"); + try { + return mService.getInputDeviceState(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return STATE_DISCONNECTED; + } + } + + /** + * Set priority of an input device. + * + * Priority is a non-negative integer. Priority can take the following + * values: + * {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT} + * + * @param device Paired device. + * @param priority Integer priority + * @return true if priority is set, false on error + */ + public boolean setInputDevicePriority(BluetoothDevice device, int priority) { + if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")"); + try { + return mService.setInputDevicePriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Get the priority associated with an Input Device. + * + * @param device Input Device + * @return non-negative priority, or negative error code on error. + */ + public int getInputDevicePriority(BluetoothDevice device) { + if (DBG) log("getInputDevicePriority(" + device + ")"); + try { + return mService.getInputDevicePriority(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return PRIORITY_OFF; + } + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 4164a3d6e66..1909e032637 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -49,6 +49,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid Hid = + ParcelUuid.fromString("00000011-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -82,6 +84,10 @@ public final class BluetoothUuid { return uuid.equals(AvrcpTarget); } + public static boolean isInputDevice(ParcelUuid uuid) { + return uuid.equals(Hid); + } + /** * Returns true if ParcelUuid is present in uuidArray * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index ea71034f5b4..75f093c8e3f 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.BluetoothDevice; import android.os.ParcelUuid; /** @@ -72,4 +73,12 @@ interface IBluetooth boolean connectHeadset(String address); boolean disconnectHeadset(String address); boolean notifyIncomingConnection(String address); + + // HID profile APIs + boolean connectInputDevice(in BluetoothDevice device); + boolean disconnectInputDevice(in BluetoothDevice device); + BluetoothDevice[] getConnectedInputDevices(); // change to Set<> once AIDL supports + int getInputDeviceState(in BluetoothDevice device); + boolean setInputDevicePriority(in BluetoothDevice device, int priority); + int getInputDevicePriority(in BluetoothDevice device); } -- GitLab From 026e857303f0a6b0d1e4175790b148b8824b2b87 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Mon, 21 Jun 2010 16:23:42 -0700 Subject: [PATCH 0104/1408] Changes for bluetooth keyboard support. Add device class constant for PROFILE_HID and check for Device.Major.PERIPHERAL when checking for class matches. Change UUID for HID support. Fix bug in BluetoothService. Change-Id: Ie5361257d90fd749249e915824564bc3d79fb95d --- framework/java/android/bluetooth/BluetoothClass.java | 4 ++++ framework/java/android/bluetooth/BluetoothUuid.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index c7fea9e1dad..0c9bab24965 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -259,6 +259,8 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_A2DP = 1; /** @hide */ public static final int PROFILE_OPP = 2; + /** @hide */ + public static final int PROFILE_HID = 3; /** * Check class bits for possible bluetooth profile support. @@ -324,6 +326,8 @@ public final class BluetoothClass implements Parcelable { default: return false; } + } else if (profile == PROFILE_HID) { + return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL; } else { return false; } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 1909e032637..f1ee9079012 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -50,7 +50,7 @@ public final class BluetoothUuid { public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid Hid = - ParcelUuid.fromString("00000011-0000-1000-8000-00805f9b34fb"); + ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, -- GitLab From c3b951454dc6c850cfb1dd7b7225081745f4900f Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 19 Jul 2010 16:28:27 -0700 Subject: [PATCH 0105/1408] Add HID to the state machine and add native call backs. Change-Id: Ib9f3e476d4176bc04e23e7674dc54aa5a6417308 --- .../BluetoothDeviceProfileState.java | 253 +++++++++++++++++- .../bluetooth/BluetoothProfileState.java | 12 +- 2 files changed, 256 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 8e655e213d6..1fd71518dee 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -58,19 +58,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = true; //STOPSHIP - Change to false + // TODO(): Restructure the state machine to make it scalable with regard to profiles. public static final int CONNECT_HFP_OUTGOING = 1; public static final int CONNECT_HFP_INCOMING = 2; public static final int CONNECT_A2DP_OUTGOING = 3; public static final int CONNECT_A2DP_INCOMING = 4; + public static final int CONNECT_HID_OUTGOING = 5; + public static final int CONNECT_HID_INCOMING = 6; - public static final int DISCONNECT_HFP_OUTGOING = 5; - private static final int DISCONNECT_HFP_INCOMING = 6; - public static final int DISCONNECT_A2DP_OUTGOING = 7; - public static final int DISCONNECT_A2DP_INCOMING = 8; + public static final int DISCONNECT_HFP_OUTGOING = 50; + private static final int DISCONNECT_HFP_INCOMING = 51; + public static final int DISCONNECT_A2DP_OUTGOING = 52; + public static final int DISCONNECT_A2DP_INCOMING = 53; + public static final int DISCONNECT_HID_OUTGOING = 54; + public static final int DISCONNECT_HID_INCOMING = 55; - public static final int UNPAIR = 9; - public static final int AUTO_CONNECT_PROFILES = 10; - public static final int TRANSITION_TO_STABLE = 11; + public static final int UNPAIR = 100; + public static final int AUTO_CONNECT_PROFILES = 101; + public static final int TRANSITION_TO_STABLE = 102; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs @@ -79,6 +84,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree(); private IncomingA2dp mIncomingA2dp = new IncomingA2dp(); private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp(); + private OutgoingHid mOutgoingHid = new OutgoingHid(); + private IncomingHid mIncomingHid = new IncomingHid(); private Context mContext; private BluetoothService mService; @@ -89,6 +96,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothDevice mDevice; private int mHeadsetState; private int mA2dpState; + private int mHidState; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -125,6 +133,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothA2dp.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } + } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + int oldState = + intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); + mHidState = newState; + if (oldState == BluetoothInputDevice.STATE_CONNECTED && + newState == BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HID_INCOMING); + } + if (newState == BluetoothInputDevice.STATE_CONNECTED || + newState == BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(TRANSITION_TO_STABLE); + } } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { if (!getCurrentState().equals(mBondedDevice)) { Log.e(TAG, "State is: " + getCurrentState()); @@ -165,6 +186,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine addState(mIncomingHandsfree); addState(mIncomingA2dp); addState(mOutgoingA2dp); + addState(mOutgoingHid); + addState(mIncomingHid); setInitialState(mBondedDevice); IntentFilter filter = new IntentFilter(); @@ -172,6 +195,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -224,6 +248,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: transitionTo(mIncomingA2dp); break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + transitionTo(mOutgoingHid); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + break; case UNPAIR: if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HFP_OUTGOING); @@ -233,6 +265,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(DISCONNECT_A2DP_OUTGOING); deferMessage(message); break; + } else if (mHidState != BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HID_OUTGOING); + deferMessage(message); + break; } processCommand(UNPAIR); break; @@ -254,6 +290,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine mA2dpService.getConnectedSinks().length == 0) { mA2dpService.connectSink(mDevice); } + if (mService.getInputDevicePriority(mDevice) == + BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { + mService.connectInputDevice(mDevice); + } } break; case TRANSITION_TO_STABLE: @@ -342,6 +382,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(deferMsg); } break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -409,6 +466,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // If this causes incoming HFP to fail, it is more of a headset problem // since both connections are incoming ones. break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -496,6 +560,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -561,6 +642,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -576,6 +664,143 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } + private class OutgoingHid extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering OutgoingHid state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HID_OUTGOING && + mCommand != DISCONNECT_HID_OUTGOING) { + Log.e(TAG, "Error: OutgoingHid state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("OutgoingHid State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + // defer all outgoing messages + case CONNECT_HFP_OUTGOING: + case CONNECT_A2DP_OUTGOING: + case CONNECT_HID_OUTGOING: + case DISCONNECT_HFP_OUTGOING: + case DISCONNECT_A2DP_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + + case CONNECT_HFP_INCOMING: + transitionTo(mIncomingHandsfree); + case CONNECT_A2DP_INCOMING: + transitionTo(mIncomingA2dp); + + // Don't cancel HID outgoing as there is no guarantee it + // will get canceled. + // It might already be connected but we might not have got the + // INPUT_DEVICE_STATE_CHANGE. Hence, no point disconnecting here. + // The worst case, the connection will fail, retry. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case CONNECT_HID_INCOMING: + // Bluez will take care of the conflicts + transitionTo(mIncomingHid); + break; + + case DISCONNECT_HFP_INCOMING: + case DISCONNECT_A2DP_INCOMING: + // At this point, we are already disconnected + // with HFP. Sometimes HID connection can + // fail due to the disconnection of HFP. So add a retry + // for the HID. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + // Ignore, will be handled by Bluez + break; + + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class IncomingHid extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering IncomingHid state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HID_INCOMING && + mCommand != DISCONNECT_HID_INCOMING) { + Log.e(TAG, "Error: IncomingHid state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("IncomingHid State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + case CONNECT_HFP_INCOMING: + case DISCONNECT_HFP_OUTGOING: + case CONNECT_A2DP_INCOMING: + case CONNECT_A2DP_OUTGOING: + case DISCONNECT_A2DP_OUTGOING: + case CONNECT_HID_OUTGOING: + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_INCOMING: + // Shouldn't happen but if does, we can handle it. + // Depends if the headset can handle it. + // Incoming HID will be handled by Bluez, Disconnect HFP + // the socket would have already been closed. + // ignore + break; + case DISCONNECT_HID_INCOMING: + case DISCONNECT_A2DP_INCOMING: + // Ignore, will be handled by Bluez + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + synchronized void cancelCommand(int command) { if (command == CONNECT_HFP_OUTGOING ) { @@ -619,6 +844,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_A2DP_INCOMING: // ignore, Bluez takes care return true; + case CONNECT_HID_OUTGOING: + return mService.connectInputDeviceInternal(mDevice); + case CONNECT_HID_INCOMING: + return true; case DISCONNECT_HFP_OUTGOING: if (!mHeadsetServiceConnected) { deferHeadsetMessage(command); @@ -645,6 +874,15 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return mA2dpService.disconnectSinkInternal(mDevice); } break; + case DISCONNECT_HID_INCOMING: + // ignore + return true; + case DISCONNECT_HID_OUTGOING: + if (mService.getInputDevicePriority(mDevice) == + BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { + mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON); + } + return mService.disconnectInputDeviceInternal(mDevice); case UNPAIR: return mService.removeBondInternal(mDevice.getAddress()); default: @@ -653,6 +891,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return false; } + /*package*/ BluetoothDevice getDevice() { return mDevice; } diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index 946dcaa01c8..ad70d0de0ef 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -43,8 +43,9 @@ public class BluetoothProfileState extends HierarchicalStateMachine { private static final boolean DBG = true; // STOPSHIP - change to false. private static final String TAG = "BluetoothProfileState"; - public static int HFP = 0; - public static int A2DP = 1; + public static final int HFP = 0; + public static final int A2DP = 1; + public static final int HID = 2; private static int TRANSITION_TO_STABLE = 100; @@ -70,6 +71,12 @@ public class BluetoothProfileState extends HierarchicalStateMachine { newState == BluetoothA2dp.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } + } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED || + newState == BluetoothInputDevice.STATE_DISCONNECTED)) { + sendMessage(TRANSITION_TO_STABLE); + } } } }; @@ -84,6 +91,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); context.registerReceiver(mBroadcastReceiver, filter); } -- GitLab From fc0c182532491dff4a77fd3ead33e97cafbc3205 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 6 Aug 2010 19:03:13 -0700 Subject: [PATCH 0106/1408] Check whether Bluetooth is enabled before making any API calls. For example, Settings app makes calls to get Bonded Devices before Bluetooth is on. This leads to ANRs and will prevent autoconnection. Change-Id: I56748a9bd1d603b5782c17775c6b20b831bf6572 --- framework/java/android/bluetooth/BluetoothAdapter.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8eda844380e..bb4774d567e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -474,6 +474,7 @@ public final class BluetoothAdapter { * @return true if the name was set, false otherwise */ public boolean setName(String name) { + if (getState() != STATE_ON) return false; try { return mService.setName(name); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -493,6 +494,7 @@ public final class BluetoothAdapter { * @return scan mode */ public int getScanMode() { + if (getState() != STATE_ON) return SCAN_MODE_NONE; try { return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -524,6 +526,7 @@ public final class BluetoothAdapter { * @hide */ public boolean setScanMode(int mode, int duration) { + if (getState() != STATE_ON) return false; try { return mService.setScanMode(mode, duration); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -532,11 +535,13 @@ public final class BluetoothAdapter { /** @hide */ public boolean setScanMode(int mode) { + if (getState() != STATE_ON) return false; return setScanMode(mode, 120); } /** @hide */ public int getDiscoverableTimeout() { + if (getState() != STATE_ON) return -1; try { return mService.getDiscoverableTimeout(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -545,6 +550,7 @@ public final class BluetoothAdapter { /** @hide */ public void setDiscoverableTimeout(int timeout) { + if (getState() != STATE_ON) return; try { mService.setDiscoverableTimeout(timeout); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -577,6 +583,7 @@ public final class BluetoothAdapter { * @return true on success, false on error */ public boolean startDiscovery() { + if (getState() != STATE_ON) return false; try { return mService.startDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -597,6 +604,7 @@ public final class BluetoothAdapter { * @return true on success, false on error */ public boolean cancelDiscovery() { + if (getState() != STATE_ON) return false; try { mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -619,6 +627,7 @@ public final class BluetoothAdapter { * @return true if discovering */ public boolean isDiscovering() { + if (getState() != STATE_ON) return false; try { return mService.isDiscovering(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -633,6 +642,7 @@ public final class BluetoothAdapter { * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ public Set getBondedDevices() { + if (getState() != STATE_ON) return null; try { return toDeviceSet(mService.listBonds()); } catch (RemoteException e) {Log.e(TAG, "", e);} -- GitLab From 1c8dba03ace069cf5fa83fd8cdc29988a777b07f Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 9 Aug 2010 16:54:03 -0700 Subject: [PATCH 0107/1408] Update javadoc for the API and change return of getBondedDevices(). getBondedDevices() *might* work if called before the Bluetooth State intent is broadcasted. However, this can cause ANRs and problems. This API was updated to return null, if called before the intent is received. However, this might cause existing apps to crash. Return an empty set instead. Change-Id: Ibc484d3394aa0bbebd651221efde6a7015ce7110 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bb4774d567e..03bcadc69b8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -468,6 +468,10 @@ public final class BluetoothAdapter { *

    Valid Bluetooth names are a maximum of 248 UTF-8 characters, however * many remote devices can only display the first 40 characters, and some * may be limited to just 20. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param name a valid Bluetooth name @@ -489,6 +493,10 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#BLUETOOTH} * * @return scan mode @@ -513,6 +521,10 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} *

    Applications cannot set the scan mode. They should use * startActivityForResult( @@ -578,6 +590,10 @@ public final class BluetoothAdapter { *

    Device discovery will only find remote devices that are currently * discoverable (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error @@ -600,6 +616,10 @@ public final class BluetoothAdapter { * the Activity, but is run as a system service, so an application should * always call cancel discovery even if it did not directly request a * discovery, just to be sure. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * * @return true on success, false on error */ @@ -622,6 +642,10 @@ public final class BluetoothAdapter { *

    Applications can also register for {@link #ACTION_DISCOVERY_STARTED} * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery * starts or completes. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return true if discovering @@ -637,12 +661,18 @@ public final class BluetoothAdapter { /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. + *

    If Bluetooth state is not {@link #STATE_ON}, this API + * will return an empty set. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. *

    Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ public Set getBondedDevices() { - if (getState() != STATE_ON) return null; + if (getState() != STATE_ON) { + return toDeviceSet(new String[0]); + } try { return toDeviceSet(mService.listBonds()); } catch (RemoteException e) {Log.e(TAG, "", e);} -- GitLab From aad41c526e8c3d8a84b4d10a755ed7523152f95d Mon Sep 17 00:00:00 2001 From: Herb Jellinek Date: Tue, 10 Aug 2010 13:17:43 -0700 Subject: [PATCH 0108/1408] BluetoothHeadset.java: Added ACTION_VENDOR_SPECIFIC_HEADSET_EVENT, EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS. BluetoothAssignedNumbers.java (new file): A home for BT assigned numbers, beginning with the company IDs. HeadsetBase.java: Reformatted some code. AtCommandHandler.java: Fixed comment typos. Change-Id: I34d6f248166305d72be66632779fc963b894379c Added EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID. Fixed a minor comment typo. Change-Id: I8550692c13510a853c894b2d108bef4520d931c2 --- .../android/bluetooth/AtCommandHandler.java | 5 +- .../bluetooth/BluetoothAssignedNumbers.java | 523 ++++++++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 41 +- .../java/android/bluetooth/HeadsetBase.java | 23 +- 4 files changed, 576 insertions(+), 16 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAssignedNumbers.java diff --git a/framework/java/android/bluetooth/AtCommandHandler.java b/framework/java/android/bluetooth/AtCommandHandler.java index 8de2133e494..6deab34ed48 100644 --- a/framework/java/android/bluetooth/AtCommandHandler.java +++ b/framework/java/android/bluetooth/AtCommandHandler.java @@ -73,7 +73,7 @@ public abstract class AtCommandHandler { * least one element in this array. * @return The result of this command. */ - // Typically used to set this paramter + // Typically used to set this parameter public AtCommandResult handleSetCommand(Object[] args) { return new AtCommandResult(AtCommandResult.ERROR); } @@ -83,11 +83,12 @@ public abstract class AtCommandHandler { * Test commands are part of the Extended command syntax, and are typically * used to request an indication of the range of legal values that "FOO" * can take.

    - * By defualt we return an OK result, to indicate that this command is at + * By default we return an OK result, to indicate that this command is at * least recognized.

    * @return The result of this command. */ public AtCommandResult handleTestCommand() { return new AtCommandResult(AtCommandResult.OK); } + } diff --git a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java new file mode 100644 index 00000000000..55bc814066c --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2010 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; + +/** + * Bluetooth Assigned Numbers. + *

    + * For now we only include Company ID values. + * @see + * The Official Bluetooth SIG Member Website | Company Identifiers + * + * @hide + */ +public class BluetoothAssignedNumbers { + + //// Bluetooth SIG Company ID values + + /* + * Ericsson Technology Licensing. + */ + public static final int ERICSSON_TECHNOLOGY = 0x0000; + + /* + * Nokia Mobile Phones. + */ + public static final int NOKIA_MOBILE_PHONES = 0x0001; + + /* + * Intel Corp. + */ + public static final int INTEL = 0x0002; + + /* + * IBM Corp. + */ + public static final int IBM = 0x0003; + + /* + * Toshiba Corp. + */ + public static final int TOSHIBA = 0x0004; + + /* + * 3Com. + */ + public static final int THREECOM = 0x0005; + + /* + * Microsoft. + */ + public static final int MICROSOFT = 0x0006; + + /* + * Lucent. + */ + public static final int LUCENT = 0x0007; + + /* + * Motorola. + */ + public static final int MOTOROLA = 0x0008; + + /* + * Infineon Technologies AG. + */ + public static final int INFINEON_TECHNOLOGIES = 0x0009; + + /* + * Cambridge Silicon Radio. + */ + public static final int CAMBRIDGE_SILICON_RADIO = 0x000A; + + /* + * Silicon Wave. + */ + public static final int SILICON_WAVE = 0x000B; + + /* + * Digianswer A/S. + */ + public static final int DIGIANSWER = 0x000C; + + /* + * Texas Instruments Inc. + */ + public static final int TEXAS_INSTRUMENTS = 0x000D; + + /* + * Parthus Technologies Inc. + */ + public static final int PARTHUS_TECHNOLOGIES = 0x000E; + + /* + * Broadcom Corporation. + */ + public static final int BROADCOM = 0x000F; + + /* + * Mitel Semiconductor. + */ + public static final int MITEL_SEMICONDUCTOR = 0x0010; + + /* + * Widcomm, Inc. + */ + public static final int WIDCOMM = 0x0011; + + /* + * Zeevo, Inc. + */ + public static final int ZEEVO = 0x0012; + + /* + * Atmel Corporation. + */ + public static final int ATMEL = 0x0013; + + /* + * Mitsubishi Electric Corporation. + */ + public static final int MITSUBISHI_ELECTRIC = 0x0014; + + /* + * RTX Telecom A/S. + */ + public static final int RTX_TELECOM = 0x0015; + + /* + * KC Technology Inc. + */ + public static final int KC_TECHNOLOGY = 0x0016; + + /* + * Newlogic. + */ + public static final int NEWLOGIC = 0x0017; + + /* + * Transilica, Inc. + */ + public static final int TRANSILICA = 0x0018; + + /* + * Rohde & Schwarz GmbH & Co. KG. + */ + public static final int ROHDE_AND_SCHWARZ = 0x0019; + + /* + * TTPCom Limited. + */ + public static final int TTPCOM = 0x001A; + + /* + * Signia Technologies, Inc. + */ + public static final int SIGNIA_TECHNOLOGIES = 0x001B; + + /* + * Conexant Systems Inc. + */ + public static final int CONEXANT_SYSTEMS = 0x001C; + + /* + * Qualcomm. + */ + public static final int QUALCOMM = 0x001D; + + /* + * Inventel. + */ + public static final int INVENTEL = 0x001E; + + /* + * AVM Berlin. + */ + public static final int AVM_BERLIN = 0x001F; + + /* + * BandSpeed, Inc. + */ + public static final int BANDSPEED = 0x0020; + + /* + * Mansella Ltd. + */ + public static final int MANSELLA = 0x0021; + + /* + * NEC Corporation. + */ + public static final int NEC = 0x0022; + + /* + * WavePlus Technology Co., Ltd. + */ + public static final int WAVEPLUS_TECHNOLOGY = 0x0023; + + /* + * Alcatel. + */ + public static final int ALCATEL = 0x0024; + + /* + * Philips Semiconductors. + */ + public static final int PHILIPS_SEMICONDUCTORS = 0x0025; + + /* + * C Technologies. + */ + public static final int C_TECHNOLOGIES = 0x0026; + + /* + * Open Interface. + */ + public static final int OPEN_INTERFACE = 0x0027; + + /* + * R F Micro Devices. + */ + public static final int RF_MICRO_DEVICES = 0x0028; + + /* + * Hitachi Ltd. + */ + public static final int HITACHI = 0x0029; + + /* + * Symbol Technologies, Inc. + */ + public static final int SYMBOL_TECHNOLOGIES = 0x002A; + + /* + * Tenovis. + */ + public static final int TENOVIS = 0x002B; + + /* + * Macronix International Co. Ltd. + */ + public static final int MACRONIX = 0x002C; + + /* + * GCT Semiconductor. + */ + public static final int GCT_SEMICONDUCTOR = 0x002D; + + /* + * Norwood Systems. + */ + public static final int NORWOOD_SYSTEMS = 0x002E; + + /* + * MewTel Technology Inc. + */ + public static final int MEWTEL_TECHNOLOGY = 0x002F; + + /* + * ST Microelectronics. + */ + public static final int ST_MICROELECTRONICS = 0x0030; + + /* + * Synopsys. + */ + public static final int SYNOPSYS = 0x0031; + + /* + * Red-M (Communications) Ltd. + */ + public static final int RED_M = 0x0032; + + /* + * Commil Ltd. + */ + public static final int COMMIL = 0x0033; + + /* + * Computer Access Technology Corporation (CATC). + */ + public static final int CATC = 0x0034; + + /* + * Eclipse (HQ Espana) S.L. + */ + public static final int ECLIPSE = 0x0035; + + /* + * Renesas Technology Corp. + */ + public static final int RENESAS_TECHNOLOGY = 0x0036; + + /* + * Mobilian Corporation. + */ + public static final int MOBILIAN_CORPORATION = 0x0037; + + /* + * Terax. + */ + public static final int TERAX = 0x0038; + + /* + * Integrated System Solution Corp. + */ + public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039; + + /* + * Matsushita Electric Industrial Co., Ltd. + */ + public static final int MATSUSHITA_ELECTRIC = 0x003A; + + /* + * Gennum Corporation. + */ + public static final int GENNUM = 0x003B; + + /* + * Research In Motion. + */ + public static final int RESEARCH_IN_MOTION = 0x003C; + + /* + * IPextreme, Inc. + */ + public static final int IPEXTREME = 0x003D; + + /* + * Systems and Chips, Inc. + */ + public static final int SYSTEMS_AND_CHIPS = 0x003E; + + /* + * Bluetooth SIG, Inc. + */ + public static final int BLUETOOTH_SIG = 0x003F; + + /* + * Seiko Epson Corporation. + */ + public static final int SEIKO_EPSON = 0x0040; + + /* + * Integrated Silicon Solution Taiwan, Inc. + */ + public static final int INTEGRATED_SILICON_SOLUTION = 0x0041; + + /* + * CONWISE Technology Corporation Ltd. + */ + public static final int CONWISE_TECHNOLOGY = 0x0042; + + /* + * PARROT SA. + */ + public static final int PARROT = 0x0043; + + /* + * Socket Mobile. + */ + public static final int SOCKET_MOBILE = 0x0044; + + /* + * Atheros Communications, Inc. + */ + public static final int ATHEROS_COMMUNICATIONS = 0x0045; + + /* + * MediaTek, Inc. + */ + public static final int MEDIATEK = 0x0046; + + /* + * Bluegiga. + */ + public static final int BLUEGIGA = 0x0047; + + /* + * Marvell Technology Group Ltd. + */ + public static final int MARVELL = 0x0048; + + /* + * 3DSP Corporation. + */ + public static final int THREE_DSP = 0x0049; + + /* + * Accel Semiconductor Ltd. + */ + public static final int ACCEL_SEMICONDUCTOR = 0x004A; + + /* + * Continental Automotive Systems. + */ + public static final int CONTINENTAL_AUTOMOTIVE = 0x004B; + + /* + * Apple, Inc. + */ + public static final int APPLE = 0x004C; + + /* + * Staccato Communications, Inc. + */ + public static final int STACCATO_COMMUNICATIONS = 0x004D; + + /* + * Avago Technologies. + */ + public static final int AVAGO = 0x004E; + + /* + * APT Licensing Ltd. + */ + public static final int APT_LICENSING = 0x004F; + + /* + * SiRF Technology, Inc. + */ + public static final int SIRF_TECHNOLOGY = 0x0050; + + /* + * Tzero Technologies, Inc. + */ + public static final int TZERO_TECHNOLOGIES = 0x0051; + + /* + * J&M Corporation. + */ + public static final int J_AND_M = 0x0052; + + /* + * Free2move AB. + */ + public static final int FREE2MOVE = 0x0053; + + /* + * 3DiJoy Corporation. + */ + public static final int THREE_DIJOY = 0x0054; + + /* + * Plantronics, Inc. + */ + public static final int PLANTRONICS = 0x0055; + + /* + * Sony Ericsson Mobile Communications. + */ + public static final int SONY_ERICSSON = 0x0056; + + /* + * Harman International Industries, Inc. + */ + public static final int HARMAN_INTERNATIONAL = 0x0057; + + /* + * Vizio, Inc. + */ + public static final int VIZIO = 0x0058; + + /* + * Nordic Semiconductor ASA. + */ + public static final int NORDIC_SEMICONDUCTOR = 0x0059; + + /* + * EM Microelectronic-Marin SA. + */ + public static final int EM_MICROELECTRONIC_MARIN = 0x005A; + + /* + * Ralink Technology Corporation. + */ + public static final int RALINK_TECHNOLOGY = 0x005B; + + /* + * Belkin International, Inc. + */ + public static final int BELKIN_INTERNATIONAL = 0x005C; + + /* + * Realtek Semiconductor Corporation. + */ + public static final int REALTEK_SEMICONDUCTOR = 0x005D; + + /* + * Stonestreet One, LLC. + */ + public static final int STONESTREET_ONE = 0x005E; + + /* + * Wicentric, Inc. + */ + public static final int WICENTRIC = 0x005F; + + /* + * RivieraWaves S.A.S. + */ + public static final int RIVIERAWAVES = 0x0060; + + /* + * You can't instantiate one of these. + */ + private BluetoothAssignedNumbers() { + } + +} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 95e61b6f6ac..e2d7369445a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -45,7 +45,7 @@ import android.util.Log; * This BluetoothHeadset object is not immediately bound to the * BluetoothHeadset service. Use the ServiceListener interface to obtain a * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetootHeadset after construction. + * immediately call methods on BluetoothHeadset after construction. * * Android only supports one connected Bluetooth Headset at a time. * @@ -84,6 +84,43 @@ public final class BluetoothHeadset { public static final String EXTRA_DISCONNECT_INITIATOR = "android.bluetooth.headset.extra.DISCONNECT_INITIATOR"; + /** + * Broadcast Action: Indicates a headset has posted a vendor-specific event. + *

    Always contains the extra fields {@link #EXTRA_DEVICE}, + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = + "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; + + /** + * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} + * intents that contains the name of the vendor-specific command. + */ + public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = + "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD"; + + /** + * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} + * intents that contains the Company ID of the vendor defining the vendor-specific + * command. + * @see + * Bluetooth SIG Assigned Numbers - Company Identifiers + */ + public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID = + "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID"; + + /** + * A Parcelable String array extra field in + * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains + * the arguments to the vendor-specific command. + */ + public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = + "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; + + /** * TODO(API release): Consider incorporating as new state in * HEADSET_STATE_CHANGED @@ -108,7 +145,7 @@ public final class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completetion. */ + /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */ diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index e2935c95d32..9ef2eb5f3ce 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -74,8 +74,8 @@ public final class HeadsetBase { private native void cleanupNativeDataNative(); - public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, - int rfcommChannel) { + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, + BluetoothDevice device, int rfcommChannel) { mDirection = DIRECTION_OUTGOING; mConnectTimestamp = System.currentTimeMillis(); mAdapter = adapter; @@ -89,9 +89,10 @@ public final class HeadsetBase { initializeNativeDataNative(-1); } - /* Create from an already exisiting rfcomm connection */ - public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, - int socketFd, int rfcommChannel, Handler handler) { + /* Create from an existing rfcomm connection */ + public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, + BluetoothDevice device, + int socketFd, int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; mConnectTimestamp = System.currentTimeMillis(); mAdapter = adapter; @@ -128,7 +129,7 @@ public final class HeadsetBase { (System.currentTimeMillis() - timestamp) + " ms"); if (result.getResultCode() == AtCommandResult.ERROR) { - Log.i(TAG, "Error pocessing <" + input + ">"); + Log.i(TAG, "Error processing <" + input + ">"); } sendURC(result.toString()); @@ -142,8 +143,9 @@ public final class HeadsetBase { */ protected void initializeAtParser() { mAtParser = new AtParser(); - //TODO(): Get rid of this as there are no parsers registered. But because of dependencies, - //it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree + + //TODO(): Get rid of this as there are no parsers registered. But because of dependencies + // it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree } public AtParser getAtParser() { @@ -159,8 +161,7 @@ public final class HeadsetBase { String input = readNative(500); if (input != null) { handleInput(input); - } - else { + } else { last_read_error = getLastReadStatusNative(); if (last_read_error != 0) { Log.i(TAG, "headset read error " + last_read_error); @@ -179,8 +180,6 @@ public final class HeadsetBase { mEventThread.start(); } - - private native String readNative(int timeout_ms); private native int getLastReadStatusNative(); -- GitLab From 41f1a096e5bee96f3b66ca0acdf6b2e425930674 Mon Sep 17 00:00:00 2001 From: Danica Chang Date: Wed, 11 Aug 2010 14:54:43 -0700 Subject: [PATCH 0109/1408] bluetooth tethering Change-Id: Id6d5fb1922facc7013abc29214d3e1141995b767 --- .../bluetooth/BluetoothDevicePicker.java | 5 + .../java/android/bluetooth/BluetoothPan.java | 192 ++++++++++++++++++ .../java/android/bluetooth/BluetoothUuid.java | 13 +- .../java/android/bluetooth/IBluetooth.aidl | 7 + 4 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothPan.java diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java index 05eed0e6de6..741572151ff 100644 --- a/framework/java/android/bluetooth/BluetoothDevicePicker.java +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -63,4 +63,9 @@ public interface BluetoothDevicePicker { public static final int FILTER_TYPE_AUDIO = 1; /** Ask device picker to show BT devices that support Object Transfer */ public static final int FILTER_TYPE_TRANSFER = 2; + /** Ask device picker to show BT devices that support + * Personal Area Networking User (PANU) profile*/ + public static final int FILTER_TYPE_PANU = 3; + /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */ + public static final int FILTER_TYPE_NAP = 4; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java new file mode 100644 index 00000000000..952765d7d9a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2008 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.ServiceManager; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @hide + */ +public final class BluetoothPan { + private static final String TAG = "BluetoothPan"; + private static final boolean DBG = false; + + /** int extra for ACTION_PAN_STATE_CHANGED */ + public static final String EXTRA_PAN_STATE = + "android.bluetooth.pan.extra.STATE"; + /** int extra for ACTION_PAN_STATE_CHANGED */ + public static final String EXTRA_PREVIOUS_PAN_STATE = + "android.bluetooth.pan.extra.PREVIOUS_STATE"; + + /** Indicates the state of an PAN device has changed. + * This intent will always contain EXTRA_DEVICE_STATE, + * EXTRA_PREVIOUS_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE + * extras. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PAN_STATE_CHANGED = + "android.bluetooth.pan.action.STATE_CHANGED"; + + public static final String NAP_ROLE = "nap"; + public static final String NAP_BRIDGE = "pan1"; + + public static final int MAX_CONNECTIONS = 7; + + public static final int STATE_DISCONNECTED = 0; + public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTED = 2; + public static final int STATE_DISCONNECTING = 3; + + private final IBluetooth mService; + private final Context mContext; + + /** + * Create a BluetoothPan proxy object for interacting with the local + * Bluetooth Pan service. + * @param c Context + */ + public BluetoothPan(Context c) { + mContext = c; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b != null) { + mService = IBluetooth.Stub.asInterface(b); + } else { + Log.w(TAG, "Bluetooth Service not available!"); + + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later + // when it is actually used. + mService = null; + } + } + + /** + * Initiate a PAN connection. + * + * This function returns false on error and true if the connection + * attempt is being made. + * + * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when the + * connection is completed. + * + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + return mService.connectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Initiate disconnect from PAN. + * + * This function return false on error and true if the disconnection + * attempt is being made. + * + * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when + * disconnect is completed. + * + * @param device Remote BT device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + return mService.disconnectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** Get the state of a PAN Device. + * + * This function returns an int representing the state of the PAN connection + * + * @param device Remote BT device. + * @return The current state of the PAN Device + * @hide + */ + public int getPanDeviceState(BluetoothDevice device) { + if (DBG) log("getPanDeviceState(" + device + ")"); + try { + return mService.getPanDeviceState(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return STATE_DISCONNECTED; + } + } + + /** Returns a set of all the connected PAN Devices + * + * Does not include devices that are currently connecting or disconnecting + * + * @return a unmodifiable set of connected PAN Devices, or null on error. + * @hide + */ + public Set getConnectedDevices() { + if (DBG) log("getConnectedDevices"); + try { + return Collections.unmodifiableSet( + new HashSet( + Arrays.asList(mService.getConnectedPanDevices()))); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + public void setBluetoothTethering(boolean value, String uuid, String bridge) { + try { + mService.setBluetoothTethering(value, uuid, bridge); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + public boolean isTetheringOn() { + try { + return mService.isTetheringOn(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index f1ee9079012..fb3dfe43181 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -51,10 +51,14 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid Hid = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid PANU = + ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid NAP = + ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush}; + ObexObjectPush, PANU, NAP}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -88,6 +92,13 @@ public final class BluetoothUuid { return uuid.equals(Hid); } + public static boolean isPANU(ParcelUuid uuid) { + return uuid.equals(PANU); + } + + public static boolean isNAP(ParcelUuid uuid) { + return uuid.equals(NAP); + } /** * Returns true if ParcelUuid is present in uuidArray * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 75f093c8e3f..f8f678bae25 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -81,4 +81,11 @@ interface IBluetooth int getInputDeviceState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); + + boolean isTetheringOn(); + void setBluetoothTethering(boolean value, String uuid, String bridge); + int getPanDeviceState(in BluetoothDevice device); + BluetoothDevice[] getConnectedPanDevices(); + boolean connectPanDevice(in BluetoothDevice device); + boolean disconnectPanDevice(in BluetoothDevice device); } -- GitLab From c7ab7b06af4ae8a165c4ee2b6229453156add50c Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 23 Aug 2010 11:49:36 -0700 Subject: [PATCH 0110/1408] Add Tethering class bit check. Change-Id: Iaa87c91c4b3db3b77c2e6c4f559fa2e438c9c247 --- framework/java/android/bluetooth/BluetoothClass.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 0c9bab24965..c8381c96a39 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -261,6 +261,10 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_OPP = 2; /** @hide */ public static final int PROFILE_HID = 3; + /** @hide */ + public static final int PROFILE_PANU = 4; + /** @hide */ + public static final int PROFILE_NAP = 5; /** * Check class bits for possible bluetooth profile support. @@ -328,6 +332,12 @@ public final class BluetoothClass implements Parcelable { } } else if (profile == PROFILE_HID) { return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL; + } else if (profile == PROFILE_PANU || profile == PROFILE_NAP){ + // No good way to distinguish between the two, based on class bits. + if (hasService(Service.NETWORKING)) { + return true; + } + return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING; } else { return false; } -- GitLab From 43d545da140ca23a0f95875449dd2b56dc0dc8dc Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 23 Aug 2010 18:32:03 -0700 Subject: [PATCH 0111/1408] Add BNEP_SVC UUID for incoming tethering connections. Change-Id: I90c24edfd133ac9fa89c3467383cd61fa23954c6 --- framework/java/android/bluetooth/BluetoothUuid.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index fb3dfe43181..3fbfc70bdb1 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -55,6 +55,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BNEP = + ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -92,13 +94,17 @@ public final class BluetoothUuid { return uuid.equals(Hid); } - public static boolean isPANU(ParcelUuid uuid) { + public static boolean isPanu(ParcelUuid uuid) { return uuid.equals(PANU); } - public static boolean isNAP(ParcelUuid uuid) { + public static boolean isNap(ParcelUuid uuid) { return uuid.equals(NAP); } + + public static boolean isBnep(ParcelUuid uuid) { + return uuid.equals(BNEP); + } /** * Returns true if ParcelUuid is present in uuidArray * -- GitLab From 3eda0c426483bc23f5d9ca604ec386ce779913c2 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 26 Aug 2010 16:03:40 -0700 Subject: [PATCH 0112/1408] Add @hide for the VENDOR_SPECIFIC_HEADSET_INTENT Change-Id: I5405b57f5815fa91ffce829dec6da3e4b7f1245f --- framework/java/android/bluetooth/BluetoothHeadset.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 53d6a378616..be21d46430d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -90,6 +90,7 @@ public final class BluetoothHeadset { * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}. *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = @@ -98,6 +99,7 @@ public final class BluetoothHeadset { /** * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} * intents that contains the name of the vendor-specific command. + * @hide */ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD"; @@ -108,6 +110,7 @@ public final class BluetoothHeadset { * command. * @see * Bluetooth SIG Assigned Numbers - Company Identifiers + * @hide */ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID"; @@ -116,6 +119,7 @@ public final class BluetoothHeadset { * A Parcelable String array extra field in * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains * the arguments to the vendor-specific command. + * @hide */ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; -- GitLab From 9798367267612f355db957e43d16a568c27a271d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 2 Sep 2010 12:17:05 -0700 Subject: [PATCH 0113/1408] Decouple Tethering UI with registering of SDP records. This can lead to usability issues, since the SDP record will get registered later on. Change-Id: Ifda78a3831572f1b9955bf06da9a8b0e949942aa --- framework/java/android/bluetooth/BluetoothPan.java | 4 ++-- framework/java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 952765d7d9a..9d0b3f20a5d 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -173,9 +173,9 @@ public final class BluetoothPan { Log.d(TAG, msg); } - public void setBluetoothTethering(boolean value, String uuid, String bridge) { + public void setBluetoothTethering(boolean value) { try { - mService.setBluetoothTethering(value, uuid, bridge); + mService.setBluetoothTethering(value); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index f8f678bae25..c4a40cdc9f0 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -83,7 +83,7 @@ interface IBluetooth int getInputDevicePriority(in BluetoothDevice device); boolean isTetheringOn(); - void setBluetoothTethering(boolean value, String uuid, String bridge); + void setBluetoothTethering(boolean value); int getPanDeviceState(in BluetoothDevice device); BluetoothDevice[] getConnectedPanDevices(); boolean connectPanDevice(in BluetoothDevice device); -- GitLab From 76965ca62f7b54ca277f06a597373439cc87c311 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 9 Sep 2010 15:37:57 -0700 Subject: [PATCH 0114/1408] Out Of Band API for Secure Simple Pairing. Change-Id: I54ded27ab85d46eef3d2cca84f2394b1ffe88ced --- .../android/bluetooth/BluetoothAdapter.java | 33 +++++++++++ .../android/bluetooth/BluetoothDevice.java | 58 ++++++++++++++++++- .../java/android/bluetooth/IBluetooth.aidl | 4 ++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 03bcadc69b8..16a8c571f41 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -26,8 +26,10 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.util.Pair; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; @@ -863,6 +865,37 @@ public final class BluetoothAdapter { return socket; } + /** + * Read the local Out of Band Pairing Data + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return Pair of Hash and Randomizer + * + * @hide + */ + public Pair readOutOfBandData() { + if (getState() != STATE_ON) return null; + try { + byte[] hash = new byte[16]; + byte[] randomizer = new byte[16]; + + byte[] ret = mService.readOutOfBandData(); + + if (ret == null || ret.length != 32) return null; + + hash = Arrays.copyOfRange(ret, 0, 16); + randomizer = Arrays.copyOfRange(ret, 16, 32); + + if (DBG) { + Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) + + ":" + Arrays.toString(randomizer)); + } + return new Pair(hash, randomizer); + + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + private Set toDeviceSet(String[] addresses) { Set devices = new HashSet(addresses.length); for (int i = 0; i < addresses.length; i++) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e77e76f79b2..e577ec4f737 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -325,7 +325,9 @@ public final class BluetoothDevice implements Parcelable { /** The user will be prompted to enter the passkey displayed on remote device * @hide */ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; - + /** The user will be prompted to accept or deny the OOB pairing request + * @hide */ + public static final int PAIRING_VARIANT_OOB_CONSENT = 5; /** * Used as an extra field in {@link #ACTION_UUID} intents, * Contains the {@link android.os.ParcelUuid}s of the remote device which @@ -463,6 +465,52 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Start the bonding (pairing) process with the remote device using the + * Out Of Band mechanism. + * + *

    This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when + * the bonding process completes, and its result. + * + *

    Android system services will handle the necessary user interactions + * to confirm and complete the bonding process. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @param hash - Simple Secure pairing hash + * @param randomizer - The random key obtained using OOB + * @return false on immediate error, true if bonding will begin + * + * @hide + */ + public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) { + try { + return sService.createBondOutOfBand(mAddress, hash, randomizer); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Set the Out Of Band data for a remote device to be used later + * in the pairing mechanism. Users can obtain this data through other + * trusted channels + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @param hash Simple Secure pairing hash + * @param randomizer The random key obtained using OOB + * @return false on error; true otherwise + * + * @hide + */ + public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) { + try { + return sService.setDeviceOutOfBandData(mAddress, hash, randomizer); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Cancel an in-progress bonding request started with {@link #createBond}. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. @@ -616,6 +664,14 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ + public boolean setRemoteOutOfBandData() { + try { + return sService.setRemoteOutOfBandData(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** @hide */ public boolean cancelPairingUserInput() { try { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index ea71034f5b4..6dd8dd64d84 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -44,12 +44,15 @@ interface IBluetooth boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); + byte[] readOutOfBandData(); boolean createBond(in String address); + boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer); boolean cancelBondProcess(in String address); boolean removeBond(in String address); String[] listBonds(); int getBondState(in String address); + boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer); String getRemoteName(in String address); int getRemoteClass(in String address); @@ -60,6 +63,7 @@ interface IBluetooth boolean setPin(in String address, in byte[] pin); boolean setPasskey(in String address, int passkey); boolean setPairingConfirmation(in String address, boolean confirm); + boolean setRemoteOutOfBandData(in String addres); boolean cancelPairingUserInput(in String address); boolean setTrust(in String address, in boolean value); -- GitLab From c7e8488fc4a1b3f5bcfbfba153be079fded7cdaa Mon Sep 17 00:00:00 2001 From: Jake Hamby Date: Thu, 16 Sep 2010 18:12:51 -0700 Subject: [PATCH 0115/1408] Fix Bluetooth Javadoc to clarify the maximum device name length. The maximum length of a Bluetooth device name is 248 bytes using UTF-8 encoding. Updated the Javadoc to clarify that the length is limited by the number of UTF-8 bytes and not the number of characters. Change-Id: I135671f5ee6c5eb6372f3fbbc5fccb02de65e6c4 --- .../java/android/bluetooth/BluetoothAdapter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 16a8c571f41..33fd39513f5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -465,11 +465,11 @@ public final class BluetoothAdapter { } /** - * Set the friendly Bluetooth name of the local Bluetoth adapter. + * Set the friendly Bluetooth name of the local Bluetooth adapter. *

    This name is visible to remote Bluetooth devices. - *

    Valid Bluetooth names are a maximum of 248 UTF-8 characters, however - * many remote devices can only display the first 40 characters, and some - * may be limited to just 20. + *

    Valid Bluetooth names are a maximum of 248 bytes using UTF-8 + * encoding, although many remote devices can only display the first + * 40 characters, and some may be limited to just 20. *

    If Bluetooth state is not {@link #STATE_ON}, this API * will return false. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} @@ -488,7 +488,7 @@ public final class BluetoothAdapter { } /** - * Get the current Bluetooth scan mode of the local Bluetooth adaper. + * Get the current Bluetooth scan mode of the local Bluetooth adapter. *

    The Bluetooth scan mode determines if the local adapter is * connectable and/or discoverable from remote Bluetooth devices. *

    Possible values are: @@ -611,7 +611,7 @@ public final class BluetoothAdapter { /** * Cancel the current device discovery process. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. - *

    Because discovery is a heavyweight precedure for the Bluetooth + *

    Because discovery is a heavyweight procedure for the Bluetooth * adapter, this method should always be called before attempting to connect * to a remote device with {@link * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by -- GitLab From 2af0776a750217842622b1f54b4964b5e9ec371c Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 24 Aug 2010 17:36:13 -0700 Subject: [PATCH 0116/1408] New public APIs for BluetoothA2dp and BluetoothHeadset profiles. Change-Id: I1cc4b109542dfd62473cb95797c8c3d0d15725f4 --- .../java/android/bluetooth/BluetoothA2dp.java | 436 ++++++++------ .../android/bluetooth/BluetoothAdapter.java | 104 ++++ .../android/bluetooth/BluetoothHeadset.java | 545 +++++++++--------- .../android/bluetooth/BluetoothProfile.java | 239 ++++++++ 4 files changed, 891 insertions(+), 433 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothProfile.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7e5f858f050..d308a5c315b 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -18,88 +18,104 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.server.BluetoothA2dpService; import android.content.Context; -import android.os.ServiceManager; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.server.BluetoothA2dpService; import android.util.Log; -import java.util.Arrays; import java.util.Collections; -import java.util.Set; +import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Set; + /** - * Public API for controlling the Bluetooth A2DP Profile Service. - * - * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP - * Service via IPC. + * This class provides the public APIs to control the Bluetooth A2DP + * profile. * - * Creating a BluetoothA2dp object will initiate a binding with the - * BluetoothHeadset service. Users of this object should call close() when they - * are finished, so that this proxy object can unbind from the service. + *

    BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothA2dp proxy object. * - * Currently the BluetoothA2dp service runs in the system server and this - * proxy object will be immediately bound to the service on construction. - * - * Currently this class provides methods to connect to A2DP audio sinks. - * - * @hide + *

    Android only supports one connected Bluetooth A2dp device at a time. + * Each method is protected with its appropriate permission. */ -public final class BluetoothA2dp { +public final class BluetoothA2dp implements BluetoothProfile { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = false; - /** int extra for ACTION_SINK_STATE_CHANGED */ - public static final String EXTRA_SINK_STATE = - "android.bluetooth.a2dp.extra.SINK_STATE"; - /** int extra for ACTION_SINK_STATE_CHANGED */ - public static final String EXTRA_PREVIOUS_SINK_STATE = - "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE"; + /** + * Intent used to broadcast the change in connection state of the A2DP + * profile. + * + *

    This intent will have 3 extras: + * {@link #EXTRA_STATE} - The current state of the profile. + * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + * + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; - /** Indicates the state of an A2DP audio sink has changed. - * This intent will always contain EXTRA_SINK_STATE, - * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE - * extras. + /** + * Intent used to broadcast the change in the Playing state of the A2DP + * profile. + * + *

    This intent will have 3 extras: + * {@link #EXTRA_STATE} - The current state of the profile. + * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + * + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SINK_STATE_CHANGED = - "android.bluetooth.a2dp.action.SINK_STATE_CHANGED"; + public static final String ACTION_PLAYING_STATE_CHANGED = + "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; - /** Playing implies connected */ - public static final int STATE_PLAYING = 4; + /** + * A2DP sink device is streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_PLAYING = 10; - /** Default priority for a2dp devices that we try to auto-connect - * and allow incoming connections */ - public static final int PRIORITY_AUTO_CONNECT = 1000; - /** Default priority for a2dp devices that should allow incoming - * connections */ - public static final int PRIORITY_ON = 100; - /** Default priority for a2dp devices that should not allow incoming - * connections */ - public static final int PRIORITY_OFF = 0; - /** Default priority when not set or when the device is unpaired */ - public static final int PRIORITY_UNDEFINED = -1; + /** + * A2DP sink device is NOT streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_NOT_PLAYING = 11; - private final IBluetoothA2dp mService; - private final Context mContext; + private ServiceListener mServiceListener; + private IBluetoothA2dp mService; + private BluetoothAdapter mAdapter; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. - * @param c Context + * */ - public BluetoothA2dp(Context c) { - mContext = c; - + /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) { IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { mService = IBluetoothA2dp.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this); + } } else { Log.w(TAG, "Bluetooth A2DP service not available!"); @@ -109,167 +125,222 @@ public final class BluetoothA2dp { } } - /** Initiate a connection to an A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when the - * connection is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean connectSink(BluetoothDevice device) { - if (DBG) log("connectSink(" + device + ")"); - try { - return mService.connectSink(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Initiate disconnect from an A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when - * disconnect is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean disconnectSink(BluetoothDevice device) { - if (DBG) log("disconnectSink(" + device + ")"); - try { - return mService.disconnectSink(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Initiate suspend from an A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when - * suspend is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} */ - public boolean suspendSink(BluetoothDevice device) { - try { - return mService.suspendSink(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public Set getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return toDeviceSet(mService.getConnectedDevices()); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return toDeviceSet(new BluetoothDevice[0]); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return toDeviceSet(new BluetoothDevice[0]); } - /** Initiate resume from an suspended A2DP sink. - * Listen for SINK_STATE_CHANGED_ACTION to find out when - * resume is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} */ - public boolean resumeSink(BluetoothDevice device) { - try { - return mService.resumeSink(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public Set getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return toDeviceSet(mService.getDevicesMatchingConnectionStates(states)); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return toDeviceSet(new BluetoothDevice[0]); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return toDeviceSet(new BluetoothDevice[0]); } - /** Check if a specified A2DP sink is connected. - * @param device Remote BT device. - * @return True if connected (or playing), false otherwise and on error. - * @hide + /** + * {@inheritDoc} */ - public boolean isSinkConnected(BluetoothDevice device) { - if (DBG) log("isSinkConnected(" + device + ")"); - int state = getSinkState(device); - return state == STATE_CONNECTED || state == STATE_PLAYING; + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; } - /** Check if any A2DP sink is connected. - * @return a unmodifiable set of connected A2DP sinks, or null on error. + /** + * {@inheritDoc} * @hide */ - public Set getConnectedSinks() { - if (DBG) log("getConnectedSinks()"); - try { - return Collections.unmodifiableSet( - new HashSet(Arrays.asList(mService.getConnectedSinks()))); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return null; + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Check if any A2DP sink is in Non Disconnected state - * i.e playing, connected, connecting, disconnecting. - * @return a unmodifiable set of connected A2DP sinks, or null on error. + /** + * {@inheritDoc} * @hide */ - public Set getNonDisconnectedSinks() { - if (DBG) log("getNonDisconnectedSinks()"); - try { - return Collections.unmodifiableSet( - new HashSet(Arrays.asList(mService.getNonDisconnectedSinks()))); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return null; + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; } - /** Get the state of an A2DP sink - * @param device Remote BT device. - * @return State code, one of STATE_ - * @hide + /** + * Check if A2DP profile is streaming music. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device BluetoothDevice device */ - public int getSinkState(BluetoothDevice device) { - if (DBG) log("getSinkState(" + device + ")"); - try { - return mService.getSinkState(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return BluetoothA2dp.STATE_DISCONNECTED; + public boolean isA2dpPlaying(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.isA2dpPlaying(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Set priority of a2dp sink. - * Priority is a non-negative integer. By default paired sinks will have - * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). - * Sinks with priority greater than zero will accept incoming connections - * (if no sink is currently connected). - * Priority for unpaired sink must be PRIORITY_NONE. - * @param device Paired sink - * @param priority Integer priority, for example PRIORITY_AUTO or - * PRIORITY_NONE - * @return true if priority is set, false on error + * Initiate suspend from an A2DP sink. + * + *

    This API will return false in scenarios like the A2DP + * device is not in connected state etc. When this API returns, + * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED} + * intent will be broadcasted with the state. Users can get the + * state of the A2DP device from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param device Remote A2DP sink + * @return false on immediate error, + * true otherwise + * @hide */ - public boolean setSinkPriority(BluetoothDevice device, int priority) { - if (DBG) log("setSinkPriority(" + device + ", " + priority + ")"); - try { - return mService.setSinkPriority(device, priority); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean suspendSink(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.suspendSink(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get priority of a2dp sink. - * @param device Sink - * @return non-negative priority, or negative error code on error. + * Initiate resume from a suspended A2DP sink. + * + *

    This API will return false in scenarios like the A2DP + * device is not in suspended state etc. When this API returns, + * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED} + * intent will be broadcasted with the state. Users can get the + * state of the A2DP device from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param device Remote A2DP sink + * @return false on immediate error, + * true otherwise + * @hide */ - public int getSinkPriority(BluetoothDevice device) { - if (DBG) log("getSinkPriority(" + device + ")"); - try { - return mService.getSinkPriority(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return PRIORITY_OFF; + public boolean resumeSink(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.resumeSink(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Helper for converting a state to a string. + /** + * Helper for converting a state to a string. + * * For debug use only - strings are not internationalized. * @hide */ @@ -285,12 +356,31 @@ public final class BluetoothA2dp { return "disconnecting"; case STATE_PLAYING: return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; default: return ""; } } + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private Set toDeviceSet(BluetoothDevice[] devices) { + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(devices))); + } + private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 33fd39513f5..21a4bd64b4d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -279,6 +280,61 @@ public final class BluetoothAdapter { */ public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; + /** + * Intent used to broadcast the change in connection state of the local + * Bluetooth adapter to a profile of the remote device. When the adapter is + * not connected to any profiles of any remote devices and it attempts a + * connection to a profile this intent will sent. Once connected, this intent + * will not be sent for any more connection attempts to any profiles of any + * remote device. When the adapter disconnects from the last profile its + * connected to of any remote device, this intent will be sent. + * + *

    This intent is useful for applications that are only concerned about + * whether the local adapter is connected to any profile of any device and + * are not really concerned about which profile. For example, an application + * which displays an icon to display whether Bluetooth is connected or not + * can use this intent. + * + *

    This intent will have 3 extras: + * {@link #EXTRA_STATE} - The current state. + * {@link #EXTRA_PREVIOUS_STATE}- The previous. + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + * + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; + + /** + * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} + * + * This extra represents the current connection state. + */ + public static final String EXTRA_CONNECTION_STATE = + "android.bluetooth.adapter.extra.CONNECTION_STATE"; + + /** + * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} + * + * This extra represents the previous connection state. + */ + public static final String EXTRA_PREVIOUS_CONNECTION_STATE = + "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; + + /** The profile is in disconnected state */ + public static final int STATE_DISCONNECTED = 0; + /** The profile is in connecting state */ + public static final int STATE_CONNECTING = 1; + /** The profile is in connected state */ + public static final int STATE_CONNECTED = 2; + /** The profile is in disconnecting state */ + public static final int STATE_DISCONNECTING = 3; + /** @hide */ public static final String BLUETOOTH_SERVICE = "bluetooth"; @@ -896,6 +952,54 @@ public final class BluetoothAdapter { return null; } + /* + * Get the profile proxy object associated with the profile. + * + *

    Profile can be one of {@link BluetoothProfile.HEADSET} or + * {@link BluetoothProfile.A2DP}. Clients must implements + * {@link BluetoothProfile.ServiceListener} to get notified of + * the connection status and to get the proxy object. + * + * @param context Context of the application + * @param listener The service Listener for connection callbacks. + * @param profile + * @return true on success, false on error + */ + public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, + int profile) { + if (context == null || listener == null) return false; + + if (profile == BluetoothProfile.HEADSET) { + BluetoothHeadset headset = new BluetoothHeadset(context, listener); + return true; + } else if (profile == BluetoothProfile.A2DP) { + BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); + return true; + } else { + return false; + } + } + + /** + * Close the connection of the profile proxy to the Service. + * + *

    Clients should call this when they are no longer using + * the proxy obtained from {@link #getProfileProxy}. + * Profile can be one of {@link BluetoothProfile#HEADSET} or + * {@link BluetoothProfile#A2DP} + * + * @param profile + * @param proxy Profile proxy object + */ + public void closeProfileProxy(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET) { + BluetoothHeadset headset = (BluetoothHeadset)proxy; + if (headset != null) { + headset.close(); + } + } + } + private Set toDeviceSet(String[] addresses) { Set devices = new HashSet(addresses.length); for (int i = 0; i < addresses.length; i++) { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index be21d46430d..0496b1f27ce 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -26,63 +28,66 @@ import android.os.RemoteException; import android.os.IBinder; import android.util.Log; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * * Public API for controlling the Bluetooth Headset Service. This includes both - * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will - * attempt a handsfree connection first, and fall back to headset. + * Bluetooth Headset and Handsfree (v1.5) profiles. * - * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset + *

    BluetoothHeadset is a proxy object for controlling the Bluetooth Headset * Service via IPC. * - * Creating a BluetoothHeadset object will create a binding with the - * BluetoothHeadset service. Users of this object should call close() when they - * are finished with the BluetoothHeadset, so that this proxy object can unbind - * from the service. - * - * This BluetoothHeadset object is not immediately bound to the - * BluetoothHeadset service. Use the ServiceListener interface to obtain a - * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetoothHeadset after construction. + *

    Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHeadset proxy object. Use + * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. * - * Android only supports one connected Bluetooth Headset at a time. - * - * @hide + *

    Android only supports one connected Bluetooth Headset at a time. + * Each method is protected with its appropriate permission. */ -public final class BluetoothHeadset { - +public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = false; + /** + * Intent used to broadcast the change in connection state of the Headset + * profile. + * + *

    This intent will have 3 extras: + * {@link #EXTRA_STATE} - The current state of the profile. + * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + * + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_STATE_CHANGED = - "android.bluetooth.headset.action.STATE_CHANGED"; + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; + /** - * TODO(API release): Consider incorporating as new state in - * HEADSET_STATE_CHANGED + * Intent used to broadcast the change in the Audio Connection state of the + * A2DP profile. + * + *

    This intent will have 3 extras: + * {@link #EXTRA_STATE} - The current state of the profile. + * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + * + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.headset.action.AUDIO_STATE_CHANGED"; - public static final String EXTRA_STATE = - "android.bluetooth.headset.extra.STATE"; - public static final String EXTRA_PREVIOUS_STATE = - "android.bluetooth.headset.extra.PREVIOUS_STATE"; - public static final String EXTRA_AUDIO_STATE = - "android.bluetooth.headset.extra.AUDIO_STATE"; - - /** Extra to be used with the Headset State change intent. - * This will be used only when Headset state changes to - * {@link #STATE_DISCONNECTED} from any previous state. - * This extra field is optional and will be used when - * we have deterministic information regarding whether - * the disconnect was initiated by the remote device or - * by the local adapter. - */ - public static final String EXTRA_DISCONNECT_INITIATOR = - "android.bluetooth.headset.extra.DISCONNECT_INITIATOR"; + "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; + /** * Broadcast Action: Indicates a headset has posted a vendor-specific event. @@ -124,100 +129,47 @@ public final class BluetoothHeadset { public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; - - /** - * TODO(API release): Consider incorporating as new state in - * HEADSET_STATE_CHANGED + /* + * Headset state when SCO audio is connected + * This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ - private IBluetoothHeadset mService; - private final Context mContext; - private final ServiceListener mServiceListener; - - /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; - /** No headset currently connected */ - public static final int STATE_DISCONNECTED = 0; - /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; - /** A headset is currently connected */ - public static final int STATE_CONNECTED = 2; - - /** A SCO audio channel is not established */ - public static final int AUDIO_STATE_DISCONNECTED = 0; - /** A SCO audio channel is established */ - public static final int AUDIO_STATE_CONNECTED = 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; - - /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */ - public static final int REMOTE_DISCONNECT = 0; - public static final int LOCAL_DISCONNECT = 1; - - - /** Default priority for headsets that for which we will accept - * inconing connections and auto-connect */ - public static final int PRIORITY_AUTO_CONNECT = 1000; - /** Default priority for headsets that for which we will accept - * inconing connections but not auto-connect */ - public static final int PRIORITY_ON = 100; - /** Default priority for headsets that should not be auto-connected - * and not allow incoming connections. */ - public static final int PRIORITY_OFF = 0; - /** Default priority when not set or when the device is unpaired */ - public static final int PRIORITY_UNDEFINED = -1; + public static final int STATE_AUDIO_CONNECTED = 10; /** - * An interface for notifying BluetoothHeadset IPC clients when they have - * been connected to the BluetoothHeadset service. + * Headset state when SCO audio is NOT connected + * This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ - public interface ServiceListener { - /** - * Called to notify the client when this proxy object has been - * connected to the BluetoothHeadset service. Clients must wait for - * this callback before making IPC calls on the BluetoothHeadset - * service. - */ - public void onServiceConnected(); - - /** - * Called to notify the client that this proxy object has been - * disconnected from the BluetoothHeadset service. Clients must not - * make IPC calls on the BluetoothHeadset service after this callback. - * This callback will currently only occur if the application hosting - * the BluetoothHeadset service, but may be called more often in future. - */ - public void onServiceDisconnected(); - } + public static final int STATE_AUDIO_DISCONNECTED = 11; + + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetoothHeadset mService; + BluetoothAdapter mAdapter; /** * Create a BluetoothHeadset proxy object. */ - public BluetoothHeadset(Context context, ServiceListener l) { + /*package*/ BluetoothHeadset(Context context, ServiceListener l) { mContext = context; mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Headset Service"); } } - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - /** * Close the connection to the backing service. * Other public functions of BluetoothHeadset will return default error * results once close() has been called. Multiple invocations of close() * are ok. */ - public synchronized void close() { + /*package*/ synchronized void close() { if (DBG) log("close()"); if (mConnection != null) { mContext.unbindService(mConnection); @@ -226,190 +178,212 @@ public final class BluetoothHeadset { } /** - * Get the current state of the Bluetooth Headset service. - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Headset service. + * {@inheritDoc} + * @hide */ - public int getState(BluetoothDevice device) { - if (DBG) log("getState()"); - if (mService != null) { + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.getState(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 mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } } - return BluetoothHeadset.STATE_ERROR; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get the BluetoothDevice for the current headset. - * @return current headset, or null if not in connected or connecting - * state, or if this proxy object is not connected to the Headset - * service. + * {@inheritDoc} + * @hide */ - public BluetoothDevice getCurrentHeadset() { - if (DBG) log("getCurrentHeadset()"); - if (mService != null) { + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.getCurrentHeadset(); - } 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 mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } } - return null; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Request to initiate a connection to a headset. - * This call does not block. Fails if a headset is already connecting - * or connected. - * Initiates auto-connection if device is null. Tries to connect to all - * devices with priority greater than PRIORITY_AUTO in descending order. - * @param device device to connect to, or null to auto-connect last connected - * headset - * @return false if there was a problem initiating the connection - * procedure, and no further HEADSET_STATE_CHANGED intents - * will be expected. + * {@inheritDoc} */ - public boolean connectHeadset(BluetoothDevice device) { - if (DBG) log("connectHeadset(" + device + ")"); - if (mService != null) { + public Set getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { try { - if (mService.connectHeadset(device)) { - return true; - } - } 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 toDeviceSet(mService.getConnectedDevices()); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return toDeviceSet(new BluetoothDevice[0]); + } } - return false; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return toDeviceSet(new BluetoothDevice[0]); } /** - * Returns true if the specified headset is connected (does not include - * connecting). Returns false if not connected, or if this proxy object - * if not currently connected to the headset service. + * {@inheritDoc} */ - public boolean isConnected(BluetoothDevice device) { - if (DBG) log("isConnected(" + device + ")"); - if (mService != null) { + public Set getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { 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 toDeviceSet(mService.getDevicesMatchingConnectionStates(states)); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return toDeviceSet(new BluetoothDevice[0]); + } } - return false; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return toDeviceSet(new BluetoothDevice[0]); } /** - * Disconnects the current headset. Currently this call blocks, it may soon - * be made asynchornous. Returns false if this proxy object is - * not currently connected to the Headset service. + * {@inheritDoc} */ - public boolean disconnectHeadset(BluetoothDevice device) { - if (DBG) log("disconnectHeadset()"); - if (mService != null) { + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getConnectionState(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - mService.disconnectHeadset(device); - return true; - } 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 mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } } - return false; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; } /** - * Start BT Voice Recognition mode, and set up Bluetooth audio path. - * Returns false if there is no headset connected, or if the - * connected headset does not support voice recognition, or on - * error. + * {@inheritDoc} + * @hide */ - public boolean startVoiceRecognition() { - if (DBG) log("startVoiceRecognition()"); - if (mService != null) { + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } try { - return mService.startVoiceRecognition(); - } 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 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; } /** - * Stop BT Voice Recognition mode, and shut down Bluetooth audio path. - * Returns false if there is no headset connected, or the connected - * headset is not in voice recognition mode, or on error. + * {@inheritDoc} + * @hide */ - public boolean stopVoiceRecognition() { - if (DBG) log("stopVoiceRecognition()"); - if (mService != null) { + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.stopVoiceRecognition(); - } 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 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; + } + + /** + * Start Bluetooth voice recognition. This methods sends the voice + * recognition AT command to the headset and establishes the + * audio connection. + * + *

    Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED} + * when the audio connection is established. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device Bluetooth headset + * @return false if there is no headset connected of if the + * connected headset doesn't support voice recognition + * or on error, true otherwise + */ + public boolean startVoiceRecognition(BluetoothDevice device) { + if (DBG) log("startVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.startVoiceRecognition(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } /** - * Set priority of headset. - * Priority is a non-negative integer. By default paired headsets will have - * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0). - * Headsets with priority greater than zero will be auto-connected, and - * incoming connections will be accepted (if no other headset is - * connected). - * Auto-connection occurs at the following events: boot, incoming phone - * call, outgoing phone call. - * Headsets with priority equal to zero, or that are unpaired, are not - * auto-connected. - * Incoming connections are ignored regardless of priority if there is - * already a headset connected. - * @param device paired headset - * @param priority Integer priority, for example PRIORITY_AUTO or - * PRIORITY_NONE - * @return true if successful, false if there was some error + * Stop Bluetooth Voice Recognition mode, and shut down the + * Bluetooth audio path. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device Bluetooth headset + * @return false if there is no headset connected + * or on error, true otherwise */ - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null) { + public boolean stopVoiceRecognition(BluetoothDevice device) { + if (DBG) log("stopVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.setPriority(device, priority); - } 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 mService.stopVoiceRecognition(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 priority of headset. - * @param device headset - * @return non-negative priority, or negative error code on error + * Check if Bluetooth SCO audio is connected. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device Bluetooth headset + * @return true if SCO is connected, + * false otherwise or on error */ - public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); - if (mService != null) { + public boolean isAudioConnected(BluetoothDevice device) { + if (DBG) log("isAudioConnected()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.getPriority(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 mService.isAudioConnected(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } } - return -1; + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** @@ -420,24 +394,29 @@ public final class BluetoothHeadset { * boot. This is a good indicator for spammy headset/handsfree units that * can keep the device awake by polling for cellular status updates. As a * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms + * + * @param device the bluetooth headset. * @return monotonically increasing battery usage hint, or a negative error * code on error * @hide */ - public int getBatteryUsageHint() { + public int getBatteryUsageHint(BluetoothDevice device) { if (DBG) log("getBatteryUsageHint()"); - if (mService != null) { + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - return mService.getBatteryUsageHint(); - } 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 mService.getBatteryUsageHint(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); return -1; } + /** * Indicates if current platform supports voice dialing over bluetooth SCO. + * * @return true if voice dialing over bluetooth is supported, false otherwise. * @hide */ @@ -448,11 +427,13 @@ public final class BluetoothHeadset { /** * Cancel the outgoing connection. + * Note: This is an internal function and shouldn't be exposed + * * @hide */ public boolean cancelConnectThread() { if (DBG) log("cancelConnectThread"); - if (mService != null) { + if (mService != null && isEnabled()) { try { return mService.cancelConnectThread(); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -465,11 +446,13 @@ public final class BluetoothHeadset { /** * Accept the incoming connection. + * Note: This is an internal function and shouldn't be exposed + * * @hide */ public boolean acceptIncomingConnect(BluetoothDevice device) { if (DBG) log("acceptIncomingConnect"); - if (mService != null) { + if (mService != null && isEnabled()) { try { return mService.acceptIncomingConnect(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -481,12 +464,14 @@ public final class BluetoothHeadset { } /** - * Create the connect thread the incoming connection. + * Create the connect thread for the incoming connection. + * Note: This is an internal function and shouldn't be exposed + * * @hide */ public boolean createIncomingConnect(BluetoothDevice device) { if (DBG) log("createIncomingConnect"); - if (mService != null) { + if (mService != null && isEnabled()) { try { return mService.createIncomingConnect(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -500,11 +485,12 @@ public final class BluetoothHeadset { /** * Connect to a Bluetooth Headset. * Note: This is an internal function and shouldn't be exposed + * * @hide */ public boolean connectHeadsetInternal(BluetoothDevice device) { if (DBG) log("connectHeadsetInternal"); - if (mService != null) { + if (mService != null && isEnabled()) { try { return mService.connectHeadsetInternal(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -518,11 +504,12 @@ public final class BluetoothHeadset { /** * Disconnect a Bluetooth Headset. * Note: This is an internal function and shouldn't be exposed + * * @hide */ public boolean disconnectHeadsetInternal(BluetoothDevice device) { if (DBG) log("disconnectHeadsetInternal"); - if (mService != null) { + if (mService != null && isEnabled()) { try { return mService.disconnectHeadsetInternal(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -532,23 +519,61 @@ public final class BluetoothHeadset { } return false; } + + /** + * Set the audio state of the Headset. + * Note: This is an internal function and shouldn't be exposed + * + * @hide + */ + public boolean setAudioState(BluetoothDevice device, int state) { + if (DBG) log("setAudioState"); + if (mService != null && isEnabled()) { + try { + return mService.setAudioState(device, state); + } 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; + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); + if (mServiceListener != null) { - mServiceListener.onServiceConnected(); + mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this); } } public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(); + mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); } } }; + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private Set toDeviceSet(BluetoothDevice[] devices) { + return Collections.unmodifiableSet( + new HashSet(Arrays.asList(devices))); + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java new file mode 100644 index 00000000000..3b4c84c9ea5 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 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.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +import java.util.Set; + +/** + * Public APIs for the Bluetooth Profiles. + * + *

    Clients should call {@link BluetoothAdapter#getProfileProxy}, + * to get the Profile Proxy. Each public profile implements this + * interface. + */ +public interface BluetoothProfile { + + /** + * Extra for the connection state intents of the individual profiles. + * + * This extra represents the current connection state of the profile of the + * Bluetooth device. + */ + public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; + + /** + * Extra for the connection state intents of the individual profiles. + * + * This extra represents the previous connection state of the profile of the + * Bluetooth device. + */ + public static final String EXTRA_PREVIOUS_STATE = + "android.bluetooth.profile.extra.PREVIOUS_STATE"; + + /** The profile is in disconnected state */ + public static final int STATE_DISCONNECTED = 0; + /** The profile is in connecting state */ + public static final int STATE_CONNECTING = 1; + /** The profile is in connected state */ + public static final int STATE_CONNECTED = 2; + /** The profile is in disconnecting state */ + public static final int STATE_DISCONNECTING = 3; + + /** + * Headset and Handsfree profile + */ + public static final int HEADSET = 1; + /** + * A2DP profile. + */ + public static final int A2DP = 2; + + /** + * Default priority for devices that we try to auto-connect to and + * and allow incoming connections for the profile + * @hide + **/ + public static final int PRIORITY_AUTO_CONNECT = 1000; + + /** + * Default priority for devices that allow incoming + * and outgoing connections for the profile + * @hide + **/ + public static final int PRIORITY_ON = 100; + + /** + * Default priority for devices that does not allow incoming + * connections and outgoing connections for the profile. + * @hide + **/ + public static final int PRIORITY_OFF = 0; + + /** + * Default priority when not set or when the device is unpaired + * @hide + * */ + public static final int PRIORITY_UNDEFINED = -1; + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + *

    Currently, the system supports only 1 connection to the + * A2DP and Headset/Handsfree profile. The API will automatically + * disconnect connected devices before connecting. + * + *

    This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean connect(BluetoothDevice device); + + /** + * Initiate disconnection from a profile + * + *

    This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

    If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device); + + /** + * Get connected devices for this specific profile. + * + *

    Return the set of devices which are in state {@link #STATE_CONNECTED} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return An unmodifiable set of devices. The set will be empty on error. + */ + public Set getConnectedDevices(); + + /** + * Get a set of devices that match any of the given connection + * states. + * + *

    If none of devices match any of the given states, + * an empty set will be returned. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return An unmodifiable set of devices. The set will be empty on error. + */ + public Set getDevicesMatchingConnectionStates(int[] states); + + /** + * Get the current connection state of the profile + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + public int getConnectionState(BluetoothDevice device); + + /** + * Set priority of the profile + * + *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority); + + /** + * 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} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + public int getPriority(BluetoothDevice device); + + /** + * An interface for notifying BluetoothProfile IPC clients when they have + * been connected or disconnected to the service. + */ + public interface ServiceListener { + /** + * Called to notify the client when the proxy object has been + * connected to the service. + * @param profile - One of {@link #HEADSET} or + * {@link #A2DP} + * @param proxy - One of {@link BluetoothHeadset} or + * {@link BluetoothA2dp} + */ + public void onServiceConnected(int profile, BluetoothProfile proxy); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the service. + * @param profile - One of {@link #HEADSET} or + * {@link #A2DP} + */ + public void onServiceDisconnected(int profile); + } +} -- GitLab From dae21e755316c579f805428af0e7d4c3af25bf1d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 27 Sep 2010 17:02:01 -0700 Subject: [PATCH 0117/1408] Update code for new BT public APIs. Change-Id: Id730fb6226db59f3a0416111c4790653c2fccb0b --- .../BluetoothDeviceProfileState.java | 107 ++++++++++-------- .../bluetooth/BluetoothProfileState.java | 20 ++-- .../android/bluetooth/IBluetoothA2dp.aidl | 24 ++-- .../android/bluetooth/IBluetoothHeadset.aidl | 25 ++-- 4 files changed, 97 insertions(+), 79 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 1fd71518dee..e460839deff 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Message; +import android.bluetooth.BluetoothAdapter; import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.util.Log; @@ -28,6 +29,8 @@ import android.util.Log; import com.android.internal.util.HierarchicalState; import com.android.internal.util.HierarchicalStateMachine; +import java.util.Set; + /** * This class is the Profile connection state machine associated with a remote * device. When the device bonds an instance of this class is created. @@ -91,12 +94,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothService mService; private BluetoothA2dpService mA2dpService; private BluetoothHeadset mHeadsetService; - private boolean mHeadsetServiceConnected; private BluetoothDevice mDevice; - private int mHeadsetState; - private int mA2dpState; - private int mHidState; + private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED; + private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED; + private int mHidState = BluetoothProfile.STATE_DISCONNECTED; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -105,32 +107,29 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (!device.equals(mDevice)) return; - if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0); - int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0); - int initiator = intent.getIntExtra( - BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR, - BluetoothHeadset.LOCAL_DISCONNECT); - mHeadsetState = newState; - if (newState == BluetoothHeadset.STATE_DISCONNECTED && - initiator == BluetoothHeadset.REMOTE_DISCONNECT) { - sendMessage(DISCONNECT_HFP_INCOMING); + if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); + mA2dpState = newState; + if (oldState == BluetoothA2dp.STATE_CONNECTED && + newState == BluetoothA2dp.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_A2DP_INCOMING); } - if (newState == BluetoothHeadset.STATE_CONNECTED || - newState == BluetoothHeadset.STATE_DISCONNECTED) { + if (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); - int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0); - mA2dpState = newState; - if ((oldState == BluetoothA2dp.STATE_CONNECTED || - oldState == BluetoothA2dp.STATE_PLAYING) && - newState == BluetoothA2dp.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_A2DP_INCOMING); + } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); + + mHeadsetState = newState; + if (oldState == BluetoothHeadset.STATE_CONNECTED && + newState == BluetoothHeadset.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HFP_INCOMING); } - if (newState == BluetoothA2dp.STATE_CONNECTED || - newState == BluetoothA2dp.STATE_DISCONNECTED) { + if (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { @@ -192,31 +191,31 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine IntentFilter filter = new IntentFilter(); // Fine-grained state broadcasts - filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); - filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); - HeadsetServiceListener l = new HeadsetServiceListener(); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, + BluetoothProfile.HEADSET); } - private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener { - public HeadsetServiceListener() { - mHeadsetService = new BluetoothHeadset(mContext, this); - } - public void onServiceConnected() { + private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = + new BluetoothProfile.ServiceListener() { + public void onServiceConnected(int profile, BluetoothProfile proxy) { synchronized(BluetoothDeviceProfileState.this) { - mHeadsetServiceConnected = true; + mHeadsetService = (BluetoothHeadset) proxy; } } - public void onServiceDisconnected() { + public void onServiceDisconnected(int profile) { synchronized(BluetoothDeviceProfileState.this) { - mHeadsetServiceConnected = false; + mHeadsetService = null; } } - } + }; private class BondedDevice extends HierarchicalState { @Override @@ -276,19 +275,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine if (isPhoneDocked(mDevice)) { // Don't auto connect to docks. break; - } else if (!mHeadsetServiceConnected) { + } else if (mHeadsetService == null) { deferMessage(message); } else { if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT && - !mHeadsetService.isConnected(mDevice)) { - mHeadsetService.connectHeadset(mDevice); + mHeadsetService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING}).size() == 0) { + mHeadsetService.connect(mDevice); } if (mA2dpService != null && - mA2dpService.getSinkPriority(mDevice) == + mA2dpService.getPriority(mDevice) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && - mA2dpService.getConnectedSinks().length == 0) { - mA2dpService.connectSink(mDevice); + mA2dpService.getDevicesMatchingConnectionStates( + new int[] {BluetoothA2dp.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING}).length == 0) { + mA2dpService.connect(mDevice); } if (mService.getInputDevicePriority(mDevice) == BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { @@ -805,7 +810,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine synchronized void cancelCommand(int command) { if (command == CONNECT_HFP_OUTGOING ) { // Cancel the outgoing thread. - if (mHeadsetServiceConnected) { + if (mHeadsetService != null) { mHeadsetService.cancelConnectThread(); } // HeadsetService is down. Phone process most likely crashed. @@ -823,12 +828,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine log("Processing command:" + command); switch(command) { case CONNECT_HFP_OUTGOING: - if (mHeadsetService != null) { + if (mHeadsetService == null) { + deferHeadsetMessage(command); + } else { return mHeadsetService.connectHeadsetInternal(mDevice); } break; case CONNECT_HFP_INCOMING: - if (!mHeadsetServiceConnected) { + if (mHeadsetService == null) { deferHeadsetMessage(command); } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { return mHeadsetService.acceptIncomingConnect(mDevice); @@ -849,7 +856,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HID_INCOMING: return true; case DISCONNECT_HFP_OUTGOING: - if (!mHeadsetServiceConnected) { + if (mHeadsetService == null) { deferHeadsetMessage(command); } else { if (mHeadsetService.getPriority(mDevice) == @@ -867,9 +874,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return true; case DISCONNECT_A2DP_OUTGOING: if (mA2dpService != null) { - if (mA2dpService.getSinkPriority(mDevice) == + if (mA2dpService.getPriority(mDevice) == BluetoothA2dp.PRIORITY_AUTO_CONNECT) { - mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON); + mA2dpService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); } return mA2dpService.disconnectSinkInternal(mDevice); } diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index ad70d0de0ef..7f42baf78e3 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -59,16 +59,16 @@ public class BluetoothProfileState extends HierarchicalStateMachine { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0); - if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED || - newState == BluetoothHeadset.STATE_DISCONNECTED)) { + if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); - if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED || - newState == BluetoothA2dp.STATE_DISCONNECTED)) { + } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { @@ -89,8 +89,8 @@ public class BluetoothProfileState extends HierarchicalStateMachine { setInitialState(mStableState); IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); - filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); context.registerReceiver(mBroadcastReceiver, filter); } diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 40f10583b63..c5044c23c7e 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -19,21 +19,25 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; /** - * System private API for Bluetooth A2DP service + * APIs for Bluetooth A2DP service * - * {@hide} + * @hide */ interface IBluetoothA2dp { - boolean connectSink(in BluetoothDevice device); - boolean disconnectSink(in BluetoothDevice device); + // Public API + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + // change to Set<> once AIDL supports + BluetoothDevice[] getConnectedDevices(); + BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + boolean isA2dpPlaying(in BluetoothDevice device); + + // Internal APIs boolean suspendSink(in BluetoothDevice device); boolean resumeSink(in BluetoothDevice device); - BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports - BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports - int getSinkState(in BluetoothDevice device); - boolean setSinkPriority(in BluetoothDevice device, int priority); - int getSinkPriority(in BluetoothDevice device); - boolean connectSinkInternal(in BluetoothDevice device); boolean disconnectSinkInternal(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index d96f0ca0de8..8bcf10335f6 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -19,25 +19,32 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; /** - * System private API for Bluetooth Headset service + * API for Bluetooth Headset service * * {@hide} */ interface IBluetoothHeadset { - int getState(in BluetoothDevice device); - BluetoothDevice getCurrentHeadset(); - boolean connectHeadset(in BluetoothDevice device); - void disconnectHeadset(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - boolean startVoiceRecognition(); - boolean stopVoiceRecognition(); + // Public API + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + // Change to Set<> when AIDL supports + BluetoothDevice[] getConnectedDevices(); + BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); - int getBatteryUsageHint(); + boolean startVoiceRecognition(in BluetoothDevice device); + boolean stopVoiceRecognition(in BluetoothDevice device); + boolean isAudioConnected(in BluetoothDevice device); + // APIs that can be made public in future + int getBatteryUsageHint(in BluetoothDevice device); + + // Internal functions, not be made public boolean createIncomingConnect(in BluetoothDevice device); boolean acceptIncomingConnect(in BluetoothDevice device); boolean cancelConnectThread(); boolean connectHeadsetInternal(in BluetoothDevice device); boolean disconnectHeadsetInternal(in BluetoothDevice device); + boolean setAudioState(in BluetoothDevice device, int state); } -- GitLab From 04c7138c7c1f36d54fbe33c40644e197248166b9 Mon Sep 17 00:00:00 2001 From: Jake Hamby Date: Tue, 21 Sep 2010 13:39:53 -0700 Subject: [PATCH 0118/1408] Typo fixes in comments and minor code cleanups. * Fix some typos in Javadoc and log messages. * Remove redundant initializer in BluetoothAdapter.readOutOfBandData() * Use canonical "UTF-8" charset name instead of "UTF8" in BluetoothDevice.convertPinToBytes() Change-Id: I58cd5dc48a7ad0053d204c5f590b4b3d438d8672 --- .../android/bluetooth/AtCommandResult.java | 2 -- .../java/android/bluetooth/AtParser.java | 23 +++++++-------- .../android/bluetooth/BluetoothAdapter.java | 8 +++--- .../bluetooth/BluetoothAudioGateway.java | 2 +- .../android/bluetooth/BluetoothClass.java | 4 +-- .../android/bluetooth/BluetoothDevice.java | 28 +++++++++---------- .../bluetooth/BluetoothDevicePicker.java | 3 +- .../android/bluetooth/BluetoothHeadset.java | 14 +++++----- .../java/android/bluetooth/BluetoothPbap.java | 2 +- .../bluetooth/BluetoothServerSocket.java | 6 ++-- .../android/bluetooth/BluetoothSocket.java | 8 +++--- .../java/android/bluetooth/HeadsetBase.java | 4 +-- 12 files changed, 50 insertions(+), 54 deletions(-) diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java index 638be2d2e99..375a6ddba86 100644 --- a/framework/java/android/bluetooth/AtCommandResult.java +++ b/framework/java/android/bluetooth/AtCommandResult.java @@ -16,8 +16,6 @@ package android.bluetooth; -import java.util.*; - /** * The result of execution of an single AT command.

    * diff --git a/framework/java/android/bluetooth/AtParser.java b/framework/java/android/bluetooth/AtParser.java index 1ea31503bb7..328fb2bbd59 100644 --- a/framework/java/android/bluetooth/AtParser.java +++ b/framework/java/android/bluetooth/AtParser.java @@ -16,16 +16,13 @@ package android.bluetooth; -import android.bluetooth.AtCommandHandler; -import android.bluetooth.AtCommandResult; - import java.util.*; /** * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard. *

    * - * Conforment with the subset of V.250 required for implementation of the + * Conformant with the subset of V.250 required for implementation of the * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP * specifications. Also implements some V.250 features not required by * Bluetooth - such as chained commands.

    @@ -48,7 +45,7 @@ import java.util.*; * are no arguments for get commands. *

  • Set Command. For example "AT+VGM=14". The command name is "VGM", and * there is a single integer argument in this case. In the general case then - * can be zero or more arguments (comma deliminated) each of integer or string + * can be zero or more arguments (comma delimited) each of integer or string * form. *
  • Test Command. For example "AT+VGM=?. No arguments. * @@ -60,7 +57,7 @@ import java.util.*; * headset/handsfree use this is acceptable, because they only use the basic * commands ATA and ATD, which are not allowed to be chained. For general V.250 * use we would need to improve this class to allow Basic command chaining - - * however its tricky to get right becuase there is no deliminator for Basic + * however it's tricky to get right because there is no delimiter for Basic * command chaining.

    * * Extended commands can be chained. For example:

    @@ -71,7 +68,7 @@ import java.util.*; * AT+CIMI * Except that only one final result code is return (although several * intermediate responses may be returned), and as soon as one command in the - * chain fails the rest are abandonded.

    + * chain fails the rest are abandoned.

    * * Handlers are registered by there command name via register(Char c, ...) or * register(String s, ...). Handlers for Basic command should be registered by @@ -80,7 +77,7 @@ import java.util.*; * * Refer to:

      *
    • ITU-T Recommendation V.250 - *
    • ETSI TS 127.007 (AT Comannd set for User Equipment, 3GPP TS 27.007) + *
    • ETSI TS 127.007 (AT Command set for User Equipment, 3GPP TS 27.007) *
    • Bluetooth Headset Profile Spec (K6) *
    • Bluetooth Handsfree Profile Spec (HFP 1.5) *
    @@ -188,7 +185,7 @@ public class AtParser { } /** - * Break an argument string into individual arguments (comma deliminated). + * Break an argument string into individual arguments (comma delimited). * Integer arguments are turned into Integer objects. Otherwise a String * object is used. */ @@ -212,7 +209,7 @@ public class AtParser { } /** - * Return the index of the end of character after the last characeter in + * Return the index of the end of character after the last character in * the extended command name. Uses the V.250 spec for allowed command * names. */ @@ -244,7 +241,7 @@ public class AtParser { * Processes an incoming AT command line.

    * This method will invoke zero or one command handler methods for each * command in the command line.

    - * @param raw_input The AT input, without EOL deliminator (e.g. ). + * @param raw_input The AT input, without EOL delimiter (e.g. ). * @return Result object for this command line. This can be * converted to a String[] response with toStrings(). */ @@ -297,8 +294,8 @@ public class AtParser { if (c == '+') { // Option 2: Extended Command - // Search for first non-name character. Shortcircuit if we dont - // handle this command name. + // Search for first non-name character. Short-circuit if + // we don't handle this command name. int i = findEndExtendedName(input, index + 1); String commandName = input.substring(index, i); if (!mExtHandlers.containsKey(commandName)) { diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 33fd39513f5..3040319bebe 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -285,7 +285,7 @@ public final class BluetoothAdapter { private static final int ADDRESS_LENGTH = 17; /** - * Lazyily initialized singleton. Guaranteed final after first object + * Lazily initialized singleton. Guaranteed final after first object * constructed. */ private static BluetoothAdapter sAdapter; @@ -410,7 +410,7 @@ public final class BluetoothAdapter { * user action to turn off Bluetooth. *

    This gracefully shuts down all Bluetooth connections, stops Bluetooth * system services, and powers down the underlying Bluetooth hardware. - *

    Bluetooth should never be disbled without + *

    Bluetooth should never be disabled without * direct user consent. The {@link #disable()} method is * provided only for applications that include a user interface for changing * system settings, such as a "power manager" app.

    @@ -876,8 +876,8 @@ public final class BluetoothAdapter { public Pair readOutOfBandData() { if (getState() != STATE_ON) return null; try { - byte[] hash = new byte[16]; - byte[] randomizer = new byte[16]; + byte[] hash; + byte[] randomizer; byte[] ret = mService.readOutOfBandData(); diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java index bc3206042e1..93513937285 100644 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ b/framework/java/android/bluetooth/BluetoothAudioGateway.java @@ -23,7 +23,7 @@ import android.os.Handler; import android.util.Log; /** - * Listen's for incoming RFCOMM connection for the headset / handsfree service. + * Listens for incoming RFCOMM connection for the headset / handsfree service. * * TODO: Use the new generic BluetoothSocket class instead of this legacy code * diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index c7fea9e1dad..6a878d73e2e 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -34,8 +34,8 @@ import android.os.Parcelable; * Bluetooth profiles or services are actually supported by a device. Accurate * service discovery is done through SDP requests, which are automatically * performed when creating an RFCOMM socket with {@link - * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link - * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}

    + * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord}

    * *

    Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for * a remote device. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e577ec4f737..ada3c24e5d0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -32,7 +32,7 @@ import java.util.UUID; /** * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you - * create a connection with the repective device or query information about + * create a connection with the respective device or query information about * it, such as the name, address, class, and bonding state. * *

    This class is really just a thin wrapper for a Bluetooth hardware @@ -48,7 +48,7 @@ import java.util.UUID; * {@link BluetoothAdapter}) or get one from the set of bonded devices * returned by {@link BluetoothAdapter#getBondedDevices() * BluetoothAdapter.getBondedDevices()}. You can then open a - * {@link BluetoothSocket} for communciation with the remote device, using + * {@link BluetoothSocket} for communication with the remote device, using * {@link #createRfcommSocketToServiceRecord(UUID)}. * *

    Note: @@ -226,8 +226,8 @@ public final class BluetoothDevice implements Parcelable { *

    A shared link keys exists locally for the remote device, so * communication can be authenticated and encrypted. *

    Being bonded (paired) with a remote device does not necessarily - * mean the device is currently connected. It just means that the ponding - * procedure was compeleted at some earlier time, and the link key is still + * mean the device is currently connected. It just means that the pending + * procedure was completed at some earlier time, and the link key is still * stored locally, ready to use on the next connection. * */ @@ -283,7 +283,7 @@ public final class BluetoothDevice implements Parcelable { * not respond to pin request in time * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; - /** A bond attempt failed because the other side explicilty rejected + /** A bond attempt failed because the other side explicitly rejected * bonding * @hide */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; @@ -515,7 +515,7 @@ public final class BluetoothDevice implements Parcelable { * Cancel an in-progress bonding request started with {@link #createBond}. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @return true on sucess, false on error + * @return true on success, false on error * @hide */ public boolean cancelBondProcess() { @@ -532,7 +532,7 @@ public final class BluetoothDevice implements Parcelable { * authentication and encryption. *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @return true on sucess, false on error + * @return true on success, false on error * @hide */ public boolean removeBond() { @@ -617,7 +617,7 @@ public final class BluetoothDevice implements Parcelable { * with the UUIDs supported by the remote end. If there is an error * in getting the SDP records or if the process takes a long time, * an Intent is sent with the UUIDs that is currently present in the - * cache. Clients should use the {@link getUuids} to get UUIDs + * cache. Clients should use the {@link #getUuids} to get UUIDs * is SDP is not to be performed. * * @return False if the sanity check fails, True if the process @@ -693,7 +693,7 @@ public final class BluetoothDevice implements Parcelable { * outgoing connection to this remote device on given channel. *

    The remote device will be authenticated and communication on this * socket will be encrypted. - *

    Use {@link BluetoothSocket#connect} to intiate the outgoing + *

    Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

    Valid RFCOMM channels are in range 1 to 30. *

    Requires {@link android.Manifest.permission#BLUETOOTH} @@ -715,7 +715,7 @@ public final class BluetoothDevice implements Parcelable { *

    This is designed to be used with {@link * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer * Bluetooth applications. - *

    Use {@link BluetoothSocket#connect} to intiate the outgoing + *

    Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. This will also perform an SDP lookup of the given uuid to * determine which channel to connect to. *

    The remote device will be authenticated and communication on this @@ -772,9 +772,9 @@ public final class BluetoothDevice implements Parcelable { /** * Check that a pin is valid and convert to byte array. * - * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. + * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters. * @param pin pin as java String - * @return the pin code as a UTF8 byte array, or null if it is an invalid + * @return the pin code as a UTF-8 byte array, or null if it is an invalid * Bluetooth pin. * @hide */ @@ -784,9 +784,9 @@ public final class BluetoothDevice implements Parcelable { } byte[] pinBytes; try { - pinBytes = pin.getBytes("UTF8"); + pinBytes = pin.getBytes("UTF-8"); } catch (UnsupportedEncodingException uee) { - Log.e(TAG, "UTF8 not supported?!?"); // this should not happen + Log.e(TAG, "UTF-8 not supported?!?"); // this should not happen return null; } if (pinBytes.length <= 0 || pinBytes.length > 16) { diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java index 05eed0e6de6..3a07033eab7 100644 --- a/framework/java/android/bluetooth/BluetoothDevicePicker.java +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -36,7 +36,8 @@ public interface BluetoothDevicePicker { /** * Broadcast when one BT device is selected from BT device picker screen. - * Selected BT device address is contained in extra string {@link BluetoothIntent} + * Selected {@link BluetoothDevice} is returned in extra data named + * {@link BluetoothDevice#EXTRA_DEVICE}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DEVICE_SELECTED = diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 4a91a8c9072..197b022cad0 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -45,7 +45,7 @@ import android.util.Log; * This BluetoothHeadset object is not immediately bound to the * BluetoothHeadset service. Use the ServiceListener interface to obtain a * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetootHeadset after construction. + * immediately call methods on BluetoothHeadset after construction. * * Android only supports one connected Bluetooth Headset at a time. * @@ -108,7 +108,7 @@ public final class BluetoothHeadset { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completetion. */ + /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */ @@ -116,11 +116,11 @@ public final class BluetoothHeadset { public static final int LOCAL_DISCONNECT = 1; - /** Default priority for headsets that for which we will accept - * inconing connections and auto-connect */ + /** Default priority for headsets for which we will accept + * incoming connections and auto-connect. */ public static final int PRIORITY_AUTO_CONNECT = 1000; - /** Default priority for headsets that for which we will accept - * inconing connections but not auto-connect */ + /** Default priority for headsets for which we will accept + * incoming connections but not auto-connect. */ public static final int PRIORITY_ON = 100; /** Default priority for headsets that should not be auto-connected * and not allow incoming connections. */ @@ -268,7 +268,7 @@ public final class BluetoothHeadset { /** * Disconnects the current headset. Currently this call blocks, it may soon - * be made asynchornous. Returns false if this proxy object is + * be made asynchronous. Returns false if this proxy object is * not currently connected to the Headset service. */ public boolean disconnectHeadset(BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index b48f48e9f19..4be077c0e7b 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -197,7 +197,7 @@ public class BluetoothPbap { /** * Disconnects the current Pbap client (PCE). Currently this call blocks, - * it may soon be made asynchornous. Returns false if this proxy object is + * it may soon be made asynchronous. Returns false if this proxy object is * not currently connected to the Pbap service. */ public boolean disconnect() { diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index c9c6c0acd9a..83e59e266d8 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -29,14 +29,14 @@ import java.io.IOException; * side, use a {@link BluetoothServerSocket} to create a listening server * socket. When a connection is accepted by the {@link BluetoothServerSocket}, * it will return a new {@link BluetoothSocket} to manage the connection. - * On the client side, use a single {@link BluetoothSocket} to both intiate + * On the client side, use a single {@link BluetoothSocket} to both initiate * an outgoing connection and to manage the connection. * *

    The most common type of Bluetooth socket is RFCOMM, which is the type * supported by the Android APIs. RFCOMM is a connection-oriented, streaming * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). * - *

    To create a listenting {@link BluetoothServerSocket} that's ready for + *

    To create a listening {@link BluetoothServerSocket} that's ready for * incoming connections, use * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call @@ -70,7 +70,7 @@ public final class BluetoothServerSocket implements Closeable { * @param encrypt require the connection to be encrypted * @param port remote port * @throws IOException On error, for example Bluetooth not available, or - * insufficient priveleges + * insufficient privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index ad033999e0d..719d730770d 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -35,7 +35,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * side, use a {@link BluetoothServerSocket} to create a listening server * socket. When a connection is accepted by the {@link BluetoothServerSocket}, * it will return a new {@link BluetoothSocket} to manage the connection. - * On the client side, use a single {@link BluetoothSocket} to both intiate + * On the client side, use a single {@link BluetoothSocket} to both initiate * an outgoing connection and to manage the connection. * *

    The most common type of Bluetooth socket is RFCOMM, which is the type @@ -113,7 +113,7 @@ public final class BluetoothSocket implements Closeable { * @param port remote port * @param uuid SDP uuid * @throws IOException On error, for example Bluetooth not available, or - * insufficient priveleges + * insufficient privileges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { @@ -158,7 +158,7 @@ public final class BluetoothSocket implements Closeable { * @param address remote device that this socket can connect to * @param port remote port * @throws IOException On error, for example Bluetooth not available, or - * insufficient priveleges + * insufficient privileges */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { @@ -226,7 +226,7 @@ public final class BluetoothSocket implements Closeable { } // all native calls are guaranteed to immediately return after - // abortNative(), so this lock should immediatley acquire + // abortNative(), so this lock should immediately acquire mLock.writeLock().lock(); try { mClosed = true; diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java index e2935c95d32..22495a7a6c3 100644 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ b/framework/java/android/bluetooth/HeadsetBase.java @@ -89,7 +89,7 @@ public final class HeadsetBase { initializeNativeDataNative(-1); } - /* Create from an already exisiting rfcomm connection */ + /* Create from an already existing rfcomm connection */ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device, int socketFd, int rfcommChannel, Handler handler) { mDirection = DIRECTION_INCOMING; @@ -128,7 +128,7 @@ public final class HeadsetBase { (System.currentTimeMillis() - timestamp) + " ms"); if (result.getResultCode() == AtCommandResult.ERROR) { - Log.i(TAG, "Error pocessing <" + input + ">"); + Log.i(TAG, "Error processing <" + input + ">"); } sendURC(result.toString()); -- GitLab From 3d9d3955c5534df1539ace2963ecc064790d84a9 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 4 Oct 2010 23:20:58 +0200 Subject: [PATCH 0119/1408] DO NOT MERGE: Disconnect PBAP while disconnecting HFP profile. PBAP profile is associated with HFP profile. Disconnect PBAP profile while disconnecting HFP profile. We ever never doing this and were expecting the remote end to do it. So when the user disconnects a headset, we will end up with the ACL link still up because PBAP has not been disconnected. This is little bit of a hack till PBAP profile is converted to use the new profile APIs. Change-Id: I5f68b228fd5e607605928a52343ad4b86c58ac0b --- .../BluetoothDeviceProfileState.java | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 8e655e213d6..df1d9601266 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -67,10 +67,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private static final int DISCONNECT_HFP_INCOMING = 6; public static final int DISCONNECT_A2DP_OUTGOING = 7; public static final int DISCONNECT_A2DP_INCOMING = 8; + public static final int DISCONNECT_PBAP_OUTGOING = 9; - public static final int UNPAIR = 9; - public static final int AUTO_CONNECT_PROFILES = 10; - public static final int TRANSITION_TO_STABLE = 11; + public static final int UNPAIR = 100; + public static final int AUTO_CONNECT_PROFILES = 101; + public static final int TRANSITION_TO_STABLE = 102; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs @@ -84,7 +85,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothService mService; private BluetoothA2dpService mA2dpService; private BluetoothHeadset mHeadsetService; + private BluetoothPbap mPbapService; private boolean mHeadsetServiceConnected; + private boolean mPbapServiceConnected; private BluetoothDevice mDevice; private int mHeadsetState; @@ -176,6 +179,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine mContext.registerReceiver(mBroadcastReceiver, filter); HeadsetServiceListener l = new HeadsetServiceListener(); + PbapServiceListener p = new PbapServiceListener(); } private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener { @@ -194,6 +198,22 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } + private class PbapServiceListener implements BluetoothPbap.ServiceListener { + public PbapServiceListener() { + mPbapService = new BluetoothPbap(mContext, this); + } + public void onServiceConnected() { + synchronized(BluetoothDeviceProfileState.this) { + mPbapServiceConnected = true; + } + } + public void onServiceDisconnected() { + synchronized(BluetoothDeviceProfileState.this) { + mPbapServiceConnected = false; + } + } + } + private class BondedDevice extends HierarchicalState { @Override protected void enter() { @@ -224,6 +244,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: transitionTo(mIncomingA2dp); break; + case DISCONNECT_PBAP_OUTGOING: + processCommand(DISCONNECT_PBAP_OUTGOING); + break; case UNPAIR: if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HFP_OUTGOING); @@ -342,6 +365,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(deferMsg); } break; + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -409,6 +433,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // If this causes incoming HFP to fail, it is more of a headset problem // since both connections are incoming ones. break; + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -496,6 +521,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -561,6 +587,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -588,7 +615,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - synchronized void deferHeadsetMessage(int command) { + synchronized void deferProfileServiceMessage(int command) { Message msg = new Message(); msg.what = command; deferMessage(msg); @@ -604,7 +631,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine break; case CONNECT_HFP_INCOMING: if (!mHeadsetServiceConnected) { - deferHeadsetMessage(command); + deferProfileServiceMessage(command); } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { return mHeadsetService.acceptIncomingConnect(mDevice); } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { @@ -621,8 +648,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return true; case DISCONNECT_HFP_OUTGOING: if (!mHeadsetServiceConnected) { - deferHeadsetMessage(command); + deferProfileServiceMessage(command); } else { + // Disconnect PBAP + // TODO(): Add PBAP to the state machine. + Message m = new Message(); + m.what = DISCONNECT_PBAP_OUTGOING; + deferMessage(m); if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT) { mHeadsetService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); @@ -645,6 +677,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return mA2dpService.disconnectSinkInternal(mDevice); } break; + case DISCONNECT_PBAP_OUTGOING: + if (!mPbapServiceConnected) { + deferProfileServiceMessage(command); + } else { + return mPbapService.disconnect(); + } + break; case UNPAIR: return mService.removeBondInternal(mDevice.getAddress()); default: -- GitLab From 3241755b01de6ce4da1647c581e823f5869d67b7 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 12 Oct 2010 16:58:06 -0700 Subject: [PATCH 0120/1408] Bluez doesn't handle disconnect while connecting well. Defer the message. Bug: 3076404 Change-Id: Id62e1008346f492a85e714240a832a061def1786 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index df1d9601266..ff82d39fc49 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -516,7 +516,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case DISCONNECT_A2DP_OUTGOING: - processCommand(DISCONNECT_A2DP_OUTGOING); + deferMessage(message); break; case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez -- GitLab From 563cd2eaf500acdb65f9dec8231758239871d857 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 13 Oct 2010 15:54:30 -0700 Subject: [PATCH 0121/1408] Work around for errant headsets. 1. Some headsets send an AVCTP connection before an AVDTP connection When that AVCTP connection fails, we get stuck in IncomingA2DP state because we don't handle AVCTP signals for now. We need to handle the signals and fix it better. 2. Also when ACL gets disconnected, reset state i.e when the low level connection disconnected reset our state. This is like a fail safe in case we get stuck. Bug: 3051490 Change-Id: Ibcf188103999ffb1e08c36c2554504c639fb7f50 --- .../BluetoothDeviceProfileState.java | 29 ++++++++++++++++--- .../bluetooth/BluetoothProfileState.java | 13 ++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index ff82d39fc49..17b28d1a809 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -136,6 +136,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Message msg = new Message(); msg.what = AUTO_CONNECT_PROFILES; sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { + // This is technically not needed, but we can get stuck sometimes. + // For example, if incoming A2DP fails, we are not informed by Bluez + sendMessage(TRANSITION_TO_STABLE); } } }; @@ -175,6 +179,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -302,7 +307,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Log.e(TAG, "Error: OutgoingHandsfree state with command:" + mCommand); } mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + if (!mStatus) { + sendMessage(TRANSITION_TO_STABLE); + mService.sendProfileStateMessage(BluetoothProfileState.HFP, + BluetoothProfileState.TRANSITION_TO_STABLE); + } } @Override @@ -393,7 +402,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Log.e(TAG, "Error: IncomingHandsfree state with command:" + mCommand); } mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + if (!mStatus) { + sendMessage(TRANSITION_TO_STABLE); + mService.sendProfileStateMessage(BluetoothProfileState.HFP, + BluetoothProfileState.TRANSITION_TO_STABLE); + } } @Override @@ -461,7 +474,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Log.e(TAG, "Error: OutgoingA2DP state with command:" + mCommand); } mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + if (!mStatus) { + sendMessage(TRANSITION_TO_STABLE); + mService.sendProfileStateMessage(BluetoothProfileState.A2DP, + BluetoothProfileState.TRANSITION_TO_STABLE); + } } @Override @@ -549,7 +566,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Log.e(TAG, "Error: IncomingA2DP state with command:" + mCommand); } mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + if (!mStatus) { + sendMessage(TRANSITION_TO_STABLE); + mService.sendProfileStateMessage(BluetoothProfileState.A2DP, + BluetoothProfileState.TRANSITION_TO_STABLE); + } } @Override diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index 946dcaa01c8..dc0b32b7567 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -43,10 +43,10 @@ public class BluetoothProfileState extends HierarchicalStateMachine { private static final boolean DBG = true; // STOPSHIP - change to false. private static final String TAG = "BluetoothProfileState"; - public static int HFP = 0; - public static int A2DP = 1; + public static final int HFP = 0; + public static final int A2DP = 1; - private static int TRANSITION_TO_STABLE = 100; + static final int TRANSITION_TO_STABLE = 100; private int mProfile; private BluetoothDevice mPendingDevice; @@ -57,7 +57,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0); if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED || @@ -70,6 +70,10 @@ public class BluetoothProfileState extends HierarchicalStateMachine { newState == BluetoothA2dp.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } + } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { + if (device.equals(mPendingDevice)) { + sendMessage(TRANSITION_TO_STABLE); + } } } }; @@ -84,6 +88,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); context.registerReceiver(mBroadcastReceiver, filter); } -- GitLab From 3296048a49977eb9e4606ed524c38b197dccddd1 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 13 Oct 2010 16:48:02 -0700 Subject: [PATCH 0122/1408] Remove STOPSHIP and reduce log spam. We still need some logs, as it helped me fix the errant headset bug. Bug: 3095000 Change-Id: If12601430e6dc30490df1ff86fc3f9d4d455e6d5 --- .../bluetooth/BluetoothDeviceProfileState.java | 14 +++++++------- .../android/bluetooth/BluetoothProfileState.java | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 17b28d1a809..71aefbc3bd4 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -56,7 +56,7 @@ import com.android.internal.util.HierarchicalStateMachine; */ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine { private static final String TAG = "BluetoothDeviceProfileState"; - private static final boolean DBG = true; //STOPSHIP - Change to false + private static final boolean DBG = false; public static final int CONNECT_HFP_OUTGOING = 1; public static final int CONNECT_HFP_INCOMING = 2; @@ -222,7 +222,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private class BondedDevice extends HierarchicalState { @Override protected void enter() { - log("Entering ACL Connected state with: " + getCurrentMessage().what); + Log.i(TAG, "Entering ACL Connected state with: " + getCurrentMessage().what); Message m = new Message(); m.copyFrom(getCurrentMessage()); sendMessageAtFrontOfQueue(m); @@ -300,7 +300,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine @Override protected void enter() { - log("Entering OutgoingHandsfree state with: " + getCurrentMessage().what); + Log.i(TAG, "Entering OutgoingHandsfree state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HFP_OUTGOING && mCommand != DISCONNECT_HFP_OUTGOING) { @@ -395,7 +395,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine @Override protected void enter() { - log("Entering IncomingHandsfree state with: " + getCurrentMessage().what); + Log.i(TAG, "Entering IncomingHandsfree state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HFP_INCOMING && mCommand != DISCONNECT_HFP_INCOMING) { @@ -467,7 +467,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine @Override protected void enter() { - log("Entering OutgoingA2dp state with: " + getCurrentMessage().what); + Log.i(TAG, "Entering OutgoingA2dp state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_A2DP_OUTGOING && mCommand != DISCONNECT_A2DP_OUTGOING) { @@ -559,7 +559,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine @Override protected void enter() { - log("Entering IncomingA2dp state with: " + getCurrentMessage().what); + Log.i(TAG, "Entering IncomingA2dp state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_A2DP_INCOMING && mCommand != DISCONNECT_A2DP_INCOMING) { @@ -643,7 +643,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } synchronized boolean processCommand(int command) { - log("Processing command:" + command); + Log.i(TAG, "Processing command:" + command); switch(command) { case CONNECT_HFP_OUTGOING: if (mHeadsetService != null) { diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index dc0b32b7567..686ff7cd31e 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -40,7 +40,7 @@ import com.android.internal.util.HierarchicalStateMachine; */ public class BluetoothProfileState extends HierarchicalStateMachine { - private static final boolean DBG = true; // STOPSHIP - change to false. + private static final boolean DBG = true; private static final String TAG = "BluetoothProfileState"; public static final int HFP = 0; -- GitLab From 07fd065b8f6f7fdae3faa1dba79ccf78ca8eaa2b Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 13 Oct 2010 19:11:28 -0700 Subject: [PATCH 0123/1408] Add function to check for AVRCP target to send volume keys. Currently, when A2DP is connected and the user changes volume keys on the device, we just change the volume on the device. We are not supposed to do that. The white paper recommendation is to keep the volume stream at max and just send the volume keys over. But then not all A2DP headsets act as AVRCP targets, hence check for that. So now the media code should use the API and not change the volume at the device but just send the keys. Bug: 3071356 Change-Id: I03a8b6f7619cf860c4e63979bf4903ded9ecd314 --- .../java/android/bluetooth/BluetoothA2dp.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d308a5c315b..920ef89d0af 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.IBinder; +import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.server.BluetoothA2dpService; @@ -338,6 +339,26 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + /** + * This function checks if the remote device is an AVCRP + * target and thus whether we should send volume keys + * changes or not. + * @hide + */ + public boolean shouldSendVolumeKeys(BluetoothDevice device) { + if (isEnabled() && isValidDevice(device)) { + ParcelUuid[] uuids = device.getUuids(); + if (uuids == null) return false; + + for (ParcelUuid uuid: uuids) { + if (BluetoothUuid.isAvrcpTarget(uuid)) { + return true; + } + } + } + return false; + } + /** * Helper for converting a state to a string. * -- GitLab From 4ea7cceec0f9e55a660be16771eee7cf5a273f31 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 14 Oct 2010 16:12:20 -0700 Subject: [PATCH 0124/1408] Disconnect PBAP while disconnecting HFP profile. PBAP profile is associated with HFP profile. Disconnect PBAP profile while disconnecting HFP profile. We ever never doing this and were expecting the remote end to do it. So when the user disconnects a headset, we will end up with the ACL link still up because PBAP has not been disconnected. This is little bit of a hack till PBAP profile is converted to use the new profile APIs. Change-Id: I04821daae6588955fbfea01472e6a1b6f9212608 --- .../BluetoothDeviceProfileState.java | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index e460839deff..1c53c45aa3b 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -75,6 +75,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int DISCONNECT_A2DP_INCOMING = 53; public static final int DISCONNECT_HID_OUTGOING = 54; public static final int DISCONNECT_HID_INCOMING = 55; + public static final int DISCONNECT_PBAP_OUTGOING = 56; public static final int UNPAIR = 100; public static final int AUTO_CONNECT_PROFILES = 101; @@ -94,6 +95,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothService mService; private BluetoothA2dpService mA2dpService; private BluetoothHeadset mHeadsetService; + private BluetoothPbap mPbapService; + private boolean mPbapServiceConnected; private BluetoothDevice mDevice; private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED; @@ -201,6 +204,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); + // TODO(): Convert PBAP to the new Profile APIs. + PbapServiceListener p = new PbapServiceListener(); } private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = @@ -217,6 +222,22 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } }; + private class PbapServiceListener implements BluetoothPbap.ServiceListener { + public PbapServiceListener() { + mPbapService = new BluetoothPbap(mContext, this); + } + public void onServiceConnected() { + synchronized(BluetoothDeviceProfileState.this) { + mPbapServiceConnected = true; + } + } + public void onServiceDisconnected() { + synchronized(BluetoothDeviceProfileState.this) { + mPbapServiceConnected = false; + } + } + } + private class BondedDevice extends HierarchicalState { @Override protected void enter() { @@ -255,6 +276,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_HID_INCOMING: transitionTo(mIncomingHid); break; + case DISCONNECT_PBAP_OUTGOING: + processCommand(DISCONNECT_PBAP_OUTGOING); + break; case UNPAIR: if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HFP_OUTGOING); @@ -404,6 +428,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(deferMsg); } break; // ignore + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -478,6 +503,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HID_INCOMING: case DISCONNECT_HID_INCOMING: break; // ignore + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -582,6 +608,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(deferMsg); } break; // ignore + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -654,6 +681,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HID_INCOMING: case DISCONNECT_HID_INCOMING: break; // ignore + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -734,7 +762,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_HID_INCOMING: // Ignore, will be handled by Bluez break; - + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -792,6 +820,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -818,7 +847,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - synchronized void deferHeadsetMessage(int command) { + synchronized void deferProfileServiceMessage(int command) { Message msg = new Message(); msg.what = command; deferMessage(msg); @@ -829,14 +858,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine switch(command) { case CONNECT_HFP_OUTGOING: if (mHeadsetService == null) { - deferHeadsetMessage(command); + deferProfileServiceMessage(command); } else { return mHeadsetService.connectHeadsetInternal(mDevice); } break; case CONNECT_HFP_INCOMING: if (mHeadsetService == null) { - deferHeadsetMessage(command); + deferProfileServiceMessage(command); } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { return mHeadsetService.acceptIncomingConnect(mDevice); } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { @@ -857,8 +886,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return true; case DISCONNECT_HFP_OUTGOING: if (mHeadsetService == null) { - deferHeadsetMessage(command); + deferProfileServiceMessage(command); } else { + // Disconnect PBAP + // TODO(): Add PBAP to the state machine. + Message m = new Message(); + m.what = DISCONNECT_PBAP_OUTGOING; + deferMessage(m); if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT) { mHeadsetService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); @@ -890,6 +924,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON); } return mService.disconnectInputDeviceInternal(mDevice); + case DISCONNECT_PBAP_OUTGOING: + if (!mPbapServiceConnected) { + deferProfileServiceMessage(command); + } else { + return mPbapService.disconnect(); + } + break; case UNPAIR: return mService.removeBondInternal(mDevice.getAddress()); default: -- GitLab From 8fa2abb604cb420b1fd5b9d7647382355e8891f7 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 14 Oct 2010 13:42:19 -0700 Subject: [PATCH 0125/1408] docs: fix typo; surface dev guide link in package description Change-Id: I2dc09bb6b79cbe3f80819d6193eb0d8a3565310d --- framework/java/android/bluetooth/package.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 5ff240c706c..9ac42dc5835 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -1,7 +1,11 @@ -Provides classes that manage Bluetooth functionality, such as scanning for -devices, connecting with devices, and managing data transfer between devices. +

    Provides classes that manage Bluetooth functionality, such as scanning for +devices, connecting with devices, and managing data transfer between devices.

    + +

    For a complete guide to using the Bluetooth APIs, see the Bluetooth developer guide.

    +{@more}

    The Bluetooth APIs let applications:

      @@ -20,9 +24,6 @@ also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.

      -

      For a detailed guide to using the Bluetooth APIs, see the Bluetooth Dev Guide topic.

      -

      Note: Not all Android devices are guaranteed to have Bluetooth functionality.

      -- GitLab From d8fc4dd39f361b09a4c34b1a22af92cf31bebd2d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 18 Oct 2010 16:41:53 -0700 Subject: [PATCH 0126/1408] Convert return type of APIs from Set to List. Most of the time it will either be empty or have 1 device. Using list makes it much a better API and since its supported by the AIDL format, the code becomes much nicer. Change-Id: I5a2508b33ba754fc8cc738409d658e1235aaf2cf --- .../java/android/bluetooth/BluetoothA2dp.java | 28 ++++++----------- .../android/bluetooth/BluetoothHeadset.java | 31 +++++++------------ .../android/bluetooth/BluetoothProfile.java | 13 +++----- .../android/bluetooth/IBluetoothA2dp.aidl | 5 ++- .../android/bluetooth/IBluetoothHeadset.aidl | 5 ++- 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 920ef89d0af..61b43036ef3 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -26,11 +26,8 @@ import android.os.ServiceManager; import android.server.BluetoothA2dpService; import android.util.Log; -import java.util.Collections; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** @@ -167,35 +164,35 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * {@inheritDoc} */ - public Set getConnectedDevices() { + public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { - return toDeviceSet(mService.getConnectedDevices()); + return mService.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } /** * {@inheritDoc} */ - public Set getDevicesMatchingConnectionStates(int[] states) { + public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { - return toDeviceSet(mService.getDevicesMatchingConnectionStates(states)); + return mService.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } /** @@ -396,11 +393,6 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - private Set toDeviceSet(BluetoothDevice[] devices) { - return Collections.unmodifiableSet( - new HashSet(Arrays.asList(devices))); - } - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0496b1f27ce..c64fdbe396d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -18,20 +18,16 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * Public API for controlling the Bluetooth Headset Service. This includes both @@ -218,35 +214,35 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * {@inheritDoc} */ - public Set getConnectedDevices() { + public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { - return toDeviceSet(mService.getConnectedDevices()); + return mService.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } /** * {@inheritDoc} */ - public Set getDevicesMatchingConnectionStates(int[] states) { + public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { - return toDeviceSet(mService.getDevicesMatchingConnectionStates(states)); + return mService.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return toDeviceSet(new BluetoothDevice[0]); + return new ArrayList(); } /** @@ -569,11 +565,6 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - private Set toDeviceSet(BluetoothDevice[] devices) { - return Collections.unmodifiableSet( - new HashSet(Arrays.asList(devices))); - } - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3b4c84c9ea5..3949b26186e 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -17,10 +17,7 @@ package android.bluetooth; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; - -import java.util.Set; +import java.util.List; /** * Public APIs for the Bluetooth Profiles. @@ -150,9 +147,9 @@ public interface BluetoothProfile { * *

      Requires {@link android.Manifest.permission#BLUETOOTH} * - * @return An unmodifiable set of devices. The set will be empty on error. + * @return List of devices. The list will be empty on error. */ - public Set getConnectedDevices(); + public List getConnectedDevices(); /** * Get a set of devices that match any of the given connection @@ -166,9 +163,9 @@ public interface BluetoothProfile { * @param states Array of states. States can be one of * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return An unmodifiable set of devices. The set will be empty on error. + * @return List of devices. The list will be empty on error. */ - public Set getDevicesMatchingConnectionStates(int[] states); + public List getDevicesMatchingConnectionStates(int[] states); /** * Get the current connection state of the profile diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index c5044c23c7e..b4fc3666003 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -27,9 +27,8 @@ interface IBluetoothA2dp { // Public API boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); - // change to Set<> once AIDL supports - BluetoothDevice[] getConnectedDevices(); - BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 8bcf10335f6..3e4c7b4f8e7 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -27,9 +27,8 @@ interface IBluetoothHeadset { // Public API boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); - // Change to Set<> when AIDL supports - BluetoothDevice[] getConnectedDevices(); - BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); -- GitLab From 9af4713a7e32a03ecc027b7797ae7f7cc4644b55 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 18 Oct 2010 17:05:09 -0700 Subject: [PATCH 0127/1408] Update BT APIs return type from Set to List. Change-Id: Ia27220dd26cde13007f6938c830517ee7f6968ce --- .../BluetoothDeviceProfileState.java | 2 +- .../bluetooth/BluetoothInputDevice.java | 16 ++++++--------- .../java/android/bluetooth/BluetoothPan.java | 20 ++++++++----------- .../java/android/bluetooth/IBluetooth.aidl | 4 ++-- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index b33ab212626..fd8f9308344 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -321,7 +321,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine mA2dpService.getDevicesMatchingConnectionStates( new int[] {BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}).length == 0) { + BluetoothProfile.STATE_DISCONNECTING}).size() == 0) { mA2dpService.connect(mDevice); } if (mService.getInputDevicePriority(mDevice) == diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 179383827dc..bc8a836183b 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -24,10 +24,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * Public API for controlling the Bluetooth HID (Input Device) Profile @@ -167,18 +165,16 @@ public final class BluetoothInputDevice { /** Check if any Input Device is connected. * - * @return a unmodifiable set of connected Input Devices, or null on error. + * @return List of devices, empty List on error. * @hide */ - public Set getConnectedInputDevices() { + public List getConnectedInputDevices() { if (DBG) log("getConnectedInputDevices()"); try { - return Collections.unmodifiableSet( - new HashSet( - Arrays.asList(mService.getConnectedInputDevices()))); + return mService.getConnectedInputDevices(); } catch (RemoteException e) { Log.e(TAG, "", e); - return null; + return new ArrayList(); } } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 9d0b3f20a5d..f55e96af5ea 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -19,15 +19,13 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; -import android.os.ServiceManager; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; /** * @hide @@ -154,18 +152,16 @@ public final class BluetoothPan { * * Does not include devices that are currently connecting or disconnecting * - * @return a unmodifiable set of connected PAN Devices, or null on error. + * @return List of PAN devices or empty on Error * @hide */ - public Set getConnectedDevices() { + public List getConnectedDevices() { if (DBG) log("getConnectedDevices"); try { - return Collections.unmodifiableSet( - new HashSet( - Arrays.asList(mService.getConnectedPanDevices()))); + return mService.getConnectedPanDevices(); } catch (RemoteException e) { Log.e(TAG, "", e); - return null; + return new ArrayList(); } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index cc231466e30..f0252b73c39 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -81,7 +81,7 @@ interface IBluetooth // HID profile APIs boolean connectInputDevice(in BluetoothDevice device); boolean disconnectInputDevice(in BluetoothDevice device); - BluetoothDevice[] getConnectedInputDevices(); // change to Set<> once AIDL supports + List getConnectedInputDevices(); int getInputDeviceState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); @@ -89,7 +89,7 @@ interface IBluetooth boolean isTetheringOn(); void setBluetoothTethering(boolean value); int getPanDeviceState(in BluetoothDevice device); - BluetoothDevice[] getConnectedPanDevices(); + List getConnectedPanDevices(); boolean connectPanDevice(in BluetoothDevice device); boolean disconnectPanDevice(in BluetoothDevice device); } -- GitLab From ee628edab1c60cf8c3a4b07e6614eebe52a0a21b Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 29 Sep 2010 11:34:59 -0700 Subject: [PATCH 0128/1408] Expose Vendor Specific Headset Event Intent. The Bluetooth Headset spec doesn't cover all cases. For example, there is nothing defined to show the battery status of the headset on the phone. This intent allows 3rd party applications to communicate effectively with vendor headsets. For example, Plantronics has an app for Android headsets and when used with Plantronics headsets, they will be able to use extra information from the headset. Change-Id: Ib997327103cbbe57d64c52ba27f74162b6769798 --- .../bluetooth/BluetoothAssignedNumbers.java | 4 +- .../android/bluetooth/BluetoothHeadset.java | 88 ++++++++++++++++--- 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java index 55bc814066c..580e9ff56f4 100644 --- a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java +++ b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java @@ -23,12 +23,10 @@ package android.bluetooth; * @see * The Official Bluetooth SIG Member Website | Company Identifiers * - * @hide */ public class BluetoothAssignedNumbers { - //// Bluetooth SIG Company ID values - + // Bluetooth SIG Company ID values /* * Ericsson Technology Licensing. */ diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c64fdbe396d..3aea418e475 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -86,12 +86,35 @@ public final class BluetoothHeadset implements BluetoothProfile { /** - * Broadcast Action: Indicates a headset has posted a vendor-specific event. - *

      Always contains the extra fields {@link #EXTRA_DEVICE}, - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}. + * Intent used to broadcast that the headset has posted a + * vendor-specific event. + * + *

      This intent will have 4 extras and 1 category. + * {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor specific + * command + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT command + * type. + * Can be one of {@link #AT_CMD_TYPE_READ}, {@link #AT_CMD_TYPE_TEST}, + * or {@link #AT_CMD_TYPE_SET}, {@link #AT_CMD_TYPE_BASIC}, + * {@link #AT_CMD_TYPE_ACTION}. + * + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command arguments. + * + * The category is the Company ID of the vendor defining the + * vendor-specific command. {@link BluetoothAssignedNumbers} + * @see + * Bluetooth SIG Assigned Numbers - Company Identifiers + * + * For example, for Plantronics specific events + * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55 + * + *

      For example, an AT+XEVENT=foo,3 will get translated into + * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT + * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET + * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 + * *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = @@ -100,31 +123,68 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} * intents that contains the name of the vendor-specific command. - * @hide */ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD"; /** * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} - * intents that contains the Company ID of the vendor defining the vendor-specific - * command. - * @see - * Bluetooth SIG Assigned Numbers - Company Identifiers - * @hide + * intents that contains the AT command type of the vendor-specific command. + */ + public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = + "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE"; + + /** + * AT command type READ used with + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} + * For example, AT+VGM?. There are no arguments for this command type. */ - public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID = - "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID"; + public static final int AT_CMD_TYPE_READ = 0; + + /** + * AT command type TEST used with + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} + * For example, AT+VGM=?. There are no arguments for this command type. + */ + public static final int AT_CMD_TYPE_TEST = 1; + + /** + * AT command type SET used with + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} + * For example, AT+VGM=. + */ + public static final int AT_CMD_TYPE_SET = 2; + + /** + * AT command type BASIC used with + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} + * For example, ATD. Single character commands and everything following the + * character are arguments. + */ + public static final int AT_CMD_TYPE_BASIC = 3; + + /** + * AT command type ACTION used with + * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} + * For example, AT+CHUP. There are no arguments for action commands. + */ + public static final int AT_CMD_TYPE_ACTION = 4; /** * A Parcelable String array extra field in * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains * the arguments to the vendor-specific command. - * @hide */ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; + /** + * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} + * for the companyId + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = + "android.bluetooth.headset.intent.category.companyid"; + /* * Headset state when SCO audio is connected * This state can be one of -- GitLab From 88abed108a9a34bc84c740567ebdfb80a4c0d0b7 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 25 Oct 2010 17:07:51 -0700 Subject: [PATCH 0129/1408] Fix docs builds. Change-Id: I764bd0f0f6994cc468b7ebbea4a3dc7a44b9f89d --- framework/java/android/bluetooth/BluetoothHeadset.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 3aea418e475..c72be6bd09c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -103,8 +103,6 @@ public final class BluetoothHeadset implements BluetoothProfile { * * The category is the Company ID of the vendor defining the * vendor-specific command. {@link BluetoothAssignedNumbers} - * @see - * Bluetooth SIG Assigned Numbers - Company Identifiers * * For example, for Plantronics specific events * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55 -- GitLab From 1fce18639d4558e7947223fb30354ab8d3262bb0 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 21 Oct 2010 14:55:05 -0700 Subject: [PATCH 0130/1408] Implement ACTION_CONNECTION_STATE_CHANGED intent. Change-Id: I6b5783c189c9796ebd85d9d54bdcb07949bef27e --- framework/java/android/bluetooth/IBluetooth.aidl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index f0252b73c39..1eb269dd572 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -92,4 +92,6 @@ interface IBluetooth List getConnectedPanDevices(); boolean connectPanDevice(in BluetoothDevice device); boolean disconnectPanDevice(in BluetoothDevice device); + + void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState); } -- GitLab From 8e8b26d45e8664707246f5f8f9e73daebe20379a Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 26 Oct 2010 16:02:26 -0700 Subject: [PATCH 0131/1408] Add an API call to get the ConnectionState of the Bluetooth Adapter. Change-Id: Icd87d7720189034946aaa98e1a6c5d03ef4219e5 --- .../android/bluetooth/BluetoothAdapter.java | 22 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 2 ++ 2 files changed, 24 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c66b2dec9e2..556fb10fb9c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -737,6 +737,27 @@ public final class BluetoothAdapter { return null; } + /** + * Get the current connection state of the local Bluetooth adapter. + * This can be used to check whether the local Bluetooth adapter is connected + * to any profile of any other remote Bluetooth Device. + * + *

      Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED} + * intent to get the connection state of the adapter. + * + * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, + * {@link #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} + * + * @hide + */ + public int getConnectionState() { + if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED; + try { + return mService.getAdapterConnectionState(); + } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);} + return BluetoothAdapter.STATE_DISCONNECTED; + } + /** * Picks RFCOMM channels until none are left. * Avoids reserved channels. @@ -879,6 +900,7 @@ public final class BluetoothAdapter { return socket; } + /** * Construct an unencrypted, unauthenticated, RFCOMM server socket. * Call #accept to retrieve connections to this socket. diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 1eb269dd572..aefb3f296ee 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -47,6 +47,8 @@ interface IBluetooth boolean isDiscovering(); byte[] readOutOfBandData(); + int getAdapterConnectionState(); + boolean createBond(in String address); boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer); boolean cancelBondProcess(in String address); -- GitLab From 5a047ee06f0a9ebcd5eace7f75a89a24761fb307 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 27 Oct 2010 15:57:23 -0700 Subject: [PATCH 0132/1408] DO NOT MERGE Fix auto connection with headsets which don't send incoming connections for all profiles. Bug: 3136095 Change-Id: I48f36b99f3f7619a6a18c6adc2057c9d6d23c3e3 --- .../BluetoothDeviceProfileState.java | 89 ++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 71aefbc3bd4..9be4c8f6f58 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -72,6 +72,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int UNPAIR = 100; public static final int AUTO_CONNECT_PROFILES = 101; public static final int TRANSITION_TO_STABLE = 102; + public static final int CONNECT_OTHER_PROFILES = 103; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs @@ -129,10 +130,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - if (!getCurrentState().equals(mBondedDevice)) { - Log.e(TAG, "State is: " + getCurrentState()); - return; - } Message msg = new Message(); msg.what = AUTO_CONNECT_PROFILES; sendMessageDelayed(msg, AUTO_CONNECT_DELAY); @@ -274,16 +271,39 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT && !mHeadsetService.isConnected(mDevice)) { + Log.i(TAG, "Headset:Auto Connect Profiles"); mHeadsetService.connectHeadset(mDevice); } if (mA2dpService != null && mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && mA2dpService.getConnectedSinks().length == 0) { + Log.i(TAG, "A2dp:Auto Connect Profiles"); mA2dpService.connectSink(mDevice); } } break; + case CONNECT_OTHER_PROFILES: + if (isPhoneDocked(mDevice)) { + break; + } + if (message.arg1 == CONNECT_A2DP_OUTGOING) { + if (mA2dpService != null && + mA2dpService.getConnectedSinks().length == 0) { + Log.i(TAG, "A2dp:Connect Other Profiles"); + mA2dpService.connectSink(mDevice); + } + } else if (message.arg1 == CONNECT_HFP_OUTGOING) { + if (!mHeadsetServiceConnected) { + deferMessage(message); + } else { + if (!mHeadsetService.isConnected(mDevice)) { + Log.i(TAG, "Headset:Connect Other Profiles"); + mHeadsetService.connectHeadset(mDevice); + } + } + } + break; case TRANSITION_TO_STABLE: // ignore. break; @@ -377,6 +397,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -449,6 +470,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -541,6 +563,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -611,6 +634,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -656,6 +680,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { return mHeadsetService.acceptIncomingConnect(mDevice); } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + handleConnectionOfOtherProfiles(command); return mHeadsetService.createIncomingConnect(mDevice); } break; @@ -665,6 +690,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case CONNECT_A2DP_INCOMING: + handleConnectionOfOtherProfiles(command); // ignore, Bluez takes care return true; case DISCONNECT_HFP_OUTGOING: @@ -713,6 +739,61 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return false; } + private void handleConnectionOfOtherProfiles(int command) { + // The white paper recommendations mentions that when there is a + // link loss, it is the responsibility of the remote device to connect. + // Many connect only 1 profile - and they connect the second profile on + // some user action (like play being pressed) and so we need this code. + // Auto Connect code only connects to the last connected device - which + // is useful in cases like when the phone reboots. But consider the + // following case: + // User is connected to the car's phone and A2DP profile. + // User comes to the desk and places the phone in the dock + // (or any speaker or music system or even another headset) and thus + // gets connected to the A2DP profile. User goes back to the car. + // Ideally the car's system is supposed to send incoming connections + // from both Handsfree and A2DP profile. But they don't. The Auto + // connect code, will not work here because we only auto connect to the + // last connected device for that profile which in this case is the dock. + // Now suppose a user is using 2 headsets simultaneously, one for the + // phone profile one for the A2DP profile. If this is the use case, we + // expect the user to use the preference to turn off the A2DP profile in + // the Settings screen for the first headset. Else, after link loss, + // there can be an incoming connection from the first headset which + // might result in the connection of the A2DP profile (if the second + // headset is slower) and thus the A2DP profile on the second headset + // will never get connected. + // + // TODO(): Handle other profiles here. + switch (command) { + case CONNECT_HFP_INCOMING: + // Connect A2DP if there is no incoming connection + // If the priority is OFF - don't auto connect. + // If the priority is AUTO_CONNECT, auto connect code takes care. + if (mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_ON) { + Message msg = new Message(); + msg.what = CONNECT_OTHER_PROFILES; + msg.arg1 = CONNECT_A2DP_OUTGOING; + sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } + break; + case CONNECT_A2DP_INCOMING: + // This is again against spec. HFP incoming connections should be made + // before A2DP, so we should not hit this case. But many devices + // don't follow this. + if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_ON) { + Message msg = new Message(); + msg.what = CONNECT_OTHER_PROFILES; + msg.arg1 = CONNECT_HFP_OUTGOING; + sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } + break; + default: + break; + } + + } + /*package*/ BluetoothDevice getDevice() { return mDevice; } -- GitLab From c6e799c5f93e92b2b8a3dc757f011d89acdeea5a Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 27 Oct 2010 15:57:23 -0700 Subject: [PATCH 0133/1408] Fix incoming connection problem This fixes the incoming auto connection of profiles with certain remote devices. Bug: 3136095 Change-Id: If26b5aa91403c0c7bd547e2970ee5bf2ac381416 --- .../BluetoothDeviceProfileState.java | 86 ++++++++++++++++++- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index fd8f9308344..0c8e4d9eb48 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -80,6 +80,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int UNPAIR = 100; public static final int AUTO_CONNECT_PROFILES = 101; public static final int TRANSITION_TO_STABLE = 102; + public static final int CONNECT_OTHER_PROFILES = 103; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs @@ -149,10 +150,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - if (!getCurrentState().equals(mBondedDevice)) { - Log.e(TAG, "State is: " + getCurrentState()); - return; - } Message msg = new Message(); msg.what = AUTO_CONNECT_PROFILES; sendMessageDelayed(msg, AUTO_CONNECT_DELAY); @@ -330,6 +327,27 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } break; + case CONNECT_OTHER_PROFILES: + if (isPhoneDocked(mDevice)) { + break; + } + if (message.arg1 == CONNECT_A2DP_OUTGOING) { + if (mA2dpService != null && + mA2dpService.getConnectedDevices().size() == 0) { + Log.i(TAG, "A2dp:Connect Other Profiles"); + mA2dpService.connect(mDevice); + } + } else if (message.arg1 == CONNECT_HFP_OUTGOING) { + if (mHeadsetService == null) { + deferMessage(message); + } else { + if (mHeadsetService.getConnectedDevices().size() == 0) { + Log.i(TAG, "Headset:Connect Other Profiles"); + mHeadsetService.connect(mDevice); + } + } + } + break; case TRANSITION_TO_STABLE: // ignore. break; @@ -440,6 +458,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -519,6 +538,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -628,6 +648,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -705,6 +726,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_PBAP_OUTGOING: case UNPAIR: case AUTO_CONNECT_PROFILES: + case CONNECT_OTHER_PROFILES: deferMessage(message); break; case TRANSITION_TO_STABLE: @@ -890,6 +912,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { return mHeadsetService.acceptIncomingConnect(mDevice); } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + handleConnectionOfOtherProfiles(command); return mHeadsetService.createIncomingConnect(mDevice); } break; @@ -899,6 +922,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case CONNECT_A2DP_INCOMING: + handleConnectionOfOtherProfiles(command); // ignore, Bluez takes care return true; case CONNECT_HID_OUTGOING: @@ -960,6 +984,60 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return false; } + private void handleConnectionOfOtherProfiles(int command) { + // The white paper recommendations mentions that when there is a + // link loss, it is the responsibility of the remote device to connect. + // Many connect only 1 profile - and they connect the second profile on + // some user action (like play being pressed) and so we need this code. + // Auto Connect code only connects to the last connected device - which + // is useful in cases like when the phone reboots. But consider the + // following case: + // User is connected to the car's phone and A2DP profile. + // User comes to the desk and places the phone in the dock + // (or any speaker or music system or even another headset) and thus + // gets connected to the A2DP profile. User goes back to the car. + // Ideally the car's system is supposed to send incoming connections + // from both Handsfree and A2DP profile. But they don't. The Auto + // connect code, will not work here because we only auto connect to the + // last connected device for that profile which in this case is the dock. + // Now suppose a user is using 2 headsets simultaneously, one for the + // phone profile one for the A2DP profile. If this is the use case, we + // expect the user to use the preference to turn off the A2DP profile in + // the Settings screen for the first headset. Else, after link loss, + // there can be an incoming connection from the first headset which + // might result in the connection of the A2DP profile (if the second + // headset is slower) and thus the A2DP profile on the second headset + // will never get connected. + // + // TODO(): Handle other profiles here. + switch (command) { + case CONNECT_HFP_INCOMING: + // Connect A2DP if there is no incoming connection + // If the priority is OFF - don't auto connect. + // If the priority is AUTO_CONNECT, auto connect code takes care. + if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { + Message msg = new Message(); + msg.what = CONNECT_OTHER_PROFILES; + msg.arg1 = CONNECT_A2DP_OUTGOING; + sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } + break; + case CONNECT_A2DP_INCOMING: + // This is again against spec. HFP incoming connections should be made + // before A2DP, so we should not hit this case. But many devices + // don't follow this. + if (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { + Message msg = new Message(); + msg.what = CONNECT_OTHER_PROFILES; + msg.arg1 = CONNECT_HFP_OUTGOING; + sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + } + break; + default: + break; + } + + } /*package*/ BluetoothDevice getDevice() { return mDevice; -- GitLab From be881e5c0fab02299f321fd6f624ce59466120b1 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 1 Nov 2010 14:57:36 -0700 Subject: [PATCH 0134/1408] Disconnect PAN and Input devices on BT disable. Also, add a missing break. Change-Id: I228f80a4223de20e60d2f3828e3f63bdc14f5afc --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 0c8e4d9eb48..138e7f2c7de 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -773,6 +773,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: transitionTo(mIncomingHandsfree); + break; case CONNECT_A2DP_INCOMING: transitionTo(mIncomingA2dp); -- GitLab From 23501e2d9b1448e1a96984e22257b3eb7617294b Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 1 Nov 2010 11:59:57 -0700 Subject: [PATCH 0135/1408] Add STATE_AUDIO_CONNECTING state. Why is it needed: SCO audio connections can fail. Currently no indication is given to clients which are waiting for SCO connections. This was working fine before because SCO connections where blocking calls, which was wrong in itself. Change-Id: Ic449b2db8506a7a5ae6be6c68715f1a7343f9e40 --- .../android/bluetooth/BluetoothHeadset.java | 36 ++++++++++++++++--- .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c72be6bd09c..c03b444a0eb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -183,7 +183,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid"; - /* + /** * Headset state when SCO audio is connected * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of @@ -192,13 +192,21 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final int STATE_AUDIO_CONNECTED = 10; /** - * Headset state when SCO audio is NOT connected + * Headset state when SCO audio is connecting * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. + * @hide */ - public static final int STATE_AUDIO_DISCONNECTED = 11; + public static final int STATE_AUDIO_CONNECTING = 12; + /** + * Headset state when SCO audio is not connected + * This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_AUDIO_STATE_CHANGED} intent. + */ + public static final int STATE_AUDIO_DISCONNECTED = 11; private Context mContext; private ServiceListener mServiceListener; @@ -370,7 +378,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

      Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED} - * when the audio connection is established. + * when the audio connection is established, + * and to {@link #STATE_AUDIO_DISCONNECTED} in case of failure. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} * @@ -593,6 +602,25 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Get the current audio state of the Headset. + * Note: This is an internal function and shouldn't be exposed + * + * @hide + */ + public int getAudioState(BluetoothDevice device) { + if (DBG) log("getAudioState"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioState(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 BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 3e4c7b4f8e7..ab07931b8bc 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -46,4 +46,5 @@ interface IBluetoothHeadset { boolean connectHeadsetInternal(in BluetoothDevice device); boolean disconnectHeadsetInternal(in BluetoothDevice device); boolean setAudioState(in BluetoothDevice device, int state); + int getAudioState(in BluetoothDevice device); } -- GitLab From 7af32b4c4d6f6c71376283551cdc08a4e0963cb0 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 26 Oct 2010 17:10:09 -0700 Subject: [PATCH 0136/1408] Add APIs for starting and stopping a virtual call. This API is useful for cases where the user wants to play their voicemail, for example, through their Bluetooth headsets. Original Change by: kausik@broadcom.com Change-Id: I6bc8929c359d698a1bacdefab4425e3a0ac5d8dd --- .../android/bluetooth/BluetoothHeadset.java | 45 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 3 ++ 2 files changed, 48 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c03b444a0eb..f63a5c5c344 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -621,6 +621,51 @@ public final class BluetoothHeadset implements BluetoothProfile { return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; } + /** + * Initiates a Virtual Voice Call to the handsfree device (if connected). + * Allows the handsfree device to be used for routing non-cellular call audio + * + * @param device Remote Bluetooth Device + * @return true if successful, false if there was some error. + * @hide + */ + public boolean startVirtualVoiceCall(BluetoothDevice device) { + if (DBG) log("startVirtualVoiceCall()"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.startVirtualVoiceCall(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; + } + + /** + * Terminates an ongoing Virtual Voice Call to the handsfree device (if connected). + * + * @param device Remote Bluetooth Device + * @return true if successful, false if there was some error. + * @hide + */ + public boolean stopVirtualVoiceCall(BluetoothDevice device) { + if (DBG) log("stopVirtualVoiceCall()"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.stopVirtualVoiceCall(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; + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index ab07931b8bc..e9521939ef8 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -47,4 +47,7 @@ interface IBluetoothHeadset { boolean disconnectHeadsetInternal(in BluetoothDevice device); boolean setAudioState(in BluetoothDevice device, int state); int getAudioState(in BluetoothDevice device); + + boolean startVirtualVoiceCall(in BluetoothDevice device); + boolean stopVirtualVoiceCall(in BluetoothDevice device); } -- GitLab From 8ea890dadac95f77f11dc753c6ea5835a3704e69 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 11 Nov 2010 10:49:46 -0800 Subject: [PATCH 0137/1408] Make AUDIO_STATE_CONNECTING in BluetoothHeadset public. As the SCO connection is done on a separate thread, this state is needed to prevent silent failures in case of APIs like voicerecognition. Change-Id: Id08725323a2c847df4d3bd9f60e42fde46100707 --- .../android/bluetooth/BluetoothHeadset.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index f63a5c5c344..d5b00420100 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -184,29 +184,29 @@ public final class BluetoothHeadset implements BluetoothProfile { "android.bluetooth.headset.intent.category.companyid"; /** - * Headset state when SCO audio is connected + * Headset state when SCO audio is not connected * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ - public static final int STATE_AUDIO_CONNECTED = 10; + public static final int STATE_AUDIO_DISCONNECTED = 10; /** * Headset state when SCO audio is connecting * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. - * @hide */ - public static final int STATE_AUDIO_CONNECTING = 12; + public static final int STATE_AUDIO_CONNECTING = 11; /** - * Headset state when SCO audio is not connected + * Headset state when SCO audio is connected * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ - public static final int STATE_AUDIO_DISCONNECTED = 11; + public static final int STATE_AUDIO_CONNECTED = 12; + private Context mContext; private ServiceListener mServiceListener; @@ -377,11 +377,15 @@ public final class BluetoothHeadset implements BluetoothProfile { * audio connection. * *

      Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED} - * when the audio connection is established, - * and to {@link #STATE_AUDIO_DISCONNECTED} in case of failure. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      {@link #EXTRA_STATE} will transition from + * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when + * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} + * in case of failure to establish the audio connection. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset * @return false if there is no headset connected of if the -- GitLab From a2dbad40eee3c5561fd1840a6ca9a5a21521c486 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 23 Nov 2010 20:03:10 -0800 Subject: [PATCH 0138/1408] Update BT code for voice capability cases. 1. Disable PBAP and Headset / Handsfree records. 2. Add API to query for local adapter UUIDs. Change-Id: Ic84ba6a49738adc89a8695d3a4890f08562e0621 --- .../java/android/bluetooth/BluetoothAdapter.java | 15 +++++++++++++++ .../bluetooth/BluetoothDeviceProfileState.java | 8 ++++---- framework/java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 556fb10fb9c..32df4e8cbb9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -520,6 +520,21 @@ public final class BluetoothAdapter { return null; } + /** + * Get the UUIDs supported by the local Bluetooth adapter. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return the UUIDs supported by the local Bluetooth Adapter. + * @hide + */ + public ParcelUuid[] getUuids() { + try { + return mService.getUuids(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + /** * Set the friendly Bluetooth name of the local Bluetooth adapter. *

      This name is visible to remote Bluetooth devices. diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 138e7f2c7de..d1a6ed5fd57 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -301,10 +301,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine if (isPhoneDocked(mDevice)) { // Don't auto connect to docks. break; - } else if (mHeadsetService == null) { - deferMessage(message); } else { - if (mHeadsetService.getPriority(mDevice) == + if (mHeadsetService != null && + mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT && mHeadsetService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, @@ -1027,7 +1026,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // This is again against spec. HFP incoming connections should be made // before A2DP, so we should not hit this case. But many devices // don't follow this. - if (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { + if (mHeadsetService != null && + mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_HFP_OUTGOING; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index aefb3f296ee..f3e73cf5db2 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -35,6 +35,7 @@ interface IBluetooth String getAddress(); String getName(); boolean setName(in String name); + ParcelUuid[] getUuids(); int getScanMode(); boolean setScanMode(int mode, int duration); -- GitLab From 70a33eb1ff51ee7494dfc706d273706d772187f5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 24 Nov 2010 13:06:31 -0800 Subject: [PATCH 0139/1408] Fix some formatting issues. Review comments from a previous review. Change-Id: Ifa6136f9bc5590ffb6a777240ad1e6607b616ca6 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index d1a6ed5fd57..3eadff915b4 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -302,7 +302,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // Don't auto connect to docks. break; } else { - if (mHeadsetService != null && + if (mHeadsetService != null && mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT && mHeadsetService.getDevicesMatchingConnectionStates( @@ -1026,7 +1026,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // This is again against spec. HFP incoming connections should be made // before A2DP, so we should not hit this case. But many devices // don't follow this. - if (mHeadsetService != null && + if (mHeadsetService != null && mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; -- GitLab From 166a60e17d5176cc6d2bd43edb4dbc15a9f47aa0 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 10 Dec 2010 12:48:58 -0800 Subject: [PATCH 0140/1408] Add direct calls to Bluez to add SDP records. This helps Settings app to enable just the Headset service. Change-Id: Ia9c39467df1e83d665e377313e16e6c54991f6d6 --- .../java/android/bluetooth/BluetoothUuid.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 3fbfc70bdb1..59622351814 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -20,6 +20,7 @@ import android.os.ParcelUuid; import java.util.Arrays; import java.util.HashSet; +import java.util.UUID; /** * Static helper methods and constants to decode the ParcelUuid of remote devices. @@ -41,8 +42,12 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid HSP_AG = + ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid Handsfree = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid Handsfree_AG = + ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AvrcpController = ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AvrcpTarget = @@ -57,6 +62,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid PBAP_PSE = + ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -173,4 +180,16 @@ public final class BluetoothUuid { return true; } + /** + * Extract the Service Identifier or the actual uuid from the Parcel Uuid. + * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, + * this function will return 110B + * @param parcelUuid + * @return the service identifier. + */ + public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; + return (int)value; + } } -- GitLab From 0454fb9333be4d37dcc0f35d9f2e2e76c0c66e44 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 13 Dec 2010 15:54:33 -0800 Subject: [PATCH 0141/1408] Remove delay with auto connection. SDP records are now registered with a dbus call so we don't have to wait for initiating auto connections. Also reduce time to connect other profiles case by 2 secs. Change-Id: I8f0eab6a95d3bfaf11a8eb7495a024949639d7fc --- .../android/bluetooth/BluetoothDeviceProfileState.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 3eadff915b4..80a80bd6617 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -82,7 +82,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; - private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs + private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -152,7 +152,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { Message msg = new Message(); msg.what = AUTO_CONNECT_PROFILES; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + sendMessage(msg); } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez @@ -1019,7 +1019,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_A2DP_OUTGOING; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); } break; case CONNECT_A2DP_INCOMING: @@ -1031,7 +1031,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_HFP_OUTGOING; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); } break; default: -- GitLab From f8c04a0b6e2241a20cb9de18fe5dd5ced803fbb5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 14 Dec 2010 14:26:46 -0800 Subject: [PATCH 0142/1408] Add EXTRA_LOCAL_ROLE to Pan State change intent. This will help apps distinguish between reverse and normal tethering. Change-Id: I5327ad75dc2cbf478e4f7c2cd6ef1dbe8fba9e93 --- .../java/android/bluetooth/BluetoothPan.java | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index f55e96af5ea..7dee25e4afa 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -34,21 +34,31 @@ public final class BluetoothPan { private static final String TAG = "BluetoothPan"; private static final boolean DBG = false; + //TODO: This needs to inherit from BluetoothProfile like other profiles. + /** int extra for ACTION_PAN_STATE_CHANGED */ - public static final String EXTRA_PAN_STATE = - "android.bluetooth.pan.extra.STATE"; + public static final String EXTRA_PAN_STATE = "android.bluetooth.pan.extra.STATE"; + /** int extra for ACTION_PAN_STATE_CHANGED */ public static final String EXTRA_PREVIOUS_PAN_STATE = "android.bluetooth.pan.extra.PREVIOUS_STATE"; - /** Indicates the state of an PAN device has changed. + /** int extra for ACTION_PAN_STATE_CHANGED */ + public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + + public static final int LOCAL_NAP_ROLE = 1; + public static final int LOCAL_PANU_ROLE = 2; + + /** + * Indicates the state of an PAN device has changed. * This intent will always contain EXTRA_DEVICE_STATE, - * EXTRA_PREVIOUS_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE + * EXTRA_PREVIOUS_DEVICE_STATE, BluetoothDevice.EXTRA_DEVICE + * and EXTRA_LOCAL_ROLE. * extras. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAN_STATE_CHANGED = - "android.bluetooth.pan.action.STATE_CHANGED"; + "android.bluetooth.pan.action.STATE_CHANGED"; public static final String NAP_ROLE = "nap"; public static final String NAP_BRIDGE = "pan1"; @@ -130,40 +140,42 @@ public final class BluetoothPan { } } - /** Get the state of a PAN Device. - * - * This function returns an int representing the state of the PAN connection - * - * @param device Remote BT device. - * @return The current state of the PAN Device - * @hide - */ - public int getPanDeviceState(BluetoothDevice device) { - if (DBG) log("getPanDeviceState(" + device + ")"); + /** + * Get the state of a PAN Device. + * + * This function returns an int representing the state of the PAN connection + * + * @param device Remote BT device. + * @return The current state of the PAN Device + * @hide + */ + public int getPanDeviceState(BluetoothDevice device) { + if (DBG) log("getPanDeviceState(" + device + ")"); + try { + return mService.getPanDeviceState(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return STATE_DISCONNECTED; + } + } + + /** + * Returns a set of all the connected PAN Devices + * + * Does not include devices that are currently connecting or disconnecting + * + * @return List of PAN devices or empty on Error + * @hide + */ + public List getConnectedDevices() { + if (DBG) log("getConnectedDevices"); try { - return mService.getPanDeviceState(device); + return mService.getConnectedPanDevices(); } catch (RemoteException e) { Log.e(TAG, "", e); - return STATE_DISCONNECTED; + return new ArrayList(); } - } - - /** Returns a set of all the connected PAN Devices - * - * Does not include devices that are currently connecting or disconnecting - * - * @return List of PAN devices or empty on Error - * @hide - */ - public List getConnectedDevices() { - if (DBG) log("getConnectedDevices"); - try { - return mService.getConnectedPanDevices(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return new ArrayList(); - } - } + } private static void log(String msg) { Log.d(TAG, msg); -- GitLab From c14f80e0e360061226df4437743d3dd5714a04d5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 21 Dec 2010 22:31:44 -0800 Subject: [PATCH 0143/1408] Add TYPE_BLUETOOTH network interface for reverse tethering. Change-Id: I2aa61ce15f57aea9e8fd3a4cb56799c8bc51e998 --- .../BluetoothTetheringDataTracker.java | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothTetheringDataTracker.java diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java new file mode 100644 index 00000000000..7b083f10e2b --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2010 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.content.Context; +import android.net.ConnectivityManager; +import android.net.DhcpInfo; +import android.net.LinkAddress; +import android.net.LinkCapabilities; +import android.net.LinkProperties; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.NetworkStateTracker; +import android.net.NetworkUtils; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * This class tracks the data connection associated with Bluetooth + * reverse tethering. This is a singleton class and an instance will be + * created by ConnectivityService. BluetoothService will call into this + * when a reverse tethered connection needs to be activated. + * + * @hide + */ +public class BluetoothTetheringDataTracker implements NetworkStateTracker { + private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; + private static final String TAG = "BluetoothTethering"; + + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); + private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); + private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); + private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); + + private LinkProperties mLinkProperties; + private LinkCapabilities mLinkCapabilities; + private NetworkInfo mNetworkInfo; + + private BluetoothPan mBluetoothPan; + private BluetoothDevice mDevice; + private static String mIface; + + /* For sending events to connectivity service handler */ + private Handler mCsHandler; + private Context mContext; + public static BluetoothTetheringDataTracker sInstance; + + private BluetoothTetheringDataTracker() { + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); + mLinkProperties = new LinkProperties(); + mLinkCapabilities = new LinkCapabilities(); + + mNetworkInfo.setIsAvailable(false); + setTeardownRequested(false); + } + + public static synchronized BluetoothTetheringDataTracker getInstance() { + if (sInstance == null) sInstance = new BluetoothTetheringDataTracker(); + return sInstance; + } + + public Object Clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + public void setTeardownRequested(boolean isRequested) { + mTeardownRequested.set(isRequested); + } + + public boolean isTeardownRequested() { + return mTeardownRequested.get(); + } + + /** + * Begin monitoring connectivity + */ + public void startMonitoring(Context context, Handler target) { + mContext = context; + mCsHandler = target; + mBluetoothPan = new BluetoothPan(mContext); + } + + /** + * Disable connectivity to a network + * TODO: do away with return value after making MobileDataStateTracker async + */ + public boolean teardown() { + mTeardownRequested.set(true); + for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { + mBluetoothPan.disconnect(device); + } + return true; + } + + /** + * Re-enable connectivity to a network after a {@link #teardown()}. + */ + public boolean reconnect() { + mTeardownRequested.set(false); + //Ignore + return true; + } + + /** + * Turn the wireless radio off for a network. + * @param turnOn {@code true} to turn the radio on, {@code false} + */ + public boolean setRadio(boolean turnOn) { + return true; + } + + /** + * @return true - If are we currently tethered with another device. + */ + public synchronized boolean isAvailable() { + return mNetworkInfo.isAvailable(); + } + + /** + * Tells the underlying networking system that the caller wants to + * begin using the named feature. The interpretation of {@code feature} + * is completely up to each networking implementation. + * @param feature the name of the feature to be used + * @param callingPid the process ID of the process that is issuing this request + * @param callingUid the user ID of the process that is issuing this request + * @return an integer value representing the outcome of the request. + * The interpretation of this value is specific to each networking + * implementation+feature combination, except that the value {@code -1} + * always indicates failure. + * TODO: needs to go away + */ + public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; + } + + /** + * Tells the underlying networking system that the caller is finished + * using the named feature. The interpretation of {@code feature} + * is completely up to each networking implementation. + * @param feature the name of the feature that is no longer needed. + * @param callingPid the process ID of the process that is issuing this request + * @param callingUid the user ID of the process that is issuing this request + * @return an integer value representing the outcome of the request. + * The interpretation of this value is specific to each networking + * implementation+feature combination, except that the value {@code -1} + * always indicates failure. + * TODO: needs to go away + */ + public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; + } + + /** + * @param enabled + */ + public void setDataEnable(boolean enabled) { + android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled); + } + + /** + * Check if private DNS route is set for the network + */ + public boolean isPrivateDnsRouteSet() { + return mPrivateDnsRouteSet.get(); + } + + /** + * Set a flag indicating private DNS route is set + */ + public void privateDnsRouteSet(boolean enabled) { + mPrivateDnsRouteSet.set(enabled); + } + + /** + * Fetch NetworkInfo for the network + */ + public synchronized NetworkInfo getNetworkInfo() { + return mNetworkInfo; + } + + /** + * Fetch LinkProperties for the network + */ + public synchronized LinkProperties getLinkProperties() { + return new LinkProperties(mLinkProperties); + } + + /** + * A capability is an Integer/String pair, the capabilities + * are defined in the class LinkSocket#Key. + * + * @return a copy of this connections capabilities, may be empty but never null. + */ + public LinkCapabilities getLinkCapabilities() { + return new LinkCapabilities(mLinkCapabilities); + } + + /** + * Fetch default gateway address for the network + */ + public int getDefaultGatewayAddr() { + return mDefaultGatewayAddr.get(); + } + + /** + * Check if default route is set + */ + public boolean isDefaultRouteSet() { + return mDefaultRouteSet.get(); + } + + /** + * Set a flag indicating default route is set for the network + */ + public void defaultRouteSet(boolean enabled) { + mDefaultRouteSet.set(enabled); + } + + /** + * Return the system properties name associated with the tcp buffer sizes + * for this network. + */ + public String getTcpBufferSizesPropName() { + return "net.tcp.buffersize.wifi"; + } + + + public synchronized void startReverseTether(String iface, BluetoothDevice device) { + mIface = iface; + mDevice = device; + Thread dhcpThread = new Thread(new Runnable() { + public void run() { + //TODO(): Add callbacks for failure and success case. + //Currently this thread runs independently. + DhcpInfo dhcpInfo = new DhcpInfo(); + if (!NetworkUtils.runDhcp(mIface, dhcpInfo)) { + Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + return; + } + mLinkProperties.addLinkAddress(new LinkAddress( + NetworkUtils.intToInetAddress(dhcpInfo.ipAddress), + NetworkUtils.intToInetAddress(dhcpInfo.netmask))); + mLinkProperties.setGateway(NetworkUtils.intToInetAddress(dhcpInfo.gateway)); + InetAddress dns1Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns1); + if (dns1Addr == null || dns1Addr.equals("0.0.0.0")) { + mLinkProperties.addDns(dns1Addr); + } + InetAddress dns2Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns2); + if (dns2Addr == null || dns2Addr.equals("0.0.0.0")) { + mLinkProperties.addDns(dns2Addr); + } + mLinkProperties.setInterfaceName(mIface); + + mNetworkInfo.setIsAvailable(true); + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + + Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); + msg.sendToTarget(); + + msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } + }); + dhcpThread.start(); + } + + public synchronized void stopReverseTether(String iface) { + NetworkUtils.stopDhcp(iface); + + mLinkProperties.clear(); + mNetworkInfo.setIsAvailable(false); + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); + + Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); + msg.sendToTarget(); + + msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } +} -- GitLab From 203b5816c481025bb1252c791c69677dad57546f Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 23 Dec 2010 12:57:02 -0800 Subject: [PATCH 0144/1408] Expose insecure rfcomm Bluetooth API. This complements the secure rfcomm API. The link key is unauthenticated and is subject to MITM attacks. The link key may be encrypted depending on the type of Bluetooth device. This helps apps which don't need the extra security or have their own security layer built on top of the rfcomm link. Change-Id: I71b2fa8de469ef98faa204b4dafac18a8e21d0d9 --- .../android/bluetooth/BluetoothAdapter.java | 38 ++++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 33 ++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 32df4e8cbb9..b2185adc86d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -868,6 +868,42 @@ public final class BluetoothAdapter { */ public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, true, true); + } + + /** + * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. + *

      The link key will be unauthenticated i.e the communication is + * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, + * the link key will be encrypted, as encryption is mandartory. + * For legacy devices (pre Bluetooth 2.1 devices) the link key will not + * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an + * encrypted and authenticated communication channel is desired. + *

      Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

      The system will assign an unused RFCOMM channel to listen on. + *

      The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

      Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) + throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, false, false); + } + + private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, + boolean auth, boolean encrypt) throws IOException { RfcommChannelPicker picker = new RfcommChannelPicker(uuid); BluetoothServerSocket socket; @@ -881,7 +917,7 @@ public final class BluetoothAdapter { } socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel); + BluetoothSocket.TYPE_RFCOMM, auth, encrypt, channel); errno = socket.mSocket.bindListen(); if (errno == 0) { if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ada3c24e5d0..e15d0031147 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -736,6 +736,39 @@ public final class BluetoothDevice implements Parcelable { new ParcelUuid(uuid)); } + /** + * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure + * outgoing connection to this remote device using SDP lookup of uuid. + *

      The communication channel will not have an authenticated link key + * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1 + * devices, the link key will be encrypted, as encryption is mandatory. + * For legacy devices (pre Bluetooth 2.1 devices) the link key will + * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an + * encrypted and authenticated communication channel is desired. + *

      This is designed to be used with {@link + * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer + * Bluetooth applications. + *

      Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. This will also perform an SDP lookup of the given uuid to + * determine which channel to connect to. + *

      The remote device will be authenticated and communication on this + * socket will be encrypted. + *

      Hint: If you are connecting to a Bluetooth serial board then try + * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. + * However if you are connecting to an Android peer then please generate + * your own unique UUID. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param uuid service record uuid to lookup RFCOMM channel + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + */ + public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, + new ParcelUuid(uuid)); + } + /** * Construct an insecure RFCOMM socket ready to start an outgoing * connection. -- GitLab From 7524e6b9e25a460034700acfeca15d14d4774ede Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 5 Jan 2011 13:49:00 -0800 Subject: [PATCH 0145/1408] Follow the spec recommendation for keyboard pairing. Generate a variable pin and show it to the user. Change-Id: Id4aae7a16f25c67d6c6ead6fd936cb14087f3c7a --- .../android/bluetooth/BluetoothClass.java | 18 +++ .../android/bluetooth/BluetoothDevice.java | 125 +++++++++++++----- 2 files changed, 108 insertions(+), 35 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index e604e6becc9..54bf4afa93f 100644 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -226,6 +226,24 @@ public final class BluetoothClass implements Parcelable { public static final int HEALTH_PULSE_OXIMETER = 0x0914; public static final int HEALTH_PULSE_RATE = 0x0918; public static final int HEALTH_DATA_DISPLAY = 0x091C; + + // Devices in PERIPHERAL major class + /** + * @hide + */ + public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500; + /** + * @hide + */ + public static final int PERIPHERAL_KEYBOARD = 0x0540; + /** + * @hide + */ + public static final int PERIPHERAL_POINTING = 0x0580; + /** + * @hide + */ + public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0; } /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e15d0031147..24217d7dc01 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -239,7 +239,7 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; /** @hide */ - public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; + public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; /** * Broadcast Action: This intent is used to broadcast the {@link UUID} @@ -276,58 +276,113 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; - /** A bond attempt succeeded - * @hide */ + /** + * A bond attempt succeeded + * @hide + */ public static final int BOND_SUCCESS = 0; - /** A bond attempt failed because pins did not match, or remote device did + + /** + * A bond attempt failed because pins did not match, or remote device did * not respond to pin request in time - * @hide */ + * @hide + */ public static final int UNBOND_REASON_AUTH_FAILED = 1; - /** A bond attempt failed because the other side explicitly rejected + + /** + * A bond attempt failed because the other side explicitly rejected * bonding - * @hide */ + * @hide + */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; - /** A bond attempt failed because we canceled the bonding process - * @hide */ + + /** + * A bond attempt failed because we canceled the bonding process + * @hide + */ public static final int UNBOND_REASON_AUTH_CANCELED = 3; - /** A bond attempt failed because we could not contact the remote device - * @hide */ + + /** + * A bond attempt failed because we could not contact the remote device + * @hide + */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - /** A bond attempt failed because a discovery is in progress - * @hide */ + + /** + * A bond attempt failed because a discovery is in progress + * @hide + */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; - /** A bond attempt failed because of authentication timeout - * @hide */ + + /** + * A bond attempt failed because of authentication timeout + * @hide + */ public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; - /** A bond attempt failed because of repeated attempts - * @hide */ + + /** + * A bond attempt failed because of repeated attempts + * @hide + */ public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; - /** A bond attempt failed because we received an Authentication Cancel - * by remote end - * @hide */ + + /** + * A bond attempt failed because we received an Authentication Cancel + * by remote end + * @hide + */ public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; - /** An existing bond was explicitly revoked - * @hide */ + + /** + * An existing bond was explicitly revoked + * @hide + */ public static final int UNBOND_REASON_REMOVED = 9; - /** The user will be prompted to enter a pin - * @hide */ + /** + * The user will be prompted to enter a pin + * @hide + */ public static final int PAIRING_VARIANT_PIN = 0; - /** The user will be prompted to enter a passkey - * @hide */ + + /** + * The user will be prompted to enter a passkey + * @hide + */ public static final int PAIRING_VARIANT_PASSKEY = 1; - /** The user will be prompted to confirm the passkey displayed on the screen - * @hide */ + + /** + * The user will be prompted to confirm the passkey displayed on the screen + * @hide + */ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; - /** The user will be prompted to accept or deny the incoming pairing request - * @hide */ + + /** + * The user will be prompted to accept or deny the incoming pairing request + * @hide + */ public static final int PAIRING_VARIANT_CONSENT = 3; - /** The user will be prompted to enter the passkey displayed on remote device - * @hide */ + + /** + * The user will be prompted to enter the passkey displayed on remote device + * This is used for Bluetooth 2.1 pairing. + * @hide + */ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; - /** The user will be prompted to accept or deny the OOB pairing request - * @hide */ - public static final int PAIRING_VARIANT_OOB_CONSENT = 5; + + /** + * The user will be prompted to enter the PIN displayed on remote device. + * This is used for Bluetooth 2.0 pairing. + * @hide + */ + public static final int PAIRING_VARIANT_DISPLAY_PIN = 5; + + /** + * The user will be prompted to accept or deny the OOB pairing request + * @hide + */ + public static final int PAIRING_VARIANT_OOB_CONSENT = 6; + /** * Used as an extra field in {@link #ACTION_UUID} intents, * Contains the {@link android.os.ParcelUuid}s of the remote device which -- GitLab From 2a4fd10834c58099c87c84aa25659565b216b5af Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 6 Jan 2011 16:22:21 -0800 Subject: [PATCH 0146/1408] Remove auto connection on ACL connection. There is no need for this and causes problems with HID. Its the reponsibility of the remote device to initiate connection when back in range. Fixes bug: 3282682 Change-Id: I9fed15d4ce0ae4539059001db54cc302a55e2015 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 80a80bd6617..21144f22163 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -149,10 +149,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothInputDevice.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - Message msg = new Message(); - msg.what = AUTO_CONNECT_PROFILES; - sendMessage(msg); } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez -- GitLab From 87038c846bba91a4be39625eee446eb667b480f9 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 6 Jan 2011 16:22:21 -0800 Subject: [PATCH 0147/1408] DO NOT MERGE: Remove auto connection on ACL connection. There is no need for this and causes problems with certian cars. Fixes bug: 3281016 Change-Id: Idb65bbd4c8573923ebf82195b828ae071cb20aef --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 9be4c8f6f58..954fde55969 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -129,10 +129,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothA2dp.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - Message msg = new Message(); - msg.what = AUTO_CONNECT_PROFILES; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez -- GitLab From 1194821062e0b64ddcdc3a935b0058a114cedd25 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 13 Jan 2011 15:46:48 -0800 Subject: [PATCH 0148/1408] Fix bug in handling connect/disconnect multiple devices. Multiple HID devices can be connected. There is no pointing maintaining the global state. Check individual device state. Bug: 3350904 Change-Id: I03d9a6015e39e4f9d7f68cc8bbdb19731129b4e6 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 21144f22163..3280d39acca 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -102,7 +102,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothDevice mDevice; private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED; private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED; - private int mHidState = BluetoothProfile.STATE_DISCONNECTED; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -140,7 +139,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); int oldState = intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); - mHidState = newState; + if (oldState == BluetoothInputDevice.STATE_CONNECTED && newState == BluetoothInputDevice.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_INCOMING); @@ -286,7 +285,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(DISCONNECT_A2DP_OUTGOING); deferMessage(message); break; - } else if (mHidState != BluetoothInputDevice.STATE_DISCONNECTED) { + } else if (mService.getInputDeviceState(mDevice) != + BluetoothInputDevice.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_OUTGOING); deferMessage(message); break; -- GitLab From cee8d301f0b708c2922caaabb1b8bbf9730a6fdb Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 13 Jan 2011 22:50:51 -0800 Subject: [PATCH 0149/1408] Do Not Merge: Expose insecure rfcomm Bluetooth API. This complements the secure rfcomm API. The link key is unauthenticated and is subject to MITM attacks. The link key may be encrypted depending on the type of Bluetooth device. This helps apps which don't need the extra security or have their own security layer built on top of the rfcomm link. Bug: 3352266 Change-Id: I633fd0372e5e23288d6fec950dd1abc2896031f1 --- .../android/bluetooth/BluetoothAdapter.java | 56 ++++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 53 +++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3040319bebe..a7175e30369 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -729,6 +729,15 @@ public final class BluetoothAdapter { * Create a listening, secure RFCOMM Bluetooth socket. *

      A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link listenUsingInsecureRfcommOn}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. *

      Valid RFCOMM channels are in range 1 to 30. @@ -756,6 +765,15 @@ public final class BluetoothAdapter { * Create a listening, secure RFCOMM Bluetooth socket with Service Record. *

      A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link listenUsingInsecureRfcommWithServiceRecord}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. *

      The system will assign an unused RFCOMM channel to listen on. @@ -776,6 +794,42 @@ public final class BluetoothAdapter { */ public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, true, true); + } + + /** + * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. + *

      The link key will be unauthenticated i.e the communication is + * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, + * the link key will be encrypted, as encryption is mandartory. + * For legacy devices (pre Bluetooth 2.1 devices) the link key will not + * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an + * encrypted and authenticated communication channel is desired. + *

      Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

      The system will assign an unused RFCOMM channel to listen on. + *

      The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

      Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) + throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, false, false); + } + + private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, + boolean auth, boolean encrypt) throws IOException { RfcommChannelPicker picker = new RfcommChannelPicker(uuid); BluetoothServerSocket socket; @@ -789,7 +843,7 @@ public final class BluetoothAdapter { } socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel); + BluetoothSocket.TYPE_RFCOMM, auth, encrypt, channel); errno = socket.mSocket.bindListen(); if (errno == 0) { if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ada3c24e5d0..aee6ad86561 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -693,6 +693,15 @@ public final class BluetoothDevice implements Parcelable { * outgoing connection to this remote device on given channel. *

      The remote device will be authenticated and communication on this * socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link createInsecureRfcommSocket}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

      Valid RFCOMM channels are in range 1 to 30. @@ -720,11 +729,20 @@ public final class BluetoothDevice implements Parcelable { * determine which channel to connect to. *

      The remote device will be authenticated and communication on this * socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Hint: If you are connecting to a Bluetooth serial board then try * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. * However if you are connecting to an Android peer then please generate * your own unique UUID. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection @@ -736,6 +754,39 @@ public final class BluetoothDevice implements Parcelable { new ParcelUuid(uuid)); } + /** + * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure + * outgoing connection to this remote device using SDP lookup of uuid. + *

      The communication channel will not have an authenticated link key + * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1 + * devices, the link key will be encrypted, as encryption is mandatory. + * For legacy devices (pre Bluetooth 2.1 devices) the link key will + * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an + * encrypted and authenticated communication channel is desired. + *

      This is designed to be used with {@link + * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer + * Bluetooth applications. + *

      Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. This will also perform an SDP lookup of the given uuid to + * determine which channel to connect to. + *

      The remote device will be authenticated and communication on this + * socket will be encrypted. + *

      Hint: If you are connecting to a Bluetooth serial board then try + * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. + * However if you are connecting to an Android peer then please generate + * your own unique UUID. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param uuid service record uuid to lookup RFCOMM channel + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + */ + public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, + new ParcelUuid(uuid)); + } + /** * Construct an insecure RFCOMM socket ready to start an outgoing * connection. -- GitLab From c1cf3ea3e7381547907bf0240452d289c5f20919 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 19 Jan 2011 13:59:32 -0800 Subject: [PATCH 0150/1408] Make connection / disconnect failure more robust. Add error codes so that the states can be tracked better. Change-Id: Ic07a5d34589134b68dedeb4803ccb523aa01b567 --- .../android/bluetooth/BluetoothInputDevice.java | 13 +++++++++++++ framework/java/android/bluetooth/BluetoothPan.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index bc8a836183b..a70de59a48f 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -85,6 +85,19 @@ public final class BluetoothInputDevice { */ public static final int PRIORITY_UNDEFINED = -1; + /** + * Return codes for the connect and disconnect Bluez / Dbus calls. + */ + public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; + + public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; + + public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; + + public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; + + public static final int INPUT_OPERATION_SUCCESS = 5004; + private final IBluetooth mService; private final Context mContext; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 7dee25e4afa..1f07349b4c1 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -70,6 +70,19 @@ public final class BluetoothPan { public static final int STATE_CONNECTED = 2; public static final int STATE_DISCONNECTING = 3; + /** + * Return codes for the connect and disconnect Bluez / Dbus calls. + */ + public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; + + public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; + + public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; + + public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; + + public static final int PAN_OPERATION_SUCCESS = 1004; + private final IBluetooth mService; private final Context mContext; -- GitLab From 4883a5778fa6bb2ad3d4a0dad25efbc7edf46a7b Mon Sep 17 00:00:00 2001 From: Scott Main Date: Wed, 19 Jan 2011 21:13:18 -0800 Subject: [PATCH 0151/1408] docs: small javadoc, but also make the existing getProfileProxy docs visible... had wrong comment tag Change-Id: Ia4b2178057c0263ec8f835342815082de87b3af1 --- framework/java/android/bluetooth/BluetoothAdapter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b2185adc86d..fb3744d530c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1025,17 +1025,18 @@ public final class BluetoothAdapter { return null; } - /* + /** * Get the profile proxy object associated with the profile. * - *

      Profile can be one of {@link BluetoothProfile.HEADSET} or - * {@link BluetoothProfile.A2DP}. Clients must implements + *

      Profile can be one of {@link BluetoothProfile#HEADSET} or + * {@link BluetoothProfile#A2DP}. Clients must implements * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. - * @param profile + * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET} + * or {@link BluetoothProfile#A2DP}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, -- GitLab From 3eda3487992ae7f7256db9ca0a3ba8eed7203b3d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 20 Jan 2011 14:49:53 -0800 Subject: [PATCH 0152/1408] Temporary Changes for certification of SCO. Bug: 3349475 Change-Id: I555b127a1d9218494cd05c07c9e9f29628f3bc74 --- .../android/bluetooth/BluetoothHeadset.java | 20 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 2 ++ 2 files changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index d5b00420100..2959fc02d57 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -670,6 +670,26 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Send a AT command message to the headset. + * @param device Remote Bluetooth Device + * @param cmd The String to send. + * @hide + */ + public void sendAtCommand(BluetoothDevice device, String command) { + if (DBG) log("sendAtCommand()"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + mService.sendAtCommand(device, command); + } 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())); + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index e9521939ef8..3c6cf771133 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,4 +50,6 @@ interface IBluetoothHeadset { boolean startVirtualVoiceCall(in BluetoothDevice device); boolean stopVirtualVoiceCall(in BluetoothDevice device); + + void sendAtCommand(in BluetoothDevice device, String urc); } -- GitLab From ad73de5471595b559d86d32540a036ccc09ef499 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 20 Jan 2011 14:49:53 -0800 Subject: [PATCH 0153/1408] Temporary Changes for certification of SCO. Bug: 3349475 Change-Id: I555b127a1d9218494cd05c07c9e9f29628f3bc74 --- .../android/bluetooth/BluetoothHeadset.java | 20 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 2 ++ 2 files changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index d5b00420100..2959fc02d57 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -670,6 +670,26 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Send a AT command message to the headset. + * @param device Remote Bluetooth Device + * @param cmd The String to send. + * @hide + */ + public void sendAtCommand(BluetoothDevice device, String command) { + if (DBG) log("sendAtCommand()"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + mService.sendAtCommand(device, command); + } 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())); + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index e9521939ef8..3c6cf771133 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,4 +50,6 @@ interface IBluetoothHeadset { boolean startVirtualVoiceCall(in BluetoothDevice device); boolean stopVirtualVoiceCall(in BluetoothDevice device); + + void sendAtCommand(in BluetoothDevice device, String urc); } -- GitLab From 3ba236c74ddde5195ae26a9bce916f59723ac334 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 21 Jan 2011 18:58:17 -0800 Subject: [PATCH 0154/1408] Do Not Merge Connect other profiles when priority is auto connect. ACL auto connection was removed so other profiles won't auto connect when priority was auto connect. Reduced connect other profiles time delay. Bug: 3281016 Change-Id: Ic56ca6e95f618f02bc316d1f80014e58c4b6c3bc --- .../bluetooth/BluetoothDeviceProfileState.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 954fde55969..f6d7073cb30 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -75,6 +75,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int CONNECT_OTHER_PROFILES = 103; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs + private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -765,23 +766,26 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: // Connect A2DP if there is no incoming connection // If the priority is OFF - don't auto connect. - // If the priority is AUTO_CONNECT, auto connect code takes care. - if (mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_ON) { + if (mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_ON || + mA2dpService.getSinkPriority(mDevice) == + BluetoothA2dp.PRIORITY_AUTO_CONNECT) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_A2DP_OUTGOING; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); } break; case CONNECT_A2DP_INCOMING: // This is again against spec. HFP incoming connections should be made // before A2DP, so we should not hit this case. But many devices // don't follow this. - if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_ON) { + if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_ON + || mHeadsetService.getPriority(mDevice) == + BluetoothHeadset.PRIORITY_AUTO_CONNECT) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_HFP_OUTGOING; - sendMessageDelayed(msg, AUTO_CONNECT_DELAY); + sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); } break; default: -- GitLab From 0a0a845399e525a8dacc80c0d2241e1004673444 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 21 Jan 2011 18:58:17 -0800 Subject: [PATCH 0155/1408] Connect other profiles when priority is auto connect. ACL auto connection was removed so other profiles won't auto connect when priority was auto connect. Reduced connect other profiles time delay. Bug: 3281016 Change-Id: I8d98fd0d9bace193d619d7f241fff22f07e3e935 --- .../android/bluetooth/BluetoothDeviceProfileState.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 3280d39acca..6ec347f65b7 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -82,6 +82,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; + private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private BondedDevice mBondedDevice = new BondedDevice(); @@ -1010,8 +1011,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: // Connect A2DP if there is no incoming connection // If the priority is OFF - don't auto connect. - // If the priority is AUTO_CONNECT, auto connect code takes care. - if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { + if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON || + mA2dpService.getPriority(mDevice) == + BluetoothProfile.PRIORITY_AUTO_CONNECT) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_A2DP_OUTGOING; @@ -1023,7 +1025,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // before A2DP, so we should not hit this case. But many devices // don't follow this. if (mHeadsetService != null && - mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) { + (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON || + mHeadsetService.getPriority(mDevice) == + BluetoothProfile.PRIORITY_AUTO_CONNECT)) { Message msg = new Message(); msg.what = CONNECT_OTHER_PROFILES; msg.arg1 = CONNECT_HFP_OUTGOING; -- GitLab From eea6d26f8498ac5ee930d1dedc906b397cac17c2 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 24 Jan 2011 13:55:27 -0800 Subject: [PATCH 0156/1408] Rename virtual call API to ScoUsingVirtualCall. This API makes more sense with regard to headset profile. Bug: 3381532 Change-Id: Ife07d101a3b04bcddb31e7ae86712e1d97a660c5 --- .../android/bluetooth/BluetoothHeadset.java | 22 +++++++++++-------- .../android/bluetooth/IBluetoothHeadset.aidl | 4 ++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 2959fc02d57..a7e45182aa4 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -626,18 +626,21 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a Virtual Voice Call to the handsfree device (if connected). - * Allows the handsfree device to be used for routing non-cellular call audio + * Initiates a SCO channel connection with the headset (if connected). + * Also initiates a virtual voice call for Handsfree devices as many devices + * do not accept SCO audio without a call. + * This API allows the handsfree device to be used for routing non-cellular + * call audio. * * @param device Remote Bluetooth Device * @return true if successful, false if there was some error. * @hide */ - public boolean startVirtualVoiceCall(BluetoothDevice device) { - if (DBG) log("startVirtualVoiceCall()"); + public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { + if (DBG) log("startScoUsingVirtualVoiceCall()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.startVirtualVoiceCall(device); + return mService.startScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -649,17 +652,18 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Terminates an ongoing Virtual Voice Call to the handsfree device (if connected). + * Terminates an ongoing SCO connection and the associated virtual + * call. * * @param device Remote Bluetooth Device * @return true if successful, false if there was some error. * @hide */ - public boolean stopVirtualVoiceCall(BluetoothDevice device) { - if (DBG) log("stopVirtualVoiceCall()"); + public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { + if (DBG) log("stopScoUsingVirtualVoiceCall()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopVirtualVoiceCall(device); + return mService.stopScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 3c6cf771133..41f63b27721 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -48,8 +48,8 @@ interface IBluetoothHeadset { boolean setAudioState(in BluetoothDevice device, int state); int getAudioState(in BluetoothDevice device); - boolean startVirtualVoiceCall(in BluetoothDevice device); - boolean stopVirtualVoiceCall(in BluetoothDevice device); + boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); + boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); void sendAtCommand(in BluetoothDevice device, String urc); } -- GitLab From b34275754508ac58e3c92a636b4fe17249ebde70 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 25 Jan 2011 16:03:13 -0800 Subject: [PATCH 0157/1408] Improve readability of java docs for the public APIs. Change-Id: I0a0f1be04ed2ea2c14498a1a2ed4b050191ec95b --- .../java/android/bluetooth/BluetoothA2dp.java | 27 +++++--- .../android/bluetooth/BluetoothDevice.java | 18 +++++ .../android/bluetooth/BluetoothHeadset.java | 68 +++++++++++-------- .../android/bluetooth/BluetoothProfile.java | 17 +++-- 4 files changed, 83 insertions(+), 47 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 61b43036ef3..231f5920be5 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -50,15 +50,18 @@ public final class BluetoothA2dp implements BluetoothProfile { * profile. * *

      This intent will have 3 extras: - * {@link #EXTRA_STATE} - The current state of the profile. - * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + *

        + *
      • {@link #EXTRA_STATE} - The current state of the profile.
      • + *
      • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
      • + *
      • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
      • + *
      * * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = @@ -69,14 +72,17 @@ public final class BluetoothA2dp implements BluetoothProfile { * profile. * *

      This intent will have 3 extras: - * {@link #EXTRA_STATE} - The current state of the profile. - * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. + *

        + *
      • {@link #EXTRA_STATE} - The current state of the profile.
      • + *
      • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
      • + *
      • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
      • + *
      * * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PLAYING_STATE_CHANGED = @@ -258,7 +264,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Check if A2DP profile is streaming music. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device BluetoothDevice device */ @@ -281,11 +287,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * *

      This API will return false in scenarios like the A2DP * device is not in connected state etc. When this API returns, - * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED} + * true, it is guaranteed that {@link #ACTION_CONNECTION_STATE_CHANGED} * intent will be broadcasted with the state. Users can get the * state of the A2DP device from this intent. * *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. * * @param device Remote A2DP sink * @return false on immediate error, diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 24217d7dc01..254e2f813a0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -748,6 +748,15 @@ public final class BluetoothDevice implements Parcelable { * outgoing connection to this remote device on given channel. *

      The remote device will be authenticated and communication on this * socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link createInsecureRfcommSocket}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

      Valid RFCOMM channels are in range 1 to 30. @@ -775,6 +784,15 @@ public final class BluetoothDevice implements Parcelable { * determine which channel to connect to. *

      The remote device will be authenticated and communication on this * socket will be encrypted. + *

      Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. *

      Hint: If you are connecting to a Bluetooth serial board then try * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. * However if you are connecting to an Android peer then please generate diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index a7e45182aa4..f5f6092be1a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -52,15 +52,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * profile. * *

      This intent will have 3 extras: - * {@link #EXTRA_STATE} - The current state of the profile. - * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. - * + *

        + *
      • {@link #EXTRA_STATE} - The current state of the profile.
      • + *
      • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
      • + *
      • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
      • + *
      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = @@ -71,14 +73,16 @@ public final class BluetoothHeadset implements BluetoothProfile { * A2DP profile. * *

      This intent will have 3 extras: - * {@link #EXTRA_STATE} - The current state of the profile. - * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. - * + *

        + *
      • {@link #EXTRA_STATE} - The current state of the profile.
      • + *
      • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
      • + *
      • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
      • + *
      * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission + * to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = @@ -90,16 +94,18 @@ public final class BluetoothHeadset implements BluetoothProfile { * vendor-specific event. * *

      This intent will have 4 extras and 1 category. - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor specific - * command - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT command - * type. - * Can be one of {@link #AT_CMD_TYPE_READ}, {@link #AT_CMD_TYPE_TEST}, - * or {@link #AT_CMD_TYPE_SET}, {@link #AT_CMD_TYPE_BASIC}, - * {@link #AT_CMD_TYPE_ACTION}. - * - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command arguments. + *

        + *
      • {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device + *
      • + *
      • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor + * specific command
      • + *
      • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT + * command type which can be one of {@link #AT_CMD_TYPE_READ}, + * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, + * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}.
      • + *
      • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command + * arguments.
      • + *
      * * The category is the Company ID of the vendor defining the * vendor-specific command. {@link BluetoothAssignedNumbers} @@ -108,11 +114,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55 * *

      For example, an AT+XEVENT=foo,3 will get translated into - * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT - * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET - * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 - * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

        + *
      • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
      • + *
      • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
      • + *
      • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
      • + *
      + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission + * to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = @@ -184,7 +192,7 @@ public final class BluetoothHeadset implements BluetoothProfile { "android.bluetooth.headset.intent.category.companyid"; /** - * Headset state when SCO audio is not connected + * Headset state when SCO audio is not connected. * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. @@ -192,7 +200,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final int STATE_AUDIO_DISCONNECTED = 10; /** - * Headset state when SCO audio is connecting + * Headset state when SCO audio is connecting. * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. @@ -200,7 +208,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final int STATE_AUDIO_CONNECTING = 11; /** - * Headset state when SCO audio is connected + * Headset state when SCO audio is connected. * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. @@ -410,7 +418,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * Stop Bluetooth Voice Recognition mode, and shut down the * Bluetooth audio path. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset * @return false if there is no headset connected @@ -433,7 +441,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Check if Bluetooth SCO audio is connected. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset * @return true if SCO is connected, diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3949b26186e..ef80195d935 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -105,6 +105,7 @@ public interface BluetoothProfile { * from this intent. * *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, @@ -132,6 +133,7 @@ public interface BluetoothProfile { * two scenarios. * *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, @@ -145,20 +147,20 @@ public interface BluetoothProfile { * *

      Return the set of devices which are in state {@link #STATE_CONNECTED} * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return List of devices. The list will be empty on error. */ public List getConnectedDevices(); /** - * Get a set of devices that match any of the given connection + * Get a list of devices that match any of the given connection * states. * - *

      If none of devices match any of the given states, - * an empty set will be returned. + *

      If none of the devices match any of the given states, + * an empty list will be returned. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param states Array of states. States can be one of * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, @@ -170,7 +172,7 @@ public interface BluetoothProfile { /** * Get the current connection state of the profile * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Remote bluetooth device. * @return State of the profile connection. One of @@ -187,6 +189,7 @@ public interface BluetoothProfile { * {@link #PRIORITY_OFF}, * *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. * * @param device Paired bluetooth device * @param priority @@ -202,7 +205,7 @@ public interface BluetoothProfile { * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth device * @return priority of the device -- GitLab From c0e8198077109ac492d1cbbce40421f5784d89aa Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 25 Jan 2011 16:28:53 -0800 Subject: [PATCH 0158/1408] Revert "Temporary Changes for certification of SCO." This reverts commit 3eda3487992ae7f7256db9ca0a3ba8eed7203b3d. Change-Id: Ic63cdbbb06b9ed2d9c48e8b29cc376059ecadebf --- .../android/bluetooth/BluetoothHeadset.java | 20 ------------------- .../android/bluetooth/IBluetoothHeadset.aidl | 2 -- 2 files changed, 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index f5f6092be1a..768a6ff17c9 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -682,26 +682,6 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - /** - * Send a AT command message to the headset. - * @param device Remote Bluetooth Device - * @param cmd The String to send. - * @hide - */ - public void sendAtCommand(BluetoothDevice device, String command) { - if (DBG) log("sendAtCommand()"); - if (mService != null && isEnabled() && isValidDevice(device)) { - try { - mService.sendAtCommand(device, command); - } 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())); - } - } - private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 41f63b27721..273cda73bea 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,6 +50,4 @@ interface IBluetoothHeadset { boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - - void sendAtCommand(in BluetoothDevice device, String urc); } -- GitLab From 032e699b498082d1344f439ff69afee315528466 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Fri, 14 Jan 2011 16:29:58 -0800 Subject: [PATCH 0159/1408] Make the interface with DHCP IPv6 capable. It doesn't work (yet) for IPv6, but we can remove v4-centric notions from the framework. bug:2542681 Change-Id: I21c058f5c88d07706c9265bf0ea902fc90357e56 --- .../BluetoothTetheringDataTracker.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 7b083f10e2b..aa1adcbbc2a 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -18,7 +18,7 @@ package android.bluetooth; import android.content.Context; import android.net.ConnectivityManager; -import android.net.DhcpInfo; +import android.net.DhcpInfoInternal; import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; @@ -251,23 +251,12 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - DhcpInfo dhcpInfo = new DhcpInfo(); - if (!NetworkUtils.runDhcp(mIface, dhcpInfo)) { + DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); + if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } - mLinkProperties.addLinkAddress(new LinkAddress( - NetworkUtils.intToInetAddress(dhcpInfo.ipAddress), - NetworkUtils.intToInetAddress(dhcpInfo.netmask))); - mLinkProperties.setGateway(NetworkUtils.intToInetAddress(dhcpInfo.gateway)); - InetAddress dns1Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns1); - if (dns1Addr == null || dns1Addr.equals("0.0.0.0")) { - mLinkProperties.addDns(dns1Addr); - } - InetAddress dns2Addr = NetworkUtils.intToInetAddress(dhcpInfo.dns2); - if (dns2Addr == null || dns2Addr.equals("0.0.0.0")) { - mLinkProperties.addDns(dns2Addr); - } + mLinkProperties = dhcpInfoInternal.makeLinkProperties(); mLinkProperties.setInterfaceName(mIface); mNetworkInfo.setIsAvailable(true); -- GitLab From aa0427ea077352d2c5070a207402ebe5bb5d8750 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 26 Jan 2011 11:46:56 -0800 Subject: [PATCH 0160/1408] Fix some unmatched javadoc tags. Bug: 3392175 Change-Id: I84aa86c07060bc4ba399fd5c8f7cda162b432031 --- framework/java/android/bluetooth/BluetoothA2dp.java | 6 +++--- framework/java/android/bluetooth/BluetoothHeadset.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 231f5920be5..9246a103508 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -56,7 +56,7 @@ public final class BluetoothA2dp implements BluetoothProfile { *

    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • *
    * - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * @@ -75,10 +75,10 @@ public final class BluetoothA2dp implements BluetoothProfile { *

      *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • - *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • *
    * - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 768a6ff17c9..fa555209660 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -57,7 +57,7 @@ public final class BluetoothHeadset implements BluetoothProfile { *

  • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
  • *
  • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
  • * - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * @@ -78,7 +78,7 @@ public final class BluetoothHeadset implements BluetoothProfile { *

  • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
  • *
  • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
  • * - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission @@ -102,12 +102,12 @@ public final class BluetoothHeadset implements BluetoothProfile { *

  • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT * command type which can be one of {@link #AT_CMD_TYPE_READ}, * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, - * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}.
  • + * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}.
  • *
  • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command * arguments.
  • * * - * The category is the Company ID of the vendor defining the + *

    The category is the Company ID of the vendor defining the * vendor-specific command. {@link BluetoothAssignedNumbers} * * For example, for Plantronics specific events @@ -117,7 +117,7 @@ public final class BluetoothHeadset implements BluetoothProfile { *

      *
    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
    • *
    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
    • - *
    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
    • + *
    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
    • *
    *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission * to receive. -- GitLab From 6b683261b16e9f75724e0362400fb90af2b0e1bf Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 7 Jan 2011 14:38:23 -0800 Subject: [PATCH 0161/1408] Update package descriptions with editorial revisions. Notably, this removes exessive info about resources from the content package, because it's not a good location and the info is avilable in the dev guide, but also added some of the info to the Resources class description. Change-Id: Ie78af26c9cec66314deb98e53078f48e16c08e70 --- framework/java/android/bluetooth/package.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 9ac42dc5835..37505fd4047 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -3,7 +3,7 @@

    Provides classes that manage Bluetooth functionality, such as scanning for devices, connecting with devices, and managing data transfer between devices.

    -

    For a complete guide to using the Bluetooth APIs, see the For more information, see the Bluetooth developer guide.

    {@more} @@ -25,6 +25,6 @@ permission.

    Note: -Not all Android devices are guaranteed to have Bluetooth functionality.

    +Not all Android-powered devices provide Bluetooth functionality.

    -- GitLab From f9660ec83110660a3610a8b5cd2bf52d06dff548 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 1 Feb 2011 16:47:11 -0800 Subject: [PATCH 0162/1408] Fix BluetoothAdapter Connection change intent doc. Bug: 3414206 Change-Id: Icf87e28e11b0b5072fe546225bbfb1dc68487ef0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index fb3744d530c..4656e153915 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -296,12 +296,12 @@ public final class BluetoothAdapter { * can use this intent. * *

    This intent will have 3 extras: - * {@link #EXTRA_STATE} - The current state. - * {@link #EXTRA_PREVIOUS_STATE}- The previous. + * {@link #EXTRA_CONNECTION_STATE} - The current connection state. + * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state. * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. * - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE} + * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. -- GitLab From ad5d9c0ffd34c96faffdcee714e20ae7bd702ada Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 18 Feb 2011 14:52:32 -0800 Subject: [PATCH 0163/1408] Make BluetoothInputDevice inherit from BluetoothProfile. This makes it in sync with BluetoothHeadset and BluetoothA2dp profiles. Change-Id: I3ddb1d18b04aacb173b7bc376bca21c277a6afe4 --- .../android/bluetooth/BluetoothAdapter.java | 3 + .../BluetoothDeviceProfileState.java | 18 +- .../bluetooth/BluetoothInputDevice.java | 320 +++++++++--------- .../android/bluetooth/BluetoothProfile.java | 5 + .../bluetooth/BluetoothProfileState.java | 10 +- .../java/android/bluetooth/IBluetooth.aidl | 3 +- 6 files changed, 193 insertions(+), 166 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4656e153915..1f4fe801e63 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1049,6 +1049,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); return true; + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); + return true; } else { return false; } diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 6ec347f65b7..116a0687d5f 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -136,17 +136,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); int oldState = - intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); + intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - if (oldState == BluetoothInputDevice.STATE_CONNECTED && - newState == BluetoothInputDevice.STATE_DISCONNECTED) { + if (oldState == BluetoothProfile.STATE_CONNECTED && + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_INCOMING); } - if (newState == BluetoothInputDevice.STATE_CONNECTED || - newState == BluetoothInputDevice.STATE_DISCONNECTED) { + if (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { @@ -194,7 +194,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -286,7 +286,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(DISCONNECT_A2DP_OUTGOING); deferMessage(message); break; - } else if (mService.getInputDeviceState(mDevice) != + } else if (mService.getInputDeviceConnectionState(mDevice) != BluetoothInputDevice.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_OUTGOING); deferMessage(message); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index a70de59a48f..df212a82d4e 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 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. @@ -27,91 +27,88 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; + /** - * Public API for controlling the Bluetooth HID (Input Device) Profile - * - * BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service - * which handles the HID profile. + * This class provides the public APIs to control the Bluetooth Input + * Device Profile. * - * Creating a BluetoothInputDevice object will initiate a binding with the - * Bluetooth service. Users of this object should call close() when they - * are finished, so that this proxy object can unbind from the service. + *

    BluetoothInputDevice is a proxy object for controlling the Bluetooth + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothInputDevice proxy object. * - * Currently the Bluetooth service runs in the system server and this - * proxy object will be immediately bound to the service on construction. - * - * @hide + *

    Each method is protected with its appropriate permission. + *@hide */ -public final class BluetoothInputDevice { +public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; private static final boolean DBG = false; - /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ - public static final String EXTRA_INPUT_DEVICE_STATE = - "android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE"; - /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */ - public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE = - "android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE"; - - /** Indicates the state of an input device has changed. - * This intent will always contain EXTRA_INPUT_DEVICE_STATE, - * EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE - * extras. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_INPUT_DEVICE_STATE_CHANGED = - "android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED"; - - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; - - /** - * Auto connection, incoming and outgoing connection are allowed at this - * priority level. - */ - public static final int PRIORITY_AUTO_CONNECT = 1000; - /** - * Incoming and outgoing connection are allowed at this priority level - */ - public static final int PRIORITY_ON = 100; - /** - * Connections to the device are not allowed at this priority level. - */ - public static final int PRIORITY_OFF = 0; /** - * Default priority level when the device is unpaired. + * Intent used to broadcast the change in connection state of the Input + * Device profile. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ - public static final int PRIORITY_UNDEFINED = -1; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * @hide */ public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; + /** + * @hide + */ public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; + /** + * @hide + */ public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; + /** + * @hide + */ public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; + /** + * @hide + */ public static final int INPUT_OPERATION_SUCCESS = 5004; - private final IBluetooth mService; - private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetooth mService; /** * Create a BluetoothInputDevice proxy object for interacting with the local - * Bluetooth Service which handle the HID profile. - * @param c Context + * Bluetooth Service which handles the InputDevice profile + * */ - public BluetoothInputDevice(Context c) { - mContext = c; - + /*package*/ BluetoothInputDevice(Context mContext, ServiceListener l) { IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { mService = IBluetooth.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, this); + } } else { Log.w(TAG, "Bluetooth Service not available!"); @@ -121,130 +118,151 @@ public final class BluetoothInputDevice { } } - /** Initiate a connection to an Input device. - * - * This function returns false on error and true if the connection - * attempt is being made. - * - * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the - * connection is completed. - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean connectInputDevice(BluetoothDevice device) { - if (DBG) log("connectInputDevice(" + device + ")"); - try { - return mService.connectInputDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Initiate disconnect from an Input Device. - * This function return false on error and true if the disconnection - * attempt is being made. - * - * Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when - * disconnect is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise - * @hide + /** + * {@inheritDoc} + * @hide */ - public boolean disconnectInputDevice(BluetoothDevice device) { - if (DBG) log("disconnectInputDevice(" + device + ")"); - try { - return mService.disconnectInputDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnectInputDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } - /** Check if a specified InputDevice is connected. - * - * @param device Remote BT device. - * @return True if connected , false otherwise and on error. - * @hide + /** + * {@inheritDoc} */ - public boolean isInputDeviceConnected(BluetoothDevice device) { - if (DBG) log("isInputDeviceConnected(" + device + ")"); - int state = getInputDeviceState(device); - if (state == STATE_CONNECTED) return true; - return false; + public List getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedInputDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); } - /** Check if any Input Device is connected. - * - * @return List of devices, empty List on error. - * @hide + /** + * {@inheritDoc} */ - public List getConnectedInputDevices() { - if (DBG) log("getConnectedInputDevices()"); - try { - return mService.getConnectedInputDevices(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return new ArrayList(); + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getInputDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); } - /** Get the state of an Input Device. - * - * @param device Remote BT device. - * @return The current state of the Input Device - * @hide + /** + * {@inheritDoc} */ - public int getInputDeviceState(BluetoothDevice device) { - if (DBG) log("getInputDeviceState(" + device + ")"); - try { - return mService.getInputDeviceState(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return STATE_DISCONNECTED; + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getInputDeviceConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + 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 an input device. - * - * Priority is a non-negative integer. Priority can take the following - * values: - * {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT} - * - * @param device Paired device. - * @param priority Integer priority - * @return true if priority is set, false on error + * {@inheritDoc} + * @hide */ - public boolean setInputDevicePriority(BluetoothDevice device, int priority) { - if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")"); - try { - return mService.setInputDevicePriority(device, priority); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return mService.setInputDevicePriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get the priority associated with an Input Device. - * - * @param device Input Device - * @return non-negative priority, or negative error code on error. + * {@inheritDoc} + * @hide */ - public int getInputDevicePriority(BluetoothDevice device) { - if (DBG) log("getInputDevicePriority(" + device + ")"); - try { - return mService.getInputDevicePriority(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return PRIORITY_OFF; + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getInputDevicePriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index ef80195d935..1ffee720539 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -62,6 +62,11 @@ public interface BluetoothProfile { * A2DP profile. */ public static final int A2DP = 2; + /** + * Input Device Profile + * @hide + */ + public static final int INPUT_DEVICE = 3; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index 3f36926a9ea..18060a0234d 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -72,10 +72,10 @@ public class BluetoothProfileState extends HierarchicalStateMachine { newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } - } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); - if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED || - newState == BluetoothInputDevice.STATE_DISCONNECTED)) { + } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); + if (mProfile == HID && (newState == BluetoothProfile.STATE_CONNECTED || + newState == BluetoothProfile.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { @@ -96,7 +96,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); context.registerReceiver(mBroadcastReceiver, filter); } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index f3e73cf5db2..69fb06a0f7b 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -85,7 +85,8 @@ interface IBluetooth boolean connectInputDevice(in BluetoothDevice device); boolean disconnectInputDevice(in BluetoothDevice device); List getConnectedInputDevices(); - int getInputDeviceState(in BluetoothDevice device); + List getInputDevicesMatchingConnectionStates(in int[] states); + int getInputDeviceConnectionState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); -- GitLab From 97f8ec41a556abc487ee412344e4f6c4c52613cb Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 23 Feb 2011 10:22:15 -0800 Subject: [PATCH 0164/1408] Make BluetoothPan inherit from BluetoothProfile. Change-Id: Ibd3e24e391be93ebe2cf975bd075efb68e10c1ff --- .../android/bluetooth/BluetoothAdapter.java | 3 + .../java/android/bluetooth/BluetoothPan.java | 276 +++++++++++------- .../android/bluetooth/BluetoothProfile.java | 8 + .../BluetoothTetheringDataTracker.java | 21 +- .../java/android/bluetooth/IBluetooth.aidl | 3 +- 5 files changed, 206 insertions(+), 105 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1f4fe801e63..e1c904420a3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1052,6 +1052,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; + } else if (profile == BluetoothProfile.PAN) { + BluetoothPan pan = new BluetoothPan(context, listener); + return true; } else { return false; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 1f07349b4c1..9ffed26f4ac 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,187 +27,261 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; + /** - * @hide + * This class provides the APIs to control the Bluetooth Pan + * Profile. + * + *

    BluetoothPan is a proxy object for controlling the Bluetooth + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothPan proxy object. + * + *

    Each method is protected with its appropriate permission. + *@hide */ -public final class BluetoothPan { +public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = false; - //TODO: This needs to inherit from BluetoothProfile like other profiles. - - /** int extra for ACTION_PAN_STATE_CHANGED */ - public static final String EXTRA_PAN_STATE = "android.bluetooth.pan.extra.STATE"; - - /** int extra for ACTION_PAN_STATE_CHANGED */ - public static final String EXTRA_PREVIOUS_PAN_STATE = - "android.bluetooth.pan.extra.PREVIOUS_STATE"; + /** + * Intent used to broadcast the change in connection state of the Pan + * profile. + * + *

    This intent will have 4 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    • {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is + * bound to.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or + * {@link #LOCAL_PANU_ROLE} + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; - /** int extra for ACTION_PAN_STATE_CHANGED */ + /** + * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent + * The local role of the PAN profile that the remote device is bound to. + * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}. + */ public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + /** + * The local device is acting as a Network Access Point. + */ public static final int LOCAL_NAP_ROLE = 1; - public static final int LOCAL_PANU_ROLE = 2; /** - * Indicates the state of an PAN device has changed. - * This intent will always contain EXTRA_DEVICE_STATE, - * EXTRA_PREVIOUS_DEVICE_STATE, BluetoothDevice.EXTRA_DEVICE - * and EXTRA_LOCAL_ROLE. - * extras. + * The local device is acting as a PAN User. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PAN_STATE_CHANGED = - "android.bluetooth.pan.action.STATE_CHANGED"; - - public static final String NAP_ROLE = "nap"; - public static final String NAP_BRIDGE = "pan1"; - - public static final int MAX_CONNECTIONS = 7; - - public static final int STATE_DISCONNECTED = 0; - public static final int STATE_CONNECTING = 1; - public static final int STATE_CONNECTED = 2; - public static final int STATE_DISCONNECTING = 3; + public static final int LOCAL_PANU_ROLE = 2; /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * @hide */ public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; + /** + * @hide + */ public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; + /** + * @hide + */ public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; + /** + * @hide + */ public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; + /** + * @hide + */ public static final int PAN_OPERATION_SUCCESS = 1004; - private final IBluetooth mService; - private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetooth mService; /** * Create a BluetoothPan proxy object for interacting with the local - * Bluetooth Pan service. - * @param c Context + * Bluetooth Service which handles the Pan profile + * */ - public BluetoothPan(Context c) { - mContext = c; - + /*package*/ BluetoothPan(Context mContext, ServiceListener l) { IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { mService = IBluetooth.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.PAN, this); + } } else { Log.w(TAG, "Bluetooth Service not available!"); // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later - // when it is actually used. + // into Wireless settings in the emulator. Let it crash later when it is actually used. mService = null; } } /** - * Initiate a PAN connection. - * - * This function returns false on error and true if the connection - * attempt is being made. - * - * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when the - * connection is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise + * {@inheritDoc} * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - try { - return mService.connectPanDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Initiate disconnect from PAN. - * - * This function return false on error and true if the disconnection - * attempt is being made. - * - * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when - * disconnect is completed. - * - * @param device Remote BT device. - * @return false on immediate error, true otherwise + * {@inheritDoc} * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - try { - return mService.disconnectPanDevice(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnectPanDevice(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** - * Get the state of a PAN Device. - * - * This function returns an int representing the state of the PAN connection - * - * @param device Remote BT device. - * @return The current state of the PAN Device - * @hide + * {@inheritDoc} */ - public int getPanDeviceState(BluetoothDevice device) { - if (DBG) log("getPanDeviceState(" + device + ")"); - try { - return mService.getPanDeviceState(device); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return STATE_DISCONNECTED; + public List getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedPanDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); } /** - * Returns a set of all the connected PAN Devices - * - * Does not include devices that are currently connecting or disconnecting - * - * @return List of PAN devices or empty on Error + * {@inheritDoc} + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getPanDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPanDeviceConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * {@inheritDoc} * @hide */ - public List getConnectedDevices() { - if (DBG) log("getConnectedDevices"); - try { - return mService.getConnectedPanDevices(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return new ArrayList(); - } + public boolean setPriority(BluetoothDevice device, int priority) { + // Priorities are not supported for PAN devices - since we don't + // auto connect. + return false; } - private static void log(String msg) { - Log.d(TAG, msg); + /** + * {@inheritDoc} + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (DBG) log("getPriority(" + device + ")"); + // Priorities are not supported for PAN devices - since we don't + // auto connect. + return BluetoothProfile.PRIORITY_ON; } public void setBluetoothTethering(boolean value) { + if (DBG) log("setBluetoothTethering(" + value + ")"); try { mService.setBluetoothTethering(value); } catch (RemoteException e) { - Log.e(TAG, "", e); + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } } public boolean isTetheringOn() { + if (DBG) log("isTetheringOn()"); try { return mService.isTetheringOn(); } catch (RemoteException e) { - Log.e(TAG, "", e); + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } -} + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 1ffee720539..1ad66f71b93 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -58,16 +58,24 @@ public interface BluetoothProfile { * Headset and Handsfree profile */ public static final int HEADSET = 1; + /** * A2DP profile. */ public static final int A2DP = 2; + /** * Input Device Profile * @hide */ public static final int INPUT_DEVICE = 3; + /** + * PAN Profile + * @hide + */ + public static final int PAN = 4; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index aa1adcbbc2a..c08f14f3f81 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -96,17 +96,32 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void startMonitoring(Context context, Handler target) { mContext = context; mCsHandler = target; - mBluetoothPan = new BluetoothPan(mContext); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); + } } + private BluetoothProfile.ServiceListener mProfileServiceListener = + new BluetoothProfile.ServiceListener() { + public void onServiceConnected(int profile, BluetoothProfile proxy) { + mBluetoothPan = (BluetoothPan) proxy; + } + public void onServiceDisconnected(int profile) { + mBluetoothPan = null; + } + }; + /** * Disable connectivity to a network * TODO: do away with return value after making MobileDataStateTracker async */ public boolean teardown() { mTeardownRequested.set(true); - for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { - mBluetoothPan.disconnect(device); + if (mBluetoothPan != null) { + for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { + mBluetoothPan.disconnect(device); + } } return true; } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 69fb06a0f7b..d25f5d0cb33 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -92,8 +92,9 @@ interface IBluetooth boolean isTetheringOn(); void setBluetoothTethering(boolean value); - int getPanDeviceState(in BluetoothDevice device); + int getPanDeviceConnectionState(in BluetoothDevice device); List getConnectedPanDevices(); + List getPanDevicesMatchingConnectionStates(in int[] states); boolean connectPanDevice(in BluetoothDevice device); boolean disconnectPanDevice(in BluetoothDevice device); -- GitLab From 6d32c9913d4bb29878b3640d5e6c82ba3b83d2de Mon Sep 17 00:00:00 2001 From: Jake Hamby Date: Thu, 9 Dec 2010 14:47:57 -0800 Subject: [PATCH 0165/1408] Refactor android.server.BluetoothService classes. Several cleanups to BluetoothService: - Move BluetoothService.BondState inner class to top level. - Extract adapter and remote device properties cache management logic into two new classes: BluetoothAdapterProperties and BluetoothDeviceProperties. - Add getter methods for other classes in the package to access the new properties cache objects for multi-part operations. - Inline log() method in BluetoothService and selected the appropriate Log method (Log.d, Log.w, Log.e) for each message. - Refactor dump() method into smaller sized pieces. - Clean up logic of updateCountersAndCheckForConnectionStateChange() method for better readability. - Change sendConnectionStateChange() to return instead of sending an intent if the current or previous state values are invalid. Previously the code sent an intent with -1 for the invalid state. - Added Javadoc comments to document the methods that are called from native Bluez code. - Fixed some typos and code style issues. Original Change by: Jake Hamby Modified by: Jaikumar Ganesh Change-Id: I76ebac00ecd29566dcb2d1ad3e7a486b7530ce24 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 116a0687d5f..98557094b91 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -82,7 +82,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; - private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private BondedDevice mBondedDevice = new BondedDevice(); -- GitLab From ffac0742635a6ea5cbc68f4418475b3cf85712bb Mon Sep 17 00:00:00 2001 From: Jozef BABJAK Date: Tue, 22 Feb 2011 09:48:18 +0100 Subject: [PATCH 0166/1408] Removing dead store; it was likely a relict of debuging code. Change-Id: I4daab1359d7c4b300ef61e3de1578d9fecb6f8da --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index f6d7073cb30..9d7e641ecd2 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -596,7 +596,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine @Override protected boolean processMessage(Message message) { log("IncomingA2dp State->Processing Message: " + message.what); - Message deferMsg = new Message(); switch(message.what) { case CONNECT_HFP_OUTGOING: deferMessage(message); -- GitLab From 854211a15e3767d25ac6412cf8550febdfede6af Mon Sep 17 00:00:00 2001 From: Albert Mojir Date: Mon, 21 Mar 2011 14:59:10 +0100 Subject: [PATCH 0167/1408] Bluetooth: correcting return value from cancelDiscovery BluetoothAdapter.cancelDiscovery was previously always returning false. Change-Id: Ic1fd134d4b710438d95c5b8ca009104529dd1bf5 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index a7175e30369..b61e40fd92b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -628,7 +628,7 @@ public final class BluetoothAdapter { public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { - mService.cancelDiscovery(); + return mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } -- GitLab From 2fbe8f4fab096b587259180f4b0a6582a9450b04 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 6 Apr 2011 11:09:30 -0700 Subject: [PATCH 0168/1408] Check if BT is disabled or not when sending audio state. When BT is turning off we need to send the audio state intent. We were not doing this since the state was turning off. The check was added to prevent misuse of public APIs, since these functions are used internally, its not a problem. Change-Id: I9834cbff741722bf99c2875fa65c0617b7b4807b --- framework/java/android/bluetooth/BluetoothHeadset.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index fa555209660..8a9bef05f44 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -603,7 +603,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean setAudioState(BluetoothDevice device, int state) { if (DBG) log("setAudioState"); - if (mService != null && isEnabled()) { + if (mService != null && !isDisabled()) { try { return mService.setAudioState(device, state); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -622,7 +622,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getAudioState(BluetoothDevice device) { if (DBG) log("getAudioState"); - if (mService != null && isEnabled()) { + if (mService != null && !isDisabled()) { try { return mService.getAudioState(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} @@ -705,6 +705,11 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + private boolean isDisabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; + return false; + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 8effc2c7fab0bbfcaca3f6a05191e4fd47d4979d Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Fri, 25 Mar 2011 13:09:25 -0700 Subject: [PATCH 0169/1408] Add external dependency API. An APN will not be connected to if some external dependency is not met. bug:3486704 Change-Id: I7d94df343b260013efd11faa978deb13f07f1389 --- .../java/android/bluetooth/BluetoothTetheringDataTracker.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index c08f14f3f81..a7b00375e84 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -300,4 +300,8 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); } + + public void setDependencyMet(boolean met) { + // not supported on this network + } } -- GitLab From dcfa81f16feeaaa6421d60056c0173c2f364a3ea Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Mon, 18 Apr 2011 14:55:10 -0700 Subject: [PATCH 0170/1408] Rename HierarchicalStateMachine and HierarchicalState to StateMachine and State. Change-Id: Ib4b33894da75f5f156066092fb145b478e52f7d7 --- .../BluetoothDeviceProfileState.java | 48 +++++++++---------- .../bluetooth/BluetoothProfileState.java | 18 +++---- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 98557094b91..6f3a2e780d3 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -26,8 +26,8 @@ import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.util.Log; -import com.android.internal.util.HierarchicalState; -import com.android.internal.util.HierarchicalStateMachine; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; import java.util.Set; @@ -57,7 +57,7 @@ import java.util.Set; * Todo(): Write tests for this class, when the Android Mock support is completed. * @hide */ -public final class BluetoothDeviceProfileState extends HierarchicalStateMachine { +public final class BluetoothDeviceProfileState extends StateMachine { private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = false; @@ -235,16 +235,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class BondedDevice extends HierarchicalState { + private class BondedDevice extends State { @Override - protected void enter() { + public void enter() { Log.i(TAG, "Entering ACL Connected state with: " + getCurrentMessage().what); Message m = new Message(); m.copyFrom(getCurrentMessage()); sendMessageAtFrontOfQueue(m); } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("ACL Connected State -> Processing Message: " + message.what); switch(message.what) { case CONNECT_HFP_OUTGOING: @@ -353,12 +353,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class OutgoingHandsfree extends HierarchicalState { + private class OutgoingHandsfree extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { Log.i(TAG, "Entering OutgoingHandsfree state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HFP_OUTGOING && @@ -374,7 +374,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("OutgoingHandsfree State -> Processing Message: " + message.what); Message deferMsg = new Message(); int command = message.what; @@ -466,12 +466,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class IncomingHandsfree extends HierarchicalState { + private class IncomingHandsfree extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { Log.i(TAG, "Entering IncomingHandsfree state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HFP_INCOMING && @@ -487,7 +487,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("IncomingHandsfree State -> Processing Message: " + message.what); switch(message.what) { case CONNECT_HFP_OUTGOING: @@ -546,12 +546,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class OutgoingA2dp extends HierarchicalState { + private class OutgoingA2dp extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { Log.i(TAG, "Entering OutgoingA2dp state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_A2DP_OUTGOING && @@ -567,7 +567,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("OutgoingA2dp State->Processing Message: " + message.what); Message deferMsg = new Message(); switch(message.what) { @@ -656,12 +656,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class IncomingA2dp extends HierarchicalState { + private class IncomingA2dp extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { Log.i(TAG, "Entering IncomingA2dp state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_A2DP_INCOMING && @@ -677,7 +677,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("IncomingA2dp State->Processing Message: " + message.what); Message deferMsg = new Message(); switch(message.what) { @@ -735,12 +735,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } - private class OutgoingHid extends HierarchicalState { + private class OutgoingHid extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { log("Entering OutgoingHid state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HID_OUTGOING && @@ -752,7 +752,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("OutgoingHid State->Processing Message: " + message.what); Message deferMsg = new Message(); switch(message.what) { @@ -816,12 +816,12 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } } - private class IncomingHid extends HierarchicalState { + private class IncomingHid extends State { private boolean mStatus = false; private int mCommand; @Override - protected void enter() { + public void enter() { log("Entering IncomingHid state with: " + getCurrentMessage().what); mCommand = getCurrentMessage().what; if (mCommand != CONNECT_HID_INCOMING && @@ -833,7 +833,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } @Override - protected boolean processMessage(Message message) { + public boolean processMessage(Message message) { log("IncomingHid State->Processing Message: " + message.what); Message deferMsg = new Message(); switch(message.what) { diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index 18060a0234d..98afdb82a8e 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -22,8 +22,8 @@ import android.content.IntentFilter; import android.os.Message; import android.util.Log; -import com.android.internal.util.HierarchicalState; -import com.android.internal.util.HierarchicalStateMachine; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; /** * This state machine is used to serialize the connections @@ -39,7 +39,7 @@ import com.android.internal.util.HierarchicalStateMachine; * @hide */ -public class BluetoothProfileState extends HierarchicalStateMachine { +public class BluetoothProfileState extends StateMachine { private static final boolean DBG = true; private static final String TAG = "BluetoothProfileState"; @@ -101,15 +101,15 @@ public class BluetoothProfileState extends HierarchicalStateMachine { context.registerReceiver(mBroadcastReceiver, filter); } - private class StableState extends HierarchicalState { + private class StableState extends State { @Override - protected void enter() { + public void enter() { log("Entering Stable State"); mPendingDevice = null; } @Override - protected boolean processMessage(Message msg) { + public boolean processMessage(Message msg) { if (msg.what != TRANSITION_TO_STABLE) { transitionTo(mPendingCommandState); } @@ -117,15 +117,15 @@ public class BluetoothProfileState extends HierarchicalStateMachine { } } - private class PendingCommandState extends HierarchicalState { + private class PendingCommandState extends State { @Override - protected void enter() { + public void enter() { log("Entering PendingCommandState State"); dispatchMessage(getCurrentMessage()); } @Override - protected boolean processMessage(Message msg) { + public boolean processMessage(Message msg) { if (msg.what == TRANSITION_TO_STABLE) { transitionTo(mStableState); } else { -- GitLab From efd744ddb96769e41092d56f0cebf809e80f2a8e Mon Sep 17 00:00:00 2001 From: Mathias Jeppsson Date: Mon, 21 Mar 2011 15:06:52 +0100 Subject: [PATCH 0171/1408] Require bonding and encryption for PBAP server The Phonebook Access Profile specification requires bonding and encryption. For devices not supporting SSP (Secure Simple Pairing), InsecureRfcomm will require neither. Adding EncryptedRfcomm to force bonding and encryption but not requiring authenticated link key. Change-Id: If47cca9c5ffd89358bcd61d64f7785d17e0ca7cc --- .../android/bluetooth/BluetoothAdapter.java | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index a7175e30369..66a74503c9d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -799,10 +799,10 @@ public final class BluetoothAdapter { /** * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. - *

    The link key will be unauthenticated i.e the communication is + *

    The link key is not required to be authenticated, i.e the communication may be * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, - * the link key will be encrypted, as encryption is mandartory. - * For legacy devices (pre Bluetooth 2.1 devices) the link key will not + * the link will be encrypted, as encryption is mandartory. + * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an * encrypted and authenticated communication channel is desired. *

    Use {@link BluetoothServerSocket#accept} to retrieve incoming @@ -828,6 +828,44 @@ public final class BluetoothAdapter { return createNewRfcommSocketAndRecord(name, uuid, false, false); } + /** + * Create a listening, encrypted, + * RFCOMM Bluetooth socket with Service Record. + *

    The link will be encrypted, but the link key is not required to be authenticated + * i.e the communication is vulnerable to Man In the Middle attacks. Use + * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. + *

    Use this socket if authentication of link key is not possible. + * For example, for Bluetooth 2.1 devices, if any of the devices does not have + * an input and output capability or just has the ability to display a numeric key, + * a secure socket connection is not possible and this socket can be used. + * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. + * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. + *

    Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

    The system will assign an unused RFCOMM channel to listen on. + *

    The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

    Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to + * connect to this socket from another device using the same {@link UUID}. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord( + String name, UUID uuid) throws IOException { + return createNewRfcommSocketAndRecord(name, uuid, false, true); + } + private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { RfcommChannelPicker picker = new RfcommChannelPicker(uuid); @@ -898,6 +936,28 @@ public final class BluetoothAdapter { return socket; } + /** + * Construct an encrypted, RFCOMM server socket. + * Call #accept to retrieve connections to this socket. + * @return An RFCOMM BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) + throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, false, true, port); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); + } + return socket; + } + /** * Construct a SCO server socket. * Call #accept to retrieve connections to this socket. -- GitLab From 638350f52c9dd10f72709bf19bd3010225478dae Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 3 May 2011 19:50:20 -0700 Subject: [PATCH 0172/1408] Add public api BluetoothSocket.isConnected. It's a convenent function to get the connection status of the bluetooth socket, ie, whether there is an active connection with remote device. bug 2149043 Change-Id: I4ca4e98c646f70746d838632f3035376b22d579b --- .../android/bluetooth/BluetoothSocket.java | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 719d730770d..9a13c3eb070 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -94,10 +94,16 @@ public final class BluetoothSocket implements Closeable { private int mPort; /* RFCOMM channel or L2CAP psm */ + private enum SocketState { + INIT, + CONNECTED, + CLOSED + } + /** prevents all native calls after destroyNative() */ - private boolean mClosed; + private SocketState mSocketState; - /** protects mClosed */ + /** protects mSocketState */ private final ReentrantReadWriteLock mLock; /** used by native code only */ @@ -145,7 +151,7 @@ public final class BluetoothSocket implements Closeable { } mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); - mClosed = false; + mSocketState = SocketState.INIT; mLock = new ReentrantReadWriteLock(); } @@ -195,13 +201,14 @@ public final class BluetoothSocket implements Closeable { public void connect() throws IOException { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mSdp != null) { mPort = mSdp.doSdp(); // blocks } connectNative(); // blocks + mSocketState = SocketState.CONNECTED; } finally { mLock.readLock().unlock(); } @@ -216,7 +223,7 @@ public final class BluetoothSocket implements Closeable { // abort blocking operations on the socket mLock.readLock().lock(); try { - if (mClosed) return; + if (mSocketState == SocketState.CLOSED) return; if (mSdp != null) { mSdp.cancel(); } @@ -229,7 +236,7 @@ public final class BluetoothSocket implements Closeable { // abortNative(), so this lock should immediately acquire mLock.writeLock().lock(); try { - mClosed = true; + mSocketState = SocketState.CLOSED; destroyNative(); } finally { mLock.writeLock().unlock(); @@ -266,6 +273,16 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } + /** + * Get the connection status of this socket, ie, whether there is an active connection with + * remote device. + * @return true if connected + * false if not connected + */ + public boolean isConnected() { + return (mSocketState == SocketState.CONNECTED); + } + /** * Currently returns unix errno instead of throwing IOException, * so that BluetoothAdapter can check the error code for EADDRINUSE @@ -273,7 +290,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { mLock.readLock().lock(); try { - if (mClosed) return EBADFD; + if (mSocketState == SocketState.CLOSED) return EBADFD; return bindListenNative(); } finally { mLock.readLock().unlock(); @@ -283,8 +300,11 @@ public final class BluetoothSocket implements Closeable { /*package*/ BluetoothSocket accept(int timeout) throws IOException { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); - return acceptNative(timeout); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + + BluetoothSocket acceptedSocket = acceptNative(timeout); + mSocketState = SocketState.CONNECTED; + return acceptedSocket; } finally { mLock.readLock().unlock(); } @@ -293,7 +313,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int available() throws IOException { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); return availableNative(); } finally { mLock.readLock().unlock(); @@ -303,7 +323,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int read(byte[] b, int offset, int length) throws IOException { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); return readNative(b, offset, length); } finally { mLock.readLock().unlock(); @@ -313,7 +333,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int write(byte[] b, int offset, int length) throws IOException { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); return writeNative(b, offset, length); } finally { mLock.readLock().unlock(); -- GitLab From 5af87ff2885bddcbbc8daa559ca632da0f743e34 Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Fri, 27 May 2011 17:09:06 -0700 Subject: [PATCH 0173/1408] Move bluetooth tests to their own package. Bug 4501764 Change-Id: Id65de5d96516720e75dfbb5d522f465187e73913 --- framework/tests/Android.mk | 17 + framework/tests/AndroidManifest.xml | 32 + .../bluetooth/BluetoothRebootStressTest.java | 94 ++ .../bluetooth/BluetoothStressTest.java | 390 +++++ .../bluetooth/BluetoothTestRunner.java | 214 +++ .../android/bluetooth/BluetoothTestUtils.java | 1487 +++++++++++++++++ 6 files changed, 2234 insertions(+) create mode 100644 framework/tests/Android.mk create mode 100644 framework/tests/AndroidManifest.xml create mode 100644 framework/tests/src/android/bluetooth/BluetoothRebootStressTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothStressTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothTestRunner.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothTestUtils.java diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk new file mode 100644 index 00000000000..4a1d18cb5c0 --- /dev/null +++ b/framework/tests/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := BluetoothTests +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml new file mode 100644 index 00000000000..96db035d3e4 --- /dev/null +++ b/framework/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/framework/tests/src/android/bluetooth/BluetoothRebootStressTest.java b/framework/tests/src/android/bluetooth/BluetoothRebootStressTest.java new file mode 100644 index 00000000000..33e9dd7fabc --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothRebootStressTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 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.content.Context; +import android.test.InstrumentationTestCase; + +/** + * Instrumentation test case for stress test involving rebooting the device. + *

    + * This test case tests that bluetooth is enabled after a device reboot. Because + * the device will reboot, the instrumentation must be driven by a script on the + * host side. + */ +public class BluetoothRebootStressTest extends InstrumentationTestCase { + private static final String TAG = "BluetoothRebootStressTest"; + private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt"; + + private BluetoothTestUtils mTestUtils; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context context = getInstrumentation().getTargetContext(); + mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + mTestUtils.close(); + } + + /** + * Test method used to start the test by turning bluetooth on. + */ + public void testStart() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.enable(adapter); + } + + /** + * Test method used in the middle iterations of the test to check if + * bluetooth is on. Does not toggle bluetooth after the check. Assumes that + * bluetooth has been turned on by {@code #testStart()} + */ + public void testMiddleNoToggle() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + } + + /** + * Test method used in the middle iterations of the test to check if + * bluetooth is on. Toggles bluetooth after the check. Assumes that + * bluetooth has been turned on by {@code #testStart()} + */ + public void testMiddleToggle() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + } + + /** + * Test method used in the stop the test by turning bluetooth off. Assumes + * that bluetooth has been turned on by {@code #testStart()} + */ + public void testStop() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + assertTrue(adapter.isEnabled()); + + mTestUtils.disable(adapter); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java new file mode 100644 index 00000000000..abd7d9af224 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2010 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.content.Context; +import android.test.InstrumentationTestCase; + +/** + * Stress test suite for Bluetooth related functions. + * + * Includes tests for enabling/disabling bluetooth, enabling/disabling discoverable mode, + * starting/stopping scans, connecting/disconnecting to HFP, A2DP, HID, PAN profiles, and verifying + * that remote connections/disconnections occur for the PAN profile. + *

    + * This test suite uses {@link android.bluetooth.BluetoothTestRunner} to for parameters such as the + * number of iterations and the addresses of remote Bluetooth devices. + */ +public class BluetoothStressTest extends InstrumentationTestCase { + private static final String TAG = "BluetoothStressTest"; + private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt"; + + private BluetoothTestUtils mTestUtils; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context context = getInstrumentation().getTargetContext(); + mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + mTestUtils.close(); + } + + /** + * Stress test for enabling and disabling Bluetooth. + */ + public void testEnable() { + int iterations = BluetoothTestRunner.sEnableIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.disable(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations); + mTestUtils.enable(adapter); + mTestUtils.disable(adapter); + } + } + + /** + * Stress test for putting the device in and taking the device out of discoverable mode. + */ + public void testDiscoverable() { + int iterations = BluetoothTestRunner.sDiscoverableIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.undiscoverable(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations); + mTestUtils.discoverable(adapter); + mTestUtils.undiscoverable(adapter); + } + + mTestUtils.disable(adapter); + } + + /** + * Stress test for starting and stopping Bluetooth scans. + */ + public void testScan() { + int iterations = BluetoothTestRunner.sScanIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.stopScan(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations); + mTestUtils.startScan(adapter); + mTestUtils.stopScan(adapter); + } + + mTestUtils.disable(adapter); + } + + /** + * Stress test for enabling and disabling the PAN NAP profile. + */ + public void testEnablePan() { + int iterations = BluetoothTestRunner.sEnablePanIterations; + if (iterations == 0) { + return; + } + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.disablePan(adapter); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of " + + iterations); + mTestUtils.enablePan(adapter); + mTestUtils.disablePan(adapter); + } + + mTestUtils.disable(adapter); + } + + /** + * Stress test for pairing and unpairing with a remote device. + *

    + * In this test, the local device initiates pairing with a remote device, and then unpairs with + * the device after the pairing has successfully completed. + */ + public void testPair() { + int iterations = BluetoothTestRunner.sPairIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.unpair(adapter, device); + } + mTestUtils.disable(adapter); + } + + /** + * Stress test for accepting a pairing request and unpairing with a remote device. + *

    + * In this test, the local device waits for a pairing request from a remote device. It accepts + * the request and then unpairs after the paring has successfully completed. + */ + public void testAcceptPair() { + int iterations = BluetoothTestRunner.sPairIterations; + if (iterations == 0) { + return; + } + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations); + mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.unpair(adapter, device); + } + mTestUtils.disable(adapter); + } + + /** + * Stress test for connecting and disconnecting with an A2DP source. + *

    + * In this test, the local device plays the role of an A2DP sink, and initiates connections and + * disconnections with an A2DP source. + */ + public void testConnectA2dp() { + int iterations = BluetoothTestRunner.sConnectA2dpIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, null); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP, + String.format("connectA2dp(device=%s)", device)); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, + String.format("disconnectA2dp(device=%s)", device)); + } + + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } + + /** + * Stress test for connecting and disconnecting the HFP with a hands free device. + *

    + * In this test, the local device plays the role of an HFP audio gateway, and initiates + * connections and disconnections with a hands free device. + */ + public void testConnectHeadset() { + int iterations = BluetoothTestRunner.sConnectHeadsetIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, + String.format("connectHeadset(device=%s)", device)); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, + String.format("disconnectHeadset(device=%s)", device)); + } + + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } + + /** + * Stress test for connecting and disconnecting with a HID device. + *

    + * In this test, the local device plays the role of a HID host, and initiates connections and + * disconnections with a HID device. + */ + public void testConnectInput() { + int iterations = BluetoothTestRunner.sConnectInputIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, null); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, + String.format("connectInput(device=%s)", device)); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, + String.format("disconnectInput(device=%s)", device)); + } + + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } + + /** + * Stress test for connecting and disconnecting with a PAN NAP. + *

    + * In this test, the local device plays the role of a PANU, and initiates connections and + * disconnections with a NAP. + */ + public void testConnectPan() { + int iterations = BluetoothTestRunner.sConnectPanIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectPan(adapter, device); + mTestUtils.disconnectPan(adapter, device); + } + + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } + + /** + * Stress test for verifying a PANU connecting and disconnecting with the device. + *

    + * In this test, the local device plays the role of a NAP which a remote PANU connects and + * disconnects from. + */ + public void testIncomingPanConnection() { + int iterations = BluetoothTestRunner.sConnectPanIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.disablePan(adapter); + mTestUtils.enablePan(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of " + + iterations); + mTestUtils.incomingPanConnection(adapter, device); + mTestUtils.incomingPanDisconnection(adapter, device); + } + + mTestUtils.unpair(adapter, device); + mTestUtils.disablePan(adapter); + mTestUtils.disable(adapter); + } + + /** + * Stress test for verifying that AudioManager can open and close SCO connections. + *

    + * In this test, a HSP connection is opened with an external headset and the SCO connection is + * repeatibly opened and closed. + */ + public void testStartStopSco() { + int iterations = BluetoothTestRunner.sStartStopScoIterations; + if (iterations == 0) { + return; + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.disable(adapter); + mTestUtils.enable(adapter); + mTestUtils.unpair(adapter, device); + mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothTestRunner.sDevicePairPin); + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.stopSco(adapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations); + mTestUtils.startSco(adapter, device); + mTestUtils.stopSco(adapter, device); + } + + mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothTestRunner.java b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java new file mode 100644 index 00000000000..64d2c1257ea --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2010 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 junit.framework.TestSuite; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import android.util.Log; + +/** + * Instrumentation test runner for Bluetooth tests. + *

    + * To run: + *

    + * {@code
    + * adb shell am instrument \
    + *     [-e enable_iterations ] \
    + *     [-e discoverable_iterations ] \
    + *     [-e scan_iterations ] \
    + *     [-e enable_pan_iterations ] \
    + *     [-e pair_iterations ] \
    + *     [-e connect_a2dp_iterations ] \
    + *     [-e connect_headset_iterations ] \
    + *     [-e connect_input_iterations ] \
    + *     [-e connect_pan_iterations ] \
    + *     [-e start_stop_sco_iterations ] \
    + *     [-e pair_address 
    ] \ + * [-e headset_address
    ] \ + * [-e a2dp_address
    ] \ + * [-e input_address
    ] \ + * [-e pan_address
    ] \ + * [-e pair_pin ] \ + * [-e pair_passkey ] \ + * -w com.android.frameworks.coretests/android.bluetooth.BluetoothTestRunner + * } + *
    + */ +public class BluetoothTestRunner extends InstrumentationTestRunner { + private static final String TAG = "BluetoothTestRunner"; + + public static int sEnableIterations = 100; + public static int sDiscoverableIterations = 1000; + public static int sScanIterations = 1000; + public static int sEnablePanIterations = 1000; + public static int sPairIterations = 100; + public static int sConnectHeadsetIterations = 100; + public static int sConnectA2dpIterations = 100; + public static int sConnectInputIterations = 100; + public static int sConnectPanIterations = 100; + public static int sStartStopScoIterations = 100; + + public static String sDeviceAddress = ""; + public static byte[] sDevicePairPin = {'1', '2', '3', '4'}; + public static int sDevicePairPasskey = 123456; + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(BluetoothStressTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return BluetoothTestRunner.class.getClassLoader(); + } + + @Override + public void onCreate(Bundle arguments) { + String val = arguments.getString("enable_iterations"); + if (val != null) { + try { + sEnableIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("discoverable_iterations"); + if (val != null) { + try { + sDiscoverableIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("scan_iterations"); + if (val != null) { + try { + sScanIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("enable_pan_iterations"); + if (val != null) { + try { + sEnablePanIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("pair_iterations"); + if (val != null) { + try { + sPairIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("connect_a2dp_iterations"); + if (val != null) { + try { + sConnectA2dpIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("connect_headset_iterations"); + if (val != null) { + try { + sConnectHeadsetIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("connect_input_iterations"); + if (val != null) { + try { + sConnectInputIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("connect_pan_iterations"); + if (val != null) { + try { + sConnectPanIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("start_stop_sco_iterations"); + if (val != null) { + try { + sStartStopScoIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("device_address"); + if (val != null) { + sDeviceAddress = val; + } + + val = arguments.getString("device_pair_pin"); + if (val != null) { + byte[] pin = BluetoothDevice.convertPinToBytes(val); + if (pin != null) { + sDevicePairPin = pin; + } + } + + val = arguments.getString("device_pair_passkey"); + if (val != null) { + try { + sDevicePairPasskey = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations)); + Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations)); + Log.i(TAG, String.format("scan_iterations=%d", sScanIterations)); + Log.i(TAG, String.format("pair_iterations=%d", sPairIterations)); + Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations)); + Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations)); + Log.i(TAG, String.format("connect_input_iterations=%d", sConnectInputIterations)); + Log.i(TAG, String.format("connect_pan_iterations=%d", sConnectPanIterations)); + Log.i(TAG, String.format("start_stop_sco_iterations=%d", sStartStopScoIterations)); + Log.i(TAG, String.format("device_address=%s", sDeviceAddress)); + Log.i(TAG, String.format("device_pair_pin=%s", new String(sDevicePairPin))); + Log.i(TAG, String.format("device_pair_passkey=%d", sDevicePairPasskey)); + + // Call onCreate last since we want to set the static variables first. + super.onCreate(arguments); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java new file mode 100644 index 00000000000..f1dd8fef5ea --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -0,0 +1,1487 @@ +/* + * Copyright (C) 2010 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.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.Environment; +import android.util.Log; + +import junit.framework.Assert; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class BluetoothTestUtils extends Assert { + + /** Timeout for enable/disable in ms. */ + private static final int ENABLE_DISABLE_TIMEOUT = 20000; + /** Timeout for discoverable/undiscoverable in ms. */ + private static final int DISCOVERABLE_UNDISCOVERABLE_TIMEOUT = 5000; + /** Timeout for starting/stopping a scan in ms. */ + private static final int START_STOP_SCAN_TIMEOUT = 5000; + /** Timeout for pair/unpair in ms. */ + private static final int PAIR_UNPAIR_TIMEOUT = 20000; + /** Timeout for connecting/disconnecting a profile in ms. */ + private static final int CONNECT_DISCONNECT_PROFILE_TIMEOUT = 20000; + /** Timeout to start or stop a SCO channel in ms. */ + private static final int START_STOP_SCO_TIMEOUT = 10000; + /** Timeout to connect a profile proxy in ms. */ + private static final int CONNECT_PROXY_TIMEOUT = 5000; + /** Time between polls in ms. */ + private static final int POLL_TIME = 100; + + private abstract class FlagReceiver extends BroadcastReceiver { + private int mExpectedFlags = 0; + private int mFiredFlags = 0; + private long mCompletedTime = -1; + + public FlagReceiver(int expectedFlags) { + mExpectedFlags = expectedFlags; + } + + public int getFiredFlags() { + synchronized (this) { + return mFiredFlags; + } + } + + public long getCompletedTime() { + synchronized (this) { + return mCompletedTime; + } + } + + protected void setFiredFlag(int flag) { + synchronized (this) { + mFiredFlags |= flag; + if ((mFiredFlags & mExpectedFlags) == mExpectedFlags) { + mCompletedTime = System.currentTimeMillis(); + } + } + } + } + + private class BluetoothReceiver extends FlagReceiver { + private static final int DISCOVERY_STARTED_FLAG = 1; + private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; + private static final int SCAN_MODE_NONE_FLAG = 1 << 2; + private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3; + private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4; + private static final int STATE_OFF_FLAG = 1 << 5; + private static final int STATE_TURNING_ON_FLAG = 1 << 6; + private static final int STATE_ON_FLAG = 1 << 7; + private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + + public BluetoothReceiver(int expectedFlags) { + super(expectedFlags); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { + setFiredFlag(DISCOVERY_STARTED_FLAG); + } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { + setFiredFlag(DISCOVERY_FINISHED_FLAG); + } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) { + int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1); + assertNotSame(-1, mode); + switch (mode) { + case BluetoothAdapter.SCAN_MODE_NONE: + setFiredFlag(SCAN_MODE_NONE_FLAG); + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE: + setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG); + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG); + break; + } + } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + assertNotSame(-1, state); + switch (state) { + case BluetoothAdapter.STATE_OFF: + setFiredFlag(STATE_OFF_FLAG); + break; + case BluetoothAdapter.STATE_TURNING_ON: + setFiredFlag(STATE_TURNING_ON_FLAG); + break; + case BluetoothAdapter.STATE_ON: + setFiredFlag(STATE_ON_FLAG); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + setFiredFlag(STATE_TURNING_OFF_FLAG); + break; + } + } + } + } + + private class PairReceiver extends FlagReceiver { + private static final int STATE_BONDED_FLAG = 1; + private static final int STATE_BONDING_FLAG = 1 << 1; + private static final int STATE_NONE_FLAG = 1 << 2; + + private BluetoothDevice mDevice; + private int mPasskey; + private byte[] mPin; + + public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) { + super(expectedFlags); + + mDevice = device; + mPasskey = passkey; + mPin = pin; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) { + return; + } + + if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) { + int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1); + assertNotSame(-1, varient); + switch (varient) { + case BluetoothDevice.PAIRING_VARIANT_PIN: + mDevice.setPin(mPin); + break; + case BluetoothDevice.PAIRING_VARIANT_PASSKEY: + mDevice.setPasskey(mPasskey); + break; + case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: + case BluetoothDevice.PAIRING_VARIANT_CONSENT: + mDevice.setPairingConfirmation(true); + break; + case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT: + mDevice.setRemoteOutOfBandData(); + break; + } + } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); + assertNotSame(-1, state); + switch (state) { + case BluetoothDevice.BOND_NONE: + setFiredFlag(STATE_NONE_FLAG); + break; + case BluetoothDevice.BOND_BONDING: + setFiredFlag(STATE_BONDING_FLAG); + break; + case BluetoothDevice.BOND_BONDED: + setFiredFlag(STATE_BONDED_FLAG); + break; + } + } + } + } + + private class ConnectProfileReceiver extends FlagReceiver { + private static final int STATE_DISCONNECTED_FLAG = 1; + private static final int STATE_CONNECTING_FLAG = 1 << 1; + private static final int STATE_CONNECTED_FLAG = 1 << 2; + private static final int STATE_DISCONNECTING_FLAG = 1 << 3; + + private BluetoothDevice mDevice; + private int mProfile; + private String mConnectionAction; + + public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) { + super(expectedFlags); + + mDevice = device; + mProfile = profile; + + switch (mProfile) { + case BluetoothProfile.A2DP: + mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED; + break; + case BluetoothProfile.HEADSET: + mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; + break; + case BluetoothProfile.INPUT_DEVICE: + mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED; + break; + case BluetoothProfile.PAN: + mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; + break; + default: + mConnectionAction = null; + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) { + if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) { + return; + } + + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + assertNotSame(-1, state); + switch (state) { + case BluetoothProfile.STATE_DISCONNECTED: + setFiredFlag(STATE_DISCONNECTED_FLAG); + break; + case BluetoothProfile.STATE_CONNECTING: + setFiredFlag(STATE_CONNECTING_FLAG); + break; + case BluetoothProfile.STATE_CONNECTED: + setFiredFlag(STATE_CONNECTED_FLAG); + break; + case BluetoothProfile.STATE_DISCONNECTING: + setFiredFlag(STATE_DISCONNECTING_FLAG); + break; + } + } + } + } + + private class ConnectPanReceiver extends ConnectProfileReceiver { + private int mRole; + + public ConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) { + super(device, BluetoothProfile.PAN, expectedFlags); + + mRole = role; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (mRole != intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, -1)) { + return; + } + + super.onReceive(context, intent); + } + } + + private class StartStopScoReceiver extends FlagReceiver { + private static final int STATE_CONNECTED_FLAG = 1; + private static final int STATE_DISCONNECTED_FLAG = 1 << 1; + + public StartStopScoReceiver(int expectedFlags) { + super(expectedFlags); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, + AudioManager.SCO_AUDIO_STATE_ERROR); + assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state); + switch(state) { + case AudioManager.SCO_AUDIO_STATE_CONNECTED: + setFiredFlag(STATE_CONNECTED_FLAG); + break; + case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: + setFiredFlag(STATE_DISCONNECTED_FLAG); + break; + } + } + } + } + + private BluetoothProfile.ServiceListener mServiceListener = + new BluetoothProfile.ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + synchronized (this) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEADSET: + mHeadset = (BluetoothHeadset) proxy; + break; + case BluetoothProfile.INPUT_DEVICE: + mInput = (BluetoothInputDevice) proxy; + break; + case BluetoothProfile.PAN: + mPan = (BluetoothPan) proxy; + break; + } + } + } + + @Override + public void onServiceDisconnected(int profile) { + synchronized (this) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = null; + break; + case BluetoothProfile.HEADSET: + mHeadset = null; + break; + case BluetoothProfile.INPUT_DEVICE: + mInput = null; + break; + case BluetoothProfile.PAN: + mPan = null; + break; + } + } + } + }; + + private List mReceivers = new ArrayList(); + + private BufferedWriter mOutputWriter; + private String mTag; + private String mOutputFile; + + private Context mContext; + private BluetoothA2dp mA2dp = null; + private BluetoothHeadset mHeadset = null; + private BluetoothInputDevice mInput = null; + private BluetoothPan mPan = null; + + /** + * Creates a utility instance for testing Bluetooth. + * + * @param context The context of the application using the utility. + * @param tag The log tag of the application using the utility. + */ + public BluetoothTestUtils(Context context, String tag) { + this(context, tag, null); + } + + /** + * Creates a utility instance for testing Bluetooth. + * + * @param context The context of the application using the utility. + * @param tag The log tag of the application using the utility. + * @param outputFile The path to an output file if the utility is to write results to a + * separate file. + */ + public BluetoothTestUtils(Context context, String tag, String outputFile) { + mContext = context; + mTag = tag; + mOutputFile = outputFile; + + if (mOutputFile == null) { + mOutputWriter = null; + } else { + try { + mOutputWriter = new BufferedWriter(new FileWriter(new File( + Environment.getExternalStorageDirectory(), mOutputFile), true)); + } catch (IOException e) { + Log.w(mTag, "Test output file could not be opened", e); + mOutputWriter = null; + } + } + } + + /** + * Closes the utility instance and unregisters any BroadcastReceivers. + */ + public void close() { + while (!mReceivers.isEmpty()) { + mContext.unregisterReceiver(mReceivers.remove(0)); + } + + if (mOutputWriter != null) { + try { + mOutputWriter.close(); + } catch (IOException e) { + Log.w(mTag, "Test output file could not be closed", e); + } + } + } + + /** + * Enables Bluetooth and checks to make sure that Bluetooth was turned on and that the correct + * actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void enable(BluetoothAdapter adapter) { + int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG + | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG); + long start = -1; + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + removeReceiver(receiver); + return; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + case BluetoothAdapter.STATE_OFF: + assertFalse(adapter.isEnabled()); + start = System.currentTimeMillis(); + assertTrue(adapter.enable()); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + start = System.currentTimeMillis(); + assertTrue(adapter.enable()); + break; + default: + removeReceiver(receiver); + fail(String.format("enable() invalid state: state=%d", state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_ON + && (receiver.getFiredFlags() & mask) == mask) { + assertTrue(adapter.isEnabled()); + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("enable() completed in %d ms", (finish - start))); + } else { + writeOutput("enable() completed"); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + state, BluetoothAdapter.STATE_ON, firedFlags, mask)); + } + + /** + * Disables Bluetooth and checks to make sure that Bluetooth was turned off and that the correct + * actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void disable(BluetoothAdapter adapter) { + int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG + | BluetoothReceiver.SCAN_MODE_NONE_FLAG); + long start = -1; + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_OFF: + assertFalse(adapter.isEnabled()); + removeReceiver(receiver); + return; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + start = System.currentTimeMillis(); + break; + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + start = System.currentTimeMillis(); + assertTrue(adapter.disable()); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + removeReceiver(receiver); + fail(String.format("disable() invalid state: state=%d", state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_OFF + && (receiver.getFiredFlags() & mask) == mask) { + assertFalse(adapter.isEnabled()); + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("disable() completed in %d ms", (finish - start))); + } else { + writeOutput("disable() completed"); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + state, BluetoothAdapter.STATE_OFF, firedFlags, mask)); + } + + /** + * Puts the local device into discoverable mode and checks to make sure that the local device + * is in discoverable mode and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void discoverable(BluetoothAdapter adapter) { + int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; + + if (!adapter.isEnabled()) { + fail("discoverable() bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + return; + } + + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode); + long start = System.currentTimeMillis(); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); + + while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE + && (receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("discoverable() completed in %d ms", + (receiver.getCompletedTime() - start))); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x " + + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, + firedFlags, mask)); + } + + /** + * Puts the local device into connectable only mode and checks to make sure that the local + * device is in in connectable mode and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void undiscoverable(BluetoothAdapter adapter) { + int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG; + + if (!adapter.isEnabled()) { + fail("undiscoverable() bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + return; + } + + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode); + long start = System.currentTimeMillis(); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); + + while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE + && (receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("undiscoverable() completed in %d ms", + (receiver.getCompletedTime() - start))); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x " + + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags, + mask)); + } + + /** + * Starts a scan for remote devices and checks to make sure that the local device is scanning + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void startScan(BluetoothAdapter adapter) { + int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG; + + if (!adapter.isEnabled()) { + fail("startScan() bluetooth not enabled"); + } + + if (adapter.isDiscovering()) { + return; + } + + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + long start = System.currentTimeMillis(); + assertTrue(adapter.startDiscovery()); + + while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) { + if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) { + writeOutput(String.format("startScan() completed in %d ms", + (receiver.getCompletedTime() - start))); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", + adapter.isDiscovering(), firedFlags, mask)); + } + + /** + * Stops a scan for remote devices and checks to make sure that the local device is not scanning + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + */ + public void stopScan(BluetoothAdapter adapter) { + int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG; + + if (!adapter.isEnabled()) { + fail("stopScan() bluetooth not enabled"); + } + + if (!adapter.isDiscovering()) { + return; + } + + BluetoothReceiver receiver = getBluetoothReceiver(mask); + + long start = System.currentTimeMillis(); + // TODO: put assertTrue() around cancelDiscovery() once it starts returning true. + adapter.cancelDiscovery(); + + while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) { + if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) { + writeOutput(String.format("stopScan() completed in %d ms", + (receiver.getCompletedTime() - start))); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", + adapter.isDiscovering(), firedFlags, mask)); + + } + + /** + * Enables PAN tethering on the local device and checks to make sure that tethering is enabled. + * + * @param adapter The BT adapter. + */ + public void enablePan(BluetoothAdapter adapter) { + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); + + long start = System.currentTimeMillis(); + mPan.setBluetoothTethering(true); + long stop = System.currentTimeMillis(); + assertTrue(mPan.isTetheringOn()); + + writeOutput(String.format("enablePan() completed in %d ms", (stop - start))); + } + + /** + * Disables PAN tethering on the local device and checks to make sure that tethering is + * disabled. + * + * @param adapter The BT adapter. + */ + public void disablePan(BluetoothAdapter adapter) { + if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); + + long start = System.currentTimeMillis(); + mPan.setBluetoothTethering(false); + long stop = System.currentTimeMillis(); + assertFalse(mPan.isTetheringOn()); + + writeOutput(String.format("disablePan() completed in %d ms", (stop - start))); + } + + /** + * Initiates a pairing with a remote device and checks to make sure that the devices are paired + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. + * @param pin The pairing pin if pairing requires a pin. Any value if not. + */ + public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) { + pairOrAcceptPair(adapter, device, passkey, pin, true); + } + + /** + * Accepts a pairing with a remote device and checks to make sure that the devices are paired + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. + * @param pin The pairing pin if pairing requires a pin. Any value if not. + */ + public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, + byte[] pin) { + pairOrAcceptPair(adapter, device, passkey, pin, false); + } + + /** + * Helper method used by {@link #pair(BluetoothAdapter, BluetoothDevice, int, byte[])} and + * {@link #acceptPair(BluetoothAdapter, BluetoothDevice, int, byte[])} to either pair or accept + * a pairing request. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. + * @param pin The pairing pin if pairing requires a pin. Any value if not. + * @param shouldPair Whether to pair or accept the pair. + */ + private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, + byte[] pin, boolean shouldPair) { + int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG; + long start = -1; + String methodName; + if (shouldPair) { + methodName = String.format("pair(device=%s)", device); + } else { + methodName = String.format("acceptPair(device=%s)", device); + } + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + PairReceiver receiver = getPairReceiver(device, passkey, pin, mask); + + int state = device.getBondState(); + switch (state) { + case BluetoothDevice.BOND_NONE: + assertFalse(adapter.getBondedDevices().contains(device)); + start = System.currentTimeMillis(); + if (shouldPair) { + assertTrue(device.createBond()); + } + break; + case BluetoothDevice.BOND_BONDING: + mask = 0; // Don't check for received intents since we might have missed them. + break; + case BluetoothDevice.BOND_BONDED: + assertTrue(adapter.getBondedDevices().contains(device)); + return; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) { + state = device.getBondState(); + if (state == BluetoothDevice.BOND_BONDED && (receiver.getFiredFlags() & mask) == mask) { + assertTrue(adapter.getBondedDevices().contains(device)); + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask)); + } + + /** + * Deletes a pairing with a remote device and checks to make sure that the devices are unpaired + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void unpair(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PairReceiver.STATE_NONE_FLAG; + long start = -1; + String methodName = String.format("unpair(device=%s)", device); + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + PairReceiver receiver = getPairReceiver(device, 0, null, mask); + + int state = device.getBondState(); + switch (state) { + case BluetoothDevice.BOND_NONE: + assertFalse(adapter.getBondedDevices().contains(device)); + removeReceiver(receiver); + return; + case BluetoothDevice.BOND_BONDING: + start = System.currentTimeMillis(); + assertTrue(device.removeBond()); + break; + case BluetoothDevice.BOND_BONDED: + assertTrue(adapter.getBondedDevices().contains(device)); + start = System.currentTimeMillis(); + assertTrue(device.removeBond()); + break; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) { + if (device.getBondState() == BluetoothDevice.BOND_NONE + && (receiver.getFiredFlags() & mask) == mask) { + assertFalse(adapter.getBondedDevices().contains(device)); + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask)); + } + + /** + * Connects a profile from the local device to a remote device and checks to make sure that the + * profile is connected and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * @param methodName The method name to printed in the logs. If null, will be + * "connectProfile(profile=<profile>, device=<device>)" + */ + public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile, + String methodName) { + if (methodName == null) { + methodName = String.format("connectProfile(profile=%d, device=%s)", profile, device); + } + int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG + | ConnectProfileReceiver.STATE_CONNECTED_FLAG); + long start = -1; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + BluetoothProfile proxy = connectProxy(adapter, profile); + assertNotNull(proxy); + + ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask); + + int state = proxy.getConnectionState(device); + switch (state) { + case BluetoothProfile.STATE_CONNECTED: + removeReceiver(receiver); + return; + case BluetoothProfile.STATE_CONNECTING: + mask = 0; // Don't check for received intents since we might have missed them. + break; + case BluetoothProfile.STATE_DISCONNECTED: + case BluetoothProfile.STATE_DISCONNECTING: + start = System.currentTimeMillis(); + assertTrue(proxy.connect(device)); + break; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { + state = proxy.getConnectionState(device); + if (state == BluetoothProfile.STATE_CONNECTED + && (receiver.getFiredFlags() & mask) == mask) { + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + methodName, state, BluetoothProfile.STATE_CONNECTED, firedFlags, mask)); + } + + /** + * Disconnects a profile between the local device and a remote device and checks to make sure + * that the profile is disconnected and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * @param methodName The method name to printed in the logs. If null, will be + * "connectProfile(profile=<profile>, device=<device>)" + */ + public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile, + String methodName) { + if (methodName == null) { + methodName = String.format("disconnectProfile(profile=%d, device=%s)", profile, device); + } + int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG + | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG); + long start = -1; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + BluetoothProfile proxy = connectProxy(adapter, profile); + assertNotNull(proxy); + + ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask); + + int state = proxy.getConnectionState(device); + switch (state) { + case BluetoothProfile.STATE_CONNECTED: + case BluetoothProfile.STATE_CONNECTING: + start = System.currentTimeMillis(); + assertTrue(proxy.disconnect(device)); + break; + case BluetoothProfile.STATE_DISCONNECTED: + removeReceiver(receiver); + return; + case BluetoothProfile.STATE_DISCONNECTING: + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { + state = proxy.getConnectionState(device); + if (state == BluetoothProfile.STATE_DISCONNECTED + && (receiver.getFiredFlags() & mask) == mask) { + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", + methodName, state, BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask)); + } + + /** + * Connects the PANU to a remote NAP and checks to make sure that the PANU is connected and that + * the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void connectPan(BluetoothAdapter adapter, BluetoothDevice device) { + connectPanOrIncomingPanConnection(adapter, device, true); + } + + /** + * Checks that a remote PANU connects to the local NAP correctly and that the correct actions + * were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void incomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device) { + connectPanOrIncomingPanConnection(adapter, device, false); + } + + /** + * Helper method used by {@link #connectPan(BluetoothAdapter, BluetoothDevice)} and + * {@link #incomingPanConnection(BluetoothAdapter, BluetoothDevice)} to either connect to a + * remote NAP or verify that a remote device connected to the local NAP. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param connect If the method should initiate the connection (is PANU) + */ + private void connectPanOrIncomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device, + boolean connect) { + long start = -1; + int mask, role; + String methodName; + + if (connect) { + methodName = String.format("connectPan(device=%s)", device); + mask = (ConnectProfileReceiver.STATE_CONNECTED_FLAG | + ConnectProfileReceiver.STATE_CONNECTING_FLAG); + role = BluetoothPan.LOCAL_PANU_ROLE; + } else { + methodName = String.format("incomingPanConnection(device=%s)", device); + mask = ConnectProfileReceiver.STATE_CONNECTED_FLAG; + role = BluetoothPan.LOCAL_NAP_ROLE; + } + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); + ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); + + int state = mPan.getConnectionState(device); + switch (state) { + case BluetoothPan.STATE_CONNECTED: + removeReceiver(receiver); + return; + case BluetoothPan.STATE_CONNECTING: + mask = 0; // Don't check for received intents since we might have missed them. + break; + case BluetoothPan.STATE_DISCONNECTED: + case BluetoothPan.STATE_DISCONNECTING: + start = System.currentTimeMillis(); + if (role == BluetoothPan.LOCAL_PANU_ROLE) { + Log.i("BT", "connect to pan"); + assertTrue(mPan.connect(device)); + } + break; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { + state = mPan.getConnectionState(device); + if (state == BluetoothPan.STATE_CONNECTED + && (receiver.getFiredFlags() & mask) == mask) { + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, state, BluetoothPan.STATE_CONNECTED, firedFlags, mask)); + } + + /** + * Disconnects the PANU from a remote NAP and checks to make sure that the PANU is disconnected + * and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void disconnectPan(BluetoothAdapter adapter, BluetoothDevice device) { + disconnectFromRemoteOrVerifyConnectNap(adapter, device, true); + } + + /** + * Checks that a remote PANU disconnects from the local NAP correctly and that the correct + * actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void incomingPanDisconnection(BluetoothAdapter adapter, BluetoothDevice device) { + disconnectFromRemoteOrVerifyConnectNap(adapter, device, false); + } + + /** + * Helper method used by {@link #disconnectPan(BluetoothAdapter, BluetoothDevice)} and + * {@link #incomingPanDisconnection(BluetoothAdapter, BluetoothDevice)} to either disconnect + * from a remote NAP or verify that a remote device disconnected from the local NAP. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param disconnect Whether the method should connect or verify. + */ + private void disconnectFromRemoteOrVerifyConnectNap(BluetoothAdapter adapter, + BluetoothDevice device, boolean disconnect) { + long start = -1; + int mask, role; + String methodName; + + if (disconnect) { + methodName = String.format("disconnectPan(device=%s)", device); + mask = (ConnectProfileReceiver.STATE_DISCONNECTED_FLAG | + ConnectProfileReceiver.STATE_DISCONNECTING_FLAG); + role = BluetoothPan.LOCAL_PANU_ROLE; + } else { + methodName = String.format("incomingPanDisconnection(device=%s)", device); + mask = ConnectProfileReceiver.STATE_DISCONNECTED_FLAG; + role = BluetoothPan.LOCAL_NAP_ROLE; + } + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); + assertNotNull(mPan); + ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); + + int state = mPan.getConnectionState(device); + switch (state) { + case BluetoothPan.STATE_CONNECTED: + case BluetoothPan.STATE_CONNECTING: + start = System.currentTimeMillis(); + if (role == BluetoothPan.LOCAL_PANU_ROLE) { + assertTrue(mPan.disconnect(device)); + } + break; + case BluetoothPan.STATE_DISCONNECTED: + removeReceiver(receiver); + return; + case BluetoothPan.STATE_DISCONNECTING: + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + removeReceiver(receiver); + fail(String.format("%s invalid state: state=%d", methodName, state)); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { + state = mPan.getConnectionState(device); + if (state == BluetoothInputDevice.STATE_DISCONNECTED + && (receiver.getFiredFlags() & mask) == mask) { + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, state, BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask)); + } + + /** + * Opens a SCO channel using {@link android.media.AudioManager#startBluetoothSco()} and checks + * to make sure that the channel is opened and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void startSco(BluetoothAdapter adapter, BluetoothDevice device) { + startStopSco(adapter, device, true); + } + + /** + * Closes a SCO channel using {@link android.media.AudioManager#stopBluetoothSco()} and checks + * to make sure that the channel is closed and that the correct actions were broadcast. + * + * @param adapter The BT adapter. + * @param device The remote device. + */ + public void stopSco(BluetoothAdapter adapter, BluetoothDevice device) { + startStopSco(adapter, device, false); + } + /** + * Helper method for {@link #startSco(BluetoothAdapter, BluetoothDevice)} and + * {@link #stopSco(BluetoothAdapter, BluetoothDevice)}. + * + * @param adapter The BT adapter. + * @param device The remote device. + * @param isStart Whether the SCO channel should be opened. + */ + private void startStopSco(BluetoothAdapter adapter, BluetoothDevice device, boolean isStart) { + long start = -1; + int mask; + String methodName; + + if (isStart) { + methodName = String.format("startSco(device=%s)", device); + mask = StartStopScoReceiver.STATE_CONNECTED_FLAG; + } else { + methodName = String.format("stopSco(device=%s)", device); + mask = StartStopScoReceiver.STATE_DISCONNECTED_FLAG; + } + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + assertNotNull(manager); + + if (!manager.isBluetoothScoAvailableOffCall()) { + fail(String.format("%s device does not support SCO", methodName)); + } + + boolean isScoOn = manager.isBluetoothScoOn(); + if (isStart == isScoOn) { + return; + } + + StartStopScoReceiver receiver = getStartStopScoReceiver(mask); + start = System.currentTimeMillis(); + if (isStart) { + manager.startBluetoothSco(); + } else { + manager.stopBluetoothSco(); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < START_STOP_SCO_TIMEOUT) { + isScoOn = manager.isBluetoothScoOn(); + if (isStart == isScoOn && (receiver.getFiredFlags() & mask) == mask) { + long finish = receiver.getCompletedTime(); + if (start != -1 && finish != -1) { + writeOutput(String.format("%s completed in %d ms", methodName, + (finish - start))); + } else { + writeOutput(String.format("%s completed", methodName)); + } + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: on=%b (expected %b), flags=0x%x (expected 0x%x)", + methodName, isScoOn, isStart, firedFlags, mask)); + } + + /** + * Writes a string to the logcat and a file if a file has been specified in the constructor. + * + * @param s The string to be written. + */ + public void writeOutput(String s) { + Log.i(mTag, s); + if (mOutputWriter == null) { + return; + } + try { + mOutputWriter.write(s + "\n"); + mOutputWriter.flush(); + } catch (IOException e) { + Log.w(mTag, "Could not write to output file", e); + } + } + + private void addReceiver(BroadcastReceiver receiver, String[] actions) { + IntentFilter filter = new IntentFilter(); + for (String action: actions) { + filter.addAction(action); + } + mContext.registerReceiver(receiver, filter); + mReceivers.add(receiver); + } + + private BluetoothReceiver getBluetoothReceiver(int expectedFlags) { + String[] actions = { + BluetoothAdapter.ACTION_DISCOVERY_FINISHED, + BluetoothAdapter.ACTION_DISCOVERY_STARTED, + BluetoothAdapter.ACTION_SCAN_MODE_CHANGED, + BluetoothAdapter.ACTION_STATE_CHANGED}; + BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + + private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin, + int expectedFlags) { + String[] actions = { + BluetoothDevice.ACTION_PAIRING_REQUEST, + BluetoothDevice.ACTION_BOND_STATE_CHANGED}; + PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + + private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile, + int expectedFlags) { + String[] actions = { + BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, + BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, + BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED}; + ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, + expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + + private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role, + int expectedFlags) { + String[] actions = {BluetoothPan.ACTION_CONNECTION_STATE_CHANGED}; + ConnectPanReceiver receiver = new ConnectPanReceiver(device, role, expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + + private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) { + String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED}; + StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + + private void removeReceiver(BroadcastReceiver receiver) { + mContext.unregisterReceiver(receiver); + mReceivers.remove(receiver); + } + + private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + if (mA2dp != null) { + return mA2dp; + } + break; + case BluetoothProfile.HEADSET: + if (mHeadset != null) { + return mHeadset; + } + break; + case BluetoothProfile.INPUT_DEVICE: + if (mInput != null) { + return mInput; + } + break; + case BluetoothProfile.PAN: + if (mPan != null) { + return mPan; + } + break; + default: + return null; + } + adapter.getProfileProxy(mContext, mServiceListener, profile); + long s = System.currentTimeMillis(); + switch (profile) { + case BluetoothProfile.A2DP: + while (mA2dp == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mA2dp; + case BluetoothProfile.HEADSET: + while (mHeadset == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mHeadset; + case BluetoothProfile.INPUT_DEVICE: + while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mInput; + case BluetoothProfile.PAN: + while (mPan == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mPan; + default: + return null; + } + } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } +} -- GitLab From ebb0628f431e330a85d4a194306de5418a939fb1 Mon Sep 17 00:00:00 2001 From: Albert Mojir Date: Mon, 21 Mar 2011 14:59:10 +0100 Subject: [PATCH 0174/1408] Bluetooth: correcting return value from cancelDiscovery BluetoothAdapter.cancelDiscovery was previously always returning false. Change-Id: Ic1fd134d4b710438d95c5b8ca009104529dd1bf5 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/tests/src/android/bluetooth/BluetoothTestUtils.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e1c904420a3..26707c96a2b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -699,7 +699,7 @@ public final class BluetoothAdapter { public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { - mService.cancelDiscovery(); + return mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index f1dd8fef5ea..5f4c226c8d4 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -678,8 +678,7 @@ public class BluetoothTestUtils extends Assert { BluetoothReceiver receiver = getBluetoothReceiver(mask); long start = System.currentTimeMillis(); - // TODO: put assertTrue() around cancelDiscovery() once it starts returning true. - adapter.cancelDiscovery(); + assertTrue(adapter.cancelDiscovery()); while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) { if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) { -- GitLab From 1236dfd0b3a6a0a6a5f58efba0ba328c5596fe3c Mon Sep 17 00:00:00 2001 From: Eric Rowe Date: Tue, 14 Jun 2011 11:49:48 -0700 Subject: [PATCH 0175/1408] Fix external storage bug for BT stress tests Change-Id: Ib6cfe80449037cfee5072145fb3a4ae0e79abf06 --- framework/tests/AndroidManifest.xml | 1 + framework/tests/src/android/bluetooth/BluetoothTestRunner.java | 2 +- framework/tests/src/android/bluetooth/BluetoothTestUtils.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index 96db035d3e4..58f158ce410 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -19,6 +19,7 @@ + diff --git a/framework/tests/src/android/bluetooth/BluetoothTestRunner.java b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java index 64d2c1257ea..56e691d8c24 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestRunner.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java @@ -47,7 +47,7 @@ import android.util.Log; * [-e pan_address
    ] \ * [-e pair_pin ] \ * [-e pair_passkey ] \ - * -w com.android.frameworks.coretests/android.bluetooth.BluetoothTestRunner + * -w com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner * } * */ diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 5f4c226c8d4..047481e5678 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -290,7 +290,7 @@ public class BluetoothTestUtils extends Assert { @Override public void onReceive(Context context, Intent intent) { - if (AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED.equals(intent.getAction())) { + if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) { int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR); assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state); -- GitLab From a7266d39019b886dfb70176fa2c20ac8437439d8 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 26 May 2011 13:56:40 -0700 Subject: [PATCH 0176/1408] Refactor Bluetooth Profile. Move connect / disconnect / set and get priority functions down the interface as they are not generic enough for all profiles. Change-Id: I2656e1bdbc8046c53bb0dfbd9172f5f10b57aa7d --- .../java/android/bluetooth/BluetoothA2dp.java | 68 ++++++++++++++- .../android/bluetooth/BluetoothHeadset.java | 68 ++++++++++++++- .../bluetooth/BluetoothInputDevice.java | 66 ++++++++++++++- .../java/android/bluetooth/BluetoothPan.java | 61 +++++++++----- .../android/bluetooth/BluetoothProfile.java | 84 ------------------- .../android/bluetooth/BluetoothTestUtils.java | 16 +++- 6 files changed, 242 insertions(+), 121 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 9246a103508..61d3707f8c6 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -130,7 +130,25 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + *

    Currently, the system supports only 1 connection to the + * A2DP profile. The API will automatically disconnect connected + * devices before connecting. + * + *

    This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -149,7 +167,29 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + *

    This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

    If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -220,7 +260,18 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -243,7 +294,16 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * 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} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8a9bef05f44..23724f245ca 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -248,7 +248,25 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + *

    Currently, the system supports only 1 connection to the + * headset/handsfree profile. The API will automatically disconnect connected + * devices before connecting. + * + *

    This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -267,7 +285,29 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + *

    This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

    If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -338,7 +378,18 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -361,7 +412,16 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * 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} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index df212a82d4e..282b70a42c8 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -119,7 +119,23 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + *

    The system supports connection to multiple input devices. + * + *

    This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -138,7 +154,29 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + *

    This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

    If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -209,7 +247,18 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -232,7 +281,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * 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} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 9ffed26f4ac..7490f9ee86b 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -140,7 +140,21 @@ public final class BluetoothPan implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + *

    This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -159,7 +173,29 @@ public final class BluetoothPan implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + *

    This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

    If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -229,27 +265,6 @@ public final class BluetoothPan implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - /** - * {@inheritDoc} - * @hide - */ - public boolean setPriority(BluetoothDevice device, int priority) { - // Priorities are not supported for PAN devices - since we don't - // auto connect. - return false; - } - - /** - * {@inheritDoc} - * @hide - */ - public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); - // Priorities are not supported for PAN devices - since we don't - // auto connect. - return BluetoothProfile.PRIORITY_ON; - } - public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); try { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 1ad66f71b93..22555f02d4c 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -103,58 +103,6 @@ public interface BluetoothProfile { * */ public static final int PRIORITY_UNDEFINED = -1; - /** - * Initiate connection to a profile of the remote bluetooth device. - * - *

    Currently, the system supports only 1 connection to the - * A2DP and Headset/Handsfree profile. The API will automatically - * disconnect connected devices before connecting. - * - *

    This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean connect(BluetoothDevice device); - - /** - * Initiate disconnection from a profile - * - *

    This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - *

    If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean disconnect(BluetoothDevice device); - /** * Get connected devices for this specific profile. * @@ -194,38 +142,6 @@ public interface BluetoothProfile { */ public int getConnectionState(BluetoothDevice device); - /** - * Set priority of the profile - * - *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - public boolean setPriority(BluetoothDevice device, int priority); - - /** - * 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} - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - public int getPriority(BluetoothDevice device); - /** * An interface for notifying BluetoothProfile IPC clients when they have * been connected or disconnected to the service. diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 5f4c226c8d4..0efa3773d16 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -936,7 +936,13 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.STATE_DISCONNECTED: case BluetoothProfile.STATE_DISCONNECTING: start = System.currentTimeMillis(); - assertTrue(proxy.connect(device)); + if (profile == BluetoothProfile.A2DP) { + assertTrue(((BluetoothA2dp)proxy).connect(device)); + } else if (profile == BluetoothProfile.HEADSET) { + assertTrue(((BluetoothHeadset)proxy).connect(device)); + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + assertTrue(((BluetoothInputDevice)proxy).connect(device)); + } break; default: removeReceiver(receiver); @@ -1005,7 +1011,13 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.STATE_CONNECTED: case BluetoothProfile.STATE_CONNECTING: start = System.currentTimeMillis(); - assertTrue(proxy.disconnect(device)); + if (profile == BluetoothProfile.A2DP) { + assertTrue(((BluetoothA2dp)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.HEADSET) { + assertTrue(((BluetoothHeadset)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + assertTrue(((BluetoothInputDevice)proxy).disconnect(device)); + } break; case BluetoothProfile.STATE_DISCONNECTED: removeReceiver(receiver); -- GitLab From 697dae032c0bfcd1faf0b0727a4d6636e86157ac Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 16 Jun 2011 21:43:29 -0700 Subject: [PATCH 0177/1408] DO NOT MERGE Incoming Bluetooth Connection requests - dialog. This sends the intents to the Settings app to show the dialogs for the incoming connection requests. Change-Id: Ibe267f7cda28ce2a46c25800df2e8ac685b0b9e6 --- .../java/android/bluetooth/BluetoothA2dp.java | 16 ++ .../android/bluetooth/BluetoothDevice.java | 27 ++ .../BluetoothDeviceProfileState.java | 246 +++++++++++++++++- .../android/bluetooth/BluetoothHeadset.java | 17 ++ .../android/bluetooth/IBluetoothA2dp.aidl | 2 + .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 6 files changed, 301 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7e5f858f050..8218c0352ac 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -269,6 +269,22 @@ public final class BluetoothA2dp { } } + /** + * Allow or disallow incoming connection + * @param device Sink + * @param value True / False + * @return Success or Failure of the binder call. + */ + public boolean allowIncomingConnect(BluetoothDevice device, boolean value) { + if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")"); + try { + return mService.allowIncomingConnect(device, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** Helper for converting a state to a string. * For debug use only - strings are not internationalized. * @hide diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index aee6ad86561..e67ace08f7c 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -276,6 +276,33 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REQUEST = + "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REPLY = + "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_CANCEL = + "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. + * @hide + */ + public static final String EXTRA_CONNECTION_ACCESS_RESULT = + "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; + + /**@hide*/ + public static final int CONNECTION_ACCESS_YES = 1; + + /**@hide*/ + public static final int CONNECTION_ACCESS_NO = 2; + /** A bond attempt succeeded * @hide */ public static final int BOND_SUCCESS = 0; diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index f6d7073cb30..5f564761dd3 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -21,9 +21,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Message; +import android.os.PowerManager; import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.util.Log; +import android.util.Pair; import com.android.internal.util.HierarchicalState; import com.android.internal.util.HierarchicalStateMachine; @@ -73,9 +75,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int AUTO_CONNECT_PROFILES = 101; public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; + private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs + private static final int CONNECTION_ACCESS_UNDEFINED = -1; + private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec + private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours + + private static final String PREFS_NAME = "ConnectionAccess"; private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -90,10 +100,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothPbap mPbapService; private boolean mHeadsetServiceConnected; private boolean mPbapServiceConnected; + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private BluetoothDevice mDevice; private int mHeadsetState; private int mA2dpState; + private long mIncomingRejectTimer; + private boolean mConnectionAccessReplyReceived = false; + private Pair mIncomingConnections; + private PowerManager.WakeLock mWakeLock; + private PowerManager mPowerManager; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -108,6 +124,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine int initiator = intent.getIntExtra( BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR, BluetoothHeadset.LOCAL_DISCONNECT); + // We trust this device now + if (newState == BluetoothHeadset.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } mHeadsetState = newState; if (newState == BluetoothHeadset.STATE_DISCONNECTED && initiator == BluetoothHeadset.REMOTE_DISCONNECT) { @@ -121,6 +141,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0); mA2dpState = newState; + // We trust this device now + if (newState == BluetoothA2dp.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } if ((oldState == BluetoothA2dp.STATE_CONNECTED || oldState == BluetoothA2dp.STATE_PLAYING) && newState == BluetoothA2dp.STATE_DISCONNECTED) { @@ -134,6 +158,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez sendMessage(TRANSITION_TO_STABLE); + } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { + mWakeLock.release(); + int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + BluetoothDevice.CONNECTION_ACCESS_NO); + Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY); + msg.arg1 = val; + sendMessage(msg); } } }; @@ -174,11 +205,20 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); mContext.registerReceiver(mBroadcastReceiver, filter); HeadsetServiceListener l = new HeadsetServiceListener(); PbapServiceListener p = new PbapServiceListener(); + + mIncomingConnections = mService.getIncomingState(address); + mIncomingRejectTimer = readTimerValue(); + mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | + PowerManager.ACQUIRE_CAUSES_WAKEUP | + PowerManager.ON_AFTER_RELEASE, TAG); + mWakeLock.setReferenceCounted(false); } private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener { @@ -438,6 +478,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // Ignore Log.e(TAG, "Error: Incoming connection with a pending incoming connection"); break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + + handleIncomingConnection(CONNECT_HFP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_HFP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_INCOMING: // Serialize the commands. deferMessage(message); @@ -608,6 +666,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_A2DP_INCOMING: // ignore break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + handleIncomingConnection(CONNECT_A2DP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + // The check protects the race condition between REQUEST_REPLY + // and the timer expiry. + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_A2DP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_OUTGOING: // Defer message and retry deferMessage(message); @@ -663,8 +740,138 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(msg); } + private void updateIncomingAllowedTimer() { + // Not doing a perfect exponential backoff because + // we want two different rates. For all practical + // purposes, this is good enough. + if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER; + + mIncomingRejectTimer *= 5; + if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) { + mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER; + } + writeTimerValue(mIncomingRejectTimer); + } + + private boolean handleIncomingConnection(int command, boolean accept) { + boolean ret = false; + Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept); + switch (command) { + case CONNECT_HFP_INCOMING: + if (!accept) { + ret = mHeadsetService.rejectIncomingConnect(mDevice); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { + writeTimerValue(0); + ret = mHeadsetService.acceptIncomingConnect(mDevice); + } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + writeTimerValue(0); + handleConnectionOfOtherProfiles(command); + ret = mHeadsetService.createIncomingConnect(mDevice); + } + break; + case CONNECT_A2DP_INCOMING: + if (!accept) { + ret = mA2dpService.allowIncomingConnect(mDevice, false); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else { + writeTimerValue(0); + ret = mA2dpService.allowIncomingConnect(mDevice, true); + handleConnectionOfOtherProfiles(command); + } + break; + default: + Log.e(TAG, "Waiting for incoming connection but state changed to:" + command); + break; + } + return ret; + } + + private void sendConnectionAccessIntent() { + mConnectionAccessReplyReceived = false; + + if (!mPowerManager.isScreenOn()) mWakeLock.acquire(); + + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private void sendConnectionAccessRemovalIntent() { + mWakeLock.release(); + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private int getTrust() { + String address = mDevice.getAddress(); + if (mIncomingConnections != null) return mIncomingConnections.first; + return CONNECTION_ACCESS_UNDEFINED; + } + + + private String getStringValue(long value) { + StringBuilder sbr = new StringBuilder(); + sbr.append(Long.toString(System.currentTimeMillis())); + sbr.append("-"); + sbr.append(Long.toString(value)); + return sbr.toString(); + } + + private void setTrust(int value) { + String second; + if (mIncomingConnections == null) { + second = getStringValue(INIT_INCOMING_REJECT_TIMER); + } else { + second = mIncomingConnections.second; + } + + mIncomingConnections = new Pair(value, second); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private void writeTimerValue(long value) { + Integer first; + if (mIncomingConnections == null) { + first = CONNECTION_ACCESS_UNDEFINED; + } else { + first = mIncomingConnections.first; + } + mIncomingConnections = new Pair(first, getStringValue(value)); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private long readTimerValue() { + if (mIncomingConnections == null) + return 0; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + return Long.parseLong(splits[1]); + } + return 0; + } + + private boolean readIncomingAllowedValue() { + if (readTimerValue() == 0) return true; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + long val1 = Long.parseLong(splits[0]); + long val2 = Long.parseLong(splits[1]); + if (val1 + val2 <= System.currentTimeMillis()) { + return true; + } + } + return false; + } + synchronized boolean processCommand(int command) { - Log.i(TAG, "Processing command:" + command); + Log.e(TAG, "Processing command:" + command); + Message msg; switch(command) { case CONNECT_HFP_OUTGOING: if (mHeadsetService != null) { @@ -674,11 +881,21 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: if (!mHeadsetServiceConnected) { deferProfileServiceMessage(command); - } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { - return mHeadsetService.acceptIncomingConnect(mDevice); - } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { - handleConnectionOfOtherProfiles(command); - return mHeadsetService.createIncomingConnect(mDevice); + } else { + // Check if device is already trusted + int access = getTrust(); + if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { + handleIncomingConnection(command, true); + } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && + !readIncomingAllowedValue()) { + handleIncomingConnection(command, false); + } else { + sendConnectionAccessIntent(); + msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); + sendMessageDelayed(msg, + CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); + } + return true; } break; case CONNECT_A2DP_OUTGOING: @@ -687,8 +904,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case CONNECT_A2DP_INCOMING: - handleConnectionOfOtherProfiles(command); - // ignore, Bluez takes care + // Check if device is already trusted + int access = getTrust(); + if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { + handleIncomingConnection(command, true); + } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && + !readIncomingAllowedValue()) { + handleIncomingConnection(command, false); + } else { + sendConnectionAccessIntent(); + msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); + sendMessageDelayed(msg, + CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); + } return true; case DISCONNECT_HFP_OUTGOING: if (!mHeadsetServiceConnected) { @@ -729,6 +957,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case UNPAIR: + writeTimerValue(INIT_INCOMING_REJECT_TIMER); + setTrust(CONNECTION_ACCESS_UNDEFINED); return mService.removeBondInternal(mDevice.getAddress()); default: Log.e(TAG, "Error: Unknown Command"); diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 197b022cad0..61b5a0b0161 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -456,6 +456,23 @@ public final class BluetoothHeadset { return false; } + /** + * Reject the incoming connection. + * @hide + */ + public boolean rejectIncomingConnect(BluetoothDevice device) { + if (DBG) log("rejectIncomingConnect"); + if (mService != null) { + try { + return mService.rejectIncomingConnect(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; + } + /** * Connect to a Bluetooth Headset. * Note: This is an internal function and shouldn't be exposed diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 40f10583b63..0c2cf1b1c62 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -36,4 +36,6 @@ interface IBluetoothA2dp { boolean connectSinkInternal(in BluetoothDevice device); boolean disconnectSinkInternal(in BluetoothDevice device); + boolean allowIncomingConnect(in BluetoothDevice device, boolean value); + } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index d96f0ca0de8..62bceeed5e9 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -37,6 +37,7 @@ interface IBluetoothHeadset { boolean createIncomingConnect(in BluetoothDevice device); boolean acceptIncomingConnect(in BluetoothDevice device); + boolean rejectIncomingConnect(in BluetoothDevice device); boolean cancelConnectThread(); boolean connectHeadsetInternal(in BluetoothDevice device); boolean disconnectHeadsetInternal(in BluetoothDevice device); -- GitLab From bf981ca2e9d1d75ef726fa6d17a0b5450cf17cdf Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 1 Apr 2011 16:33:09 -0700 Subject: [PATCH 0178/1408] Implement APIs for Bluetooth Health profile. This first patch implements all the APIs. The APIs wil be made public soon. The data specification API will be submited in another patchset. Change-Id: I2462683b7e07380e2c42474b0036b34d03b4bed1 --- .../android/bluetooth/BluetoothAdapter.java | 3 + .../android/bluetooth/BluetoothHealth.java | 444 ++++++++++++++++++ .../BluetoothHealthAppConfiguration.aidl | 19 + .../BluetoothHealthAppConfiguration.java | 180 +++++++ .../android/bluetooth/BluetoothProfile.java | 10 +- .../java/android/bluetooth/IBluetooth.aidl | 14 + .../bluetooth/IBluetoothHealthCallback.aidl | 31 ++ 7 files changed, 699 insertions(+), 2 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothHealth.java create mode 100644 framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl create mode 100644 framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java create mode 100644 framework/java/android/bluetooth/IBluetoothHealthCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index a8c31f92e05..b993bd8e959 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1115,6 +1115,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; + } else if (profile == BluetoothProfile.HEALTH) { + BluetoothHealth health = new BluetoothHealth(context, listener); + return true; } else { return false; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java new file mode 100644 index 00000000000..52efc071ce6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2011 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.annotation.SdkConstant; +import android.content.Context; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Public API for Bluetooth Health Profile. + * + *

    BluetoothHealth is a proxy object for controlling the Bluetooth + * Service via IPC. + * + *

    Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHealth proxy object. Use + * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. + * @hide + */ +public final class BluetoothHealth implements BluetoothProfile { + private static final String TAG = "BluetoothHealth"; + private static final boolean DBG = false; + + /** + * Health Profile Source Role - the health device. + */ + public static final int SOURCE_ROLE = 1 << 0; + + /** + * Health Profile Sink Role the device talking to the health device. + */ + public static final int SINK_ROLE = 1 << 1; + + /** + * Health Profile - Channel Type used - Reliable + */ + public static final int CHANNEL_TYPE_RELIABLE = 10; + + /** + * Health Profile - Channel Type used - Streaming + */ + public static final int CHANNEL_TYPE_STREAMING = 11; + + /** + * @hide + */ + public static final int CHANNEL_TYPE_ANY = 12; + + private final ArrayList mAppConfigs = + new ArrayList(); + + /** + * Register an application configuration that acts as a Health SINK. + * This is the configuration that will be used to communicate with health devices + * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so + * the callback is used to notify success or failure if the function returns true. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param name The friendly name associated with the application or configuration. + * @param dataType The dataType of the Source role of Health Profile to which + * the sink wants to connect to. + * @param callback A callback to indicate success or failure of the registration and + * all operations done on this application configuration. + * @return If true, callback will be called. + */ + public boolean registerSinkAppConfiguration(String name, int dataType, + IBluetoothHealthCallback callback) { + if (!isEnabled() || name == null) return false; + + if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); + return registerAppConfiguration(name, dataType, SINK_ROLE, + CHANNEL_TYPE_ANY, callback); + } + + /** + * Register an application configuration that acts as a Health SINK or in a Health + * SOURCE role.This is an asynchronous call and so + * the callback is used to notify success or failure if the function returns true. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param name The friendly name associated with the application or configuration. + * @param dataType The dataType of the Source role of Health Profile. + * @param channelType The channel type. Will be one of + * {@link #CHANNEL_TYPE_RELIABLE} or + * {@link #CHANNEL_TYPE_STREAMING} + * @param callback - A callback to indicate success or failure. + * @return If true, callback will be called. + * @hide + */ + public boolean registerAppConfiguration(String name, int dataType, int role, + int channelType, IBluetoothHealthCallback callback) { + boolean result = false; + if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; + + if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + BluetoothHealthAppConfiguration config = + new BluetoothHealthAppConfiguration(name, dataType, role, channelType, + callback); + + if (mService != null) { + try { + result = mService.registerAppConfiguration(config); + } 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())); + } + + if (result) mAppConfigs.add(config); + return result; + } + + /** + * Unregister an application configuration that has been registered using + * {@link #registerSinkAppConfiguration} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param config The health app configuration + * @return Success or failure. + * @hide + */ + public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { + boolean result = false; + if (mService != null && isEnabled() && isValidAppConfig(config)) { + try { + result = mService.unregisterAppConfiguration(config); + } 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())); + } + if (result) mAppConfigs.remove(config); + return result; + } + + /** + * Connect to a health device which has the {@link #SOURCE_ROLE}. + * This is an asynchrnous call. If this function returns true, the callback + * associated with the application configuration will be called. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote Bluetooth device. + * @param config The application configuration which has been registed using + * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @return If true, the callback associated with the application config will be called. + */ + public boolean connectChannelToSource(BluetoothDevice device, + BluetoothHealthAppConfiguration config) { + if (mService != null && isEnabled() && isValidDevice(device) && + isValidAppConfig(config)) { + try { + return mService.connectChannelToSource(device, config); + } 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; + } + + /** + * Connect to a health device which has the {@link #SINK_ROLE}. + * This is an asynchronous call. If this function returns true, the callback + * associated with the application configuration will be called. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote Bluetooth device. + * @param config The application configuration which has been registed using + * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @return If true, the callback associated with the application config will be called. + * @hide + */ + public boolean connectChannelToSink(BluetoothDevice device, + BluetoothHealthAppConfiguration config, int channelType) { + if (mService != null && isEnabled() && isValidDevice(device) && + isValidAppConfig(config)) { + try { + return mService.connectChannelToSink(device, config, channelType); + } 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; + } + + /** + * Disconnect a connected health channel. + * This is an asynchronous call. If this function returns true, the callback + * associated with the application configuration will be called. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote Bluetooth device. + * @param config The application configuration which has been registed using + * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param fd The file descriptor that was associated with the channel. + * @return If true, the callback associated with the application config will be called. + * @hide + */ + public boolean disconnectChannel(BluetoothDevice device, + BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { + if (mService != null && isEnabled() && isValidDevice(device) && + isValidAppConfig(config)) { + try { + return mService.disconnectChannel(device, config, fd); + } 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; + } + + /** + * Get the file descriptor of the main channel associated with the remote device + * and application configuration. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote Bluetooth health device + * @param config The application configuration + * @return null on failure, ParcelFileDescriptor on success. + */ + + public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, + BluetoothHealthAppConfiguration config) { + if (mService != null && isEnabled() && isValidDevice(device) && + isValidAppConfig(config)) { + try { + return mService.getMainChannelFd(device, config); + } 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 null; + } + + /** + * Get the current connection state of the profile. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * This is not specific to any application configuration but represents the connection + * state of the local Bluetooth adapter with the remote device. This can be used + * by applications like status bar which would just like to know the state of the + * local adapter. + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + public int getConnectionState(BluetoothDevice device) { + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getHealthDeviceConnectionState(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 STATE_DISCONNECTED; + } + + /** + * Get connected devices for this specific profile. + * + *

    Return the set of devices which are in state {@link #STATE_CONNECTED} + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * This is not specific to any application configuration but represents the connection + * state of the local Bluetooth adapter for this profile. This can be used + * by applications like status bar which would just like to know the state of the + * local adapter. + * @return List of devices. The list will be empty on error. + */ + public List getConnectedDevices() { + if (mService != null && isEnabled()) { + try { + return mService.getConnectedHealthDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * Get a list of devices that match any of the given connection + * states. + * + *

    If none of the devices match any of the given states, + * an empty list will be returned. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * This is not specific to any application configuration but represents the connection + * state of the local Bluetooth adapter for this profile. This can be used + * by applications like status bar which would just like to know the state of the + * local adapter. + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (mService != null && isEnabled()) { + try { + return mService.getHealthDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** Health Channel Connection State - Disconnected */ + public static final int STATE_CHANNEL_DISCONNECTED = 0; + /** Health Channel Connection State - Connecting */ + public static final int STATE_CHANNEL_CONNECTING = 1; + /** Health Channel Connection State - Connected */ + public static final int STATE_CHANNEL_CONNECTED = 2; + /** Health Channel Connection State - Disconnecting */ + public static final int STATE_CHANNEL_DISCONNECTING = 3; + + /** Health App Configuration registration success */ + public static final int APPLICATION_REGISTRATION_SUCCESS = 0; + /** Health App Configuration registration failure */ + public static final int APPLICATION_REGISTRATION_FAILURE = 1; + /** Health App Configuration un-registration success */ + public static final int APPLICATION_UNREGISTRATION_SUCCESS = 2; + /** Health App Configuration un-registration failure */ + public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetooth mService; + BluetoothAdapter mAdapter; + + /** + * Create a BluetoothHealth proxy object. + */ + /*package*/ BluetoothHealth(Context mContext, ServiceListener l) { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + if (b != null) { + mService = IBluetooth.Stub.asInterface(b); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, this); + } + } else { + Log.w(TAG, "Bluetooth Service not available!"); + + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later when it is actually used. + mService = null; + } + } + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; + log("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; + } + + private boolean isValidAppConfig(BluetoothHealthAppConfiguration config) { + if (!mAppConfigs.isEmpty() && mAppConfigs.contains(config)) return true; + log("Not a valid config: " + config); + return false; + } + + private boolean checkAppParam(String name, int role, int channelType, + IBluetoothHealthCallback callback) { + if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || + (channelType != CHANNEL_TYPE_RELIABLE && + channelType != CHANNEL_TYPE_STREAMING && + channelType != CHANNEL_TYPE_ANY) || callback == null) { + return false; + } + if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false; + return true; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl new file mode 100644 index 00000000000..bc9e54f5787 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2011, 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; + +parcelable BluetoothHealthAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java new file mode 100644 index 00000000000..b87aea59138 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2011 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.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The Bluetooth Health Application Configuration that is used in conjunction with + * the {@link BluetoothHealth} class. This class represents an application configuration + * that the Bluetooth Health third party application will register to communicate with the + * remote Bluetooth health device. + * + * @hide + */ +public final class BluetoothHealthAppConfiguration implements Parcelable { + private final String mName; + private final int mDataType; + private final int mRole; + private final int mChannelType; + private final IBluetoothHealthCallback mCallback; + + /** + * Constructor to register the SINK role + * + * @param name Friendly name associated with the application configuration + * @param dataType Data Type of the remote Bluetooth Health device + * @param callback Callback associated with the application configuration. + */ + BluetoothHealthAppConfiguration(String name, int dataType, IBluetoothHealthCallback callback) { + mName = name; + mDataType = dataType; + mRole = BluetoothHealth.SINK_ROLE; + mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; + mCallback = callback; + } + + /** + * Constructor to register the application configuration. + * + * @param name Friendly name associated with the application configuration + * @param dataType Data Type of the remote Bluetooth Health device + * @param role {@link BluetoothHealth.SOURCE_ROLE} or + * {@link BluetoothHealth.SINK_ROLE} + * @param callback Callback associated with the application configuration. + */ + BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType, + IBluetoothHealthCallback callback) { + mName = name; + mDataType = dataType; + mRole = role; + mChannelType = channelType; + mCallback = callback; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHealthAppConfiguration) { + BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o; + // config.getName() can never be NULL + return mName.equals(config.getName()) && + mDataType == config.getDataType() && + mRole == config.getRole() && + mChannelType == config.getChannelType() && + mCallback.equals(config.getCallback()); + } + return false; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mName != null ? mName.hashCode() : 0); + result = 31 * result + mDataType; + result = 31 * result + mRole; + result = 31 * result + mChannelType; + result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "BluetoothHealthAppConfiguration [mName = " + mName + + ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + + mChannelType + ",callback=" + mCallback +"]"; + } + + public int describeContents() { + return 0; + } + + /** + * Return the data type associated with this application configuration. + * + * @return dataType + */ + public int getDataType() { + return mDataType; + } + + /** + * Return the name of the application configuration. + * + * @return String name + */ + public String getName() { + return mName; + } + + /** + * Return the role associated with this application configuration. + * + * @return One of {@link BluetoothHealth#SOURCE_ROLE} or + * {@link BluetoothHealth#SINK_ROLE} + */ + public int getRole() { + return mRole; + } + + /** + * Return the channel type associated with this application configuration. + * + * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or + * {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or + * {@link BluetoothHealth#CHANNEL_TYPE_ANY}. + */ + public int getChannelType() { + return mChannelType; + } + + /** + * Return the callback associated with this application configuration. + * + * @return IBluetoothHealthCallback + */ + public IBluetoothHealthCallback getCallback() { + return mCallback; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { + String name = in.readString(); + int type = in.readInt(); + int role = in.readInt(); + int channelType = in.readInt(); + IBluetoothHealthCallback callback = + IBluetoothHealthCallback.Stub.asInterface(in.readStrongBinder()); + return new BluetoothHealthAppConfiguration(name, type, role, channelType, + callback); + } + public BluetoothHealthAppConfiguration[] newArray(int size) { + return new BluetoothHealthAppConfiguration[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeString(mName); + out.writeInt(mDataType); + out.writeInt(mRole); + out.writeInt(mChannelType); + out.writeStrongInterface(mCallback); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 22555f02d4c..6cd81fde722 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -64,17 +64,23 @@ public interface BluetoothProfile { */ public static final int A2DP = 2; + /** + * Health Profile + * @hide + */ + public static final int HEALTH = 3; + /** * Input Device Profile * @hide */ - public static final int INPUT_DEVICE = 3; + public static final int INPUT_DEVICE = 4; /** * PAN Profile * @hide */ - public static final int PAN = 4; + public static final int PAN = 5; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index d25f5d0cb33..28b09b6db21 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -18,7 +18,9 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHealthAppConfiguration; import android.os.ParcelUuid; +import android.os.ParcelFileDescriptor; /** * System private API for talking with the Bluetooth service. @@ -98,5 +100,17 @@ interface IBluetooth boolean connectPanDevice(in BluetoothDevice device); boolean disconnectPanDevice(in BluetoothDevice device); + // HDP profile APIs + boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); + boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, + int channelType); + boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, in ParcelFileDescriptor fd); + ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); + List getConnectedHealthDevices(); + List getHealthDevicesMatchingConnectionStates(in int[] states); + int getHealthDeviceConnectionState(in BluetoothDevice device); + void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState); } diff --git a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl new file mode 100644 index 00000000000..9fe5335c5ae --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011, 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.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHealthAppConfiguration; +import android.os.ParcelFileDescriptor; + +/** + *@hide + */ +interface IBluetoothHealthCallback +{ + void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); + void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, + in BluetoothDevice device, int prevState, int newState, in ParcelFileDescriptor fd); +} -- GitLab From 75cf0b94f31b68d5c414b7c0b037d3910ba3b92e Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Sat, 25 Jun 2011 21:47:07 -0700 Subject: [PATCH 0179/1408] Incoming Bluetooth Connection requests - dialog. This sends the intents to the Settings app to show the dialogs for the incoming connection requests. Includes down merged contributions from Jaikumar Ganesh. Change-Id: Ic8b857aad3554315aae39a0e871eb94d0ac98a91 --- .../java/android/bluetooth/BluetoothA2dp.java | 19 +- .../android/bluetooth/BluetoothDevice.java | 64 ++++ .../BluetoothDeviceProfileState.java | 275 +++++++++++++++++- .../android/bluetooth/BluetoothHeadset.java | 17 ++ .../bluetooth/BluetoothInputDevice.java | 22 ++ .../java/android/bluetooth/IBluetooth.aidl | 1 + .../android/bluetooth/IBluetoothA2dp.aidl | 2 + .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 8 files changed, 389 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 61d3707f8c6..96f3290160f 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -423,7 +423,24 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - /** + /** + * Allow or disallow incoming connection + * @param device Sink + * @param value True / False + * @return Success or Failure of the binder call. + * @hide + */ + public boolean allowIncomingConnect(BluetoothDevice device, boolean value) { + if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")"); + try { + return mService.allowIncomingConnect(device, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 254e2f813a0..d9525a347fa 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -276,6 +276,70 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REQUEST = + "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REPLY = + "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_CANCEL = + "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; + + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. + * @hide + */ + public static final String EXTRA_ACCESS_REQUEST_TYPE = + "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE"; + + /**@hide*/ + public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1; + + /**@hide*/ + public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2; + + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, + * Contains package name to return reply intent to. + * @hide + */ + public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME"; + + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, + * Contains class name to return reply intent to. + * @hide + */ + public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME"; + + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. + * @hide + */ + public static final String EXTRA_CONNECTION_ACCESS_RESULT = + "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; + + /**@hide*/ + public static final int CONNECTION_ACCESS_YES = 1; + + /**@hide*/ + public static final int CONNECTION_ACCESS_NO = 2; + + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents, + * Contains boolean to indicate if the allowed response is once-for-all so that + * next request will be granted without asking user again. + * @hide + */ + public static final String EXTRA_ALWAYS_ALLOWED = + "android.bluetooth.device.extra.ALWAYS_ALLOWED"; + /** * A bond attempt succeeded * @hide diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 56f236d164b..ab3a4267a7f 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -22,9 +22,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Message; import android.bluetooth.BluetoothAdapter; +import android.os.PowerManager; import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.util.Log; +import android.util.Pair; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -81,8 +83,18 @@ public final class BluetoothDeviceProfileState extends StateMachine { public static final int AUTO_CONNECT_PROFILES = 101; public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; + private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs + private static final int CONNECTION_ACCESS_UNDEFINED = -1; + private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec + private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours + + private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; + private static final String ACCESS_AUTHORITY_CLASS = + "com.android.settings.bluetooth.BluetoothPermissionRequest"; private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -98,10 +110,16 @@ public final class BluetoothDeviceProfileState extends StateMachine { private BluetoothHeadset mHeadsetService; private BluetoothPbap mPbapService; private boolean mPbapServiceConnected; + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private BluetoothDevice mDevice; private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED; private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED; + private long mIncomingRejectTimer; + private boolean mConnectionAccessReplyReceived = false; + private Pair mIncomingConnections; + private PowerManager.WakeLock mWakeLock; + private PowerManager mPowerManager; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -113,6 +131,10 @@ public final class BluetoothDeviceProfileState extends StateMachine { if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); + // We trust this device now + if (newState == BluetoothHeadset.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } mA2dpState = newState; if (oldState == BluetoothA2dp.STATE_CONNECTED && newState == BluetoothA2dp.STATE_DISCONNECTED) { @@ -125,7 +147,10 @@ public final class BluetoothDeviceProfileState extends StateMachine { } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - + // We trust this device now + if (newState == BluetoothHeadset.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } mHeadsetState = newState; if (oldState == BluetoothHeadset.STATE_CONNECTED && newState == BluetoothHeadset.STATE_DISCONNECTED) { @@ -139,7 +164,10 @@ public final class BluetoothDeviceProfileState extends StateMachine { int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - + // We trust this device now + if (newState == BluetoothHeadset.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } if (oldState == BluetoothProfile.STATE_CONNECTED && newState == BluetoothProfile.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HID_INCOMING); @@ -152,8 +180,15 @@ public final class BluetoothDeviceProfileState extends StateMachine { // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez sendMessage(TRANSITION_TO_STABLE); + } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { + mWakeLock.release(); + int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + BluetoothDevice.CONNECTION_ACCESS_NO); + Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY); + msg.arg1 = val; + sendMessage(msg); } - } + } }; private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) { @@ -195,6 +230,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -203,6 +239,14 @@ public final class BluetoothDeviceProfileState extends StateMachine { BluetoothProfile.HEADSET); // TODO(): Convert PBAP to the new Profile APIs. PbapServiceListener p = new PbapServiceListener(); + + mIncomingConnections = mService.getIncomingState(address); + mIncomingRejectTimer = readTimerValue(); + mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | + PowerManager.ACQUIRE_CAUSES_WAKEUP | + PowerManager.ON_AFTER_RELEASE, TAG); + mWakeLock.setReferenceCounted(false); } private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = @@ -497,6 +541,24 @@ public final class BluetoothDeviceProfileState extends StateMachine { // Ignore Log.e(TAG, "Error: Incoming connection with a pending incoming connection"); break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + + handleIncomingConnection(CONNECT_HFP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_HFP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_INCOMING: // Serialize the commands. deferMessage(message); @@ -690,6 +752,25 @@ public final class BluetoothDeviceProfileState extends StateMachine { case CONNECT_A2DP_INCOMING: // ignore break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + handleIncomingConnection(CONNECT_A2DP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + // The check protects the race condition between REQUEST_REPLY + // and the timer expiry. + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_A2DP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_OUTGOING: // Defer message and retry deferMessage(message); @@ -847,6 +928,20 @@ public final class BluetoothDeviceProfileState extends StateMachine { case DISCONNECT_HID_OUTGOING: deferMessage(message); break; + case CONNECTION_ACCESS_REQUEST_REPLY: + mConnectionAccessReplyReceived = true; + int val = message.arg1; + setTrust(val); + handleIncomingConnection(CONNECT_HID_INCOMING, + val == BluetoothDevice.CONNECTION_ACCESS_YES); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_HID_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case DISCONNECT_HFP_INCOMING: // Shouldn't happen but if does, we can handle it. // Depends if the headset can handle it. @@ -891,8 +986,150 @@ public final class BluetoothDeviceProfileState extends StateMachine { deferMessage(msg); } + private void updateIncomingAllowedTimer() { + // Not doing a perfect exponential backoff because + // we want two different rates. For all practical + // purposes, this is good enough. + if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER; + + mIncomingRejectTimer *= 5; + if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) { + mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER; + } + writeTimerValue(mIncomingRejectTimer); + } + + private boolean handleIncomingConnection(int command, boolean accept) { + boolean ret = false; + Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept); + switch (command) { + case CONNECT_HFP_INCOMING: + if (!accept) { + ret = mHeadsetService.rejectIncomingConnect(mDevice); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { + writeTimerValue(0); + ret = mHeadsetService.acceptIncomingConnect(mDevice); + } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + writeTimerValue(0); + handleConnectionOfOtherProfiles(command); + ret = mHeadsetService.createIncomingConnect(mDevice); + } + break; + case CONNECT_A2DP_INCOMING: + if (!accept) { + ret = mA2dpService.allowIncomingConnect(mDevice, false); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else { + writeTimerValue(0); + ret = mA2dpService.allowIncomingConnect(mDevice, true); + handleConnectionOfOtherProfiles(command); + } + break; + case CONNECT_HID_INCOMING: + if (!accept) { + ret = mService.allowIncomingHidConnect(mDevice, false); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else { + writeTimerValue(0); + ret = mService.allowIncomingHidConnect(mDevice, true); + } + break; + default: + Log.e(TAG, "Waiting for incoming connection but state changed to:" + command); + break; + } + return ret; + } + + private void sendConnectionAccessIntent() { + mConnectionAccessReplyReceived = false; + + if (!mPowerManager.isScreenOn()) mWakeLock.acquire(); + + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); + intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); + intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, + BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private void sendConnectionAccessRemovalIntent() { + mWakeLock.release(); + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private int getTrust() { + String address = mDevice.getAddress(); + if (mIncomingConnections != null) return mIncomingConnections.first; + return CONNECTION_ACCESS_UNDEFINED; + } + + + private String getStringValue(long value) { + StringBuilder sbr = new StringBuilder(); + sbr.append(Long.toString(System.currentTimeMillis())); + sbr.append("-"); + sbr.append(Long.toString(value)); + return sbr.toString(); + } + + private void setTrust(int value) { + String second; + if (mIncomingConnections == null) { + second = getStringValue(INIT_INCOMING_REJECT_TIMER); + } else { + second = mIncomingConnections.second; + } + + mIncomingConnections = new Pair(value, second); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private void writeTimerValue(long value) { + Integer first; + if (mIncomingConnections == null) { + first = CONNECTION_ACCESS_UNDEFINED; + } else { + first = mIncomingConnections.first; + } + mIncomingConnections = new Pair(first, getStringValue(value)); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private long readTimerValue() { + if (mIncomingConnections == null) + return 0; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + return Long.parseLong(splits[1]); + } + return 0; + } + + private boolean readIncomingAllowedValue() { + if (readTimerValue() == 0) return true; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + long val1 = Long.parseLong(splits[0]); + long val2 = Long.parseLong(splits[1]); + if (val1 + val2 <= System.currentTimeMillis()) { + return true; + } + } + return false; + } + synchronized boolean processCommand(int command) { - Log.i(TAG, "Processing command:" + command); + log("Processing command:" + command); switch(command) { case CONNECT_HFP_OUTGOING: if (mHeadsetService == null) { @@ -904,11 +1141,9 @@ public final class BluetoothDeviceProfileState extends StateMachine { case CONNECT_HFP_INCOMING: if (mHeadsetService == null) { deferProfileServiceMessage(command); - } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { - return mHeadsetService.acceptIncomingConnect(mDevice); - } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { - handleConnectionOfOtherProfiles(command); - return mHeadsetService.createIncomingConnect(mDevice); + } else { + processIncomingConnectCommand(command); + return true; } break; case CONNECT_A2DP_OUTGOING: @@ -917,12 +1152,12 @@ public final class BluetoothDeviceProfileState extends StateMachine { } break; case CONNECT_A2DP_INCOMING: - handleConnectionOfOtherProfiles(command); - // ignore, Bluez takes care + processIncomingConnectCommand(command); return true; case CONNECT_HID_OUTGOING: return mService.connectInputDeviceInternal(mDevice); case CONNECT_HID_INCOMING: + processIncomingConnectCommand(command); return true; case DISCONNECT_HFP_OUTGOING: if (mHeadsetService == null) { @@ -972,6 +1207,8 @@ public final class BluetoothDeviceProfileState extends StateMachine { } break; case UNPAIR: + writeTimerValue(INIT_INCOMING_REJECT_TIMER); + setTrust(CONNECTION_ACCESS_UNDEFINED); return mService.removeBondInternal(mDevice.getAddress()); default: Log.e(TAG, "Error: Unknown Command"); @@ -979,6 +1216,22 @@ public final class BluetoothDeviceProfileState extends StateMachine { return false; } + private void processIncomingConnectCommand(int command) { + // Check if device is already trusted + int access = getTrust(); + if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { + handleIncomingConnection(command, true); + } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && + !readIncomingAllowedValue()) { + handleIncomingConnection(command, false); + } else { + sendConnectionAccessIntent(); + Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); + sendMessageDelayed(msg, + CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); + } + } + private void handleConnectionOfOtherProfiles(int command) { // The white paper recommendations mentions that when there is a // link loss, it is the responsibility of the remote device to connect. diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 23724f245ca..32843618dcd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -617,6 +617,23 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Reject the incoming connection. + * @hide + */ + public boolean rejectIncomingConnect(BluetoothDevice device) { + if (DBG) log("rejectIncomingConnect"); + if (mService != null) { + try { + return mService.rejectIncomingConnect(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; + } + /** * Connect to a Bluetooth Headset. * Note: This is an internal function and shouldn't be exposed diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 282b70a42c8..f6757d9bd36 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -308,6 +308,28 @@ public final class BluetoothInputDevice implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } + /** + * Allow or disallow incoming connection + * @param device Input device + * @param allow true / false + * @return Success or Failure of the operation + * @hide + */ + public boolean allowIncomingConnect(BluetoothDevice device, boolean allow) { + if (DBG) log("allowIncomingConnect(" + device + ", " + allow + ")"); + + if (mService == null || !isEnabled() || !isValidDevice(device)) { + return false; + } + try { + mService.allowIncomingHidConnect(device, allow); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + return true; + } + private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 28b09b6db21..6ca6c2e1037 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -91,6 +91,7 @@ interface IBluetooth int getInputDeviceConnectionState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); + boolean allowIncomingHidConnect(in BluetoothDevice device, boolean value); boolean isTetheringOn(); void setBluetoothTethering(boolean value); diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index b4fc3666003..444dd1e95bc 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -39,4 +39,6 @@ interface IBluetoothA2dp { boolean resumeSink(in BluetoothDevice device); boolean connectSinkInternal(in BluetoothDevice device); boolean disconnectSinkInternal(in BluetoothDevice device); + boolean allowIncomingConnect(in BluetoothDevice device, boolean value); + } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 273cda73bea..ec005275bbc 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -42,6 +42,7 @@ interface IBluetoothHeadset { // Internal functions, not be made public boolean createIncomingConnect(in BluetoothDevice device); boolean acceptIncomingConnect(in BluetoothDevice device); + boolean rejectIncomingConnect(in BluetoothDevice device); boolean cancelConnectThread(); boolean connectHeadsetInternal(in BluetoothDevice device); boolean disconnectHeadsetInternal(in BluetoothDevice device); -- GitLab From 48d0cca9df0a6b13a5d4d3f8664ca8be21e60e30 Mon Sep 17 00:00:00 2001 From: Ben Dodson Date: Fri, 8 Jul 2011 14:36:42 -0700 Subject: [PATCH 0180/1408] Get server's listening channel Change-Id: Ia1cb3486a4ba502185efdcf6d7bcf0f37bb55261 --- .../java/android/bluetooth/BluetoothServerSocket.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 83e59e266d8..acce182189d 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -62,6 +62,7 @@ public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; + private final int mChannel; /** * Construct a socket for incoming connections. @@ -74,6 +75,7 @@ public final class BluetoothServerSocket implements Closeable { */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { + mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); } @@ -125,4 +127,12 @@ public final class BluetoothServerSocket implements Closeable { mHandler = handler; mMessage = message; } + + /** + * Returns the channel on which this socket is bound. + * @hide + */ + public int getChannel() { + return mChannel; + } } -- GitLab From d3b7d1d78d8a2a71d2e825bf74b04cb9e9baa52f Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 6 Jul 2011 17:37:02 -0700 Subject: [PATCH 0181/1408] Bluetooth Health APIs 1. Remove the check of configs in BluetoothHealth. This check is useless since BluetoothHealth is a proxy. 2. Add a wrapper and a callback class. We shouldn't expose Binder interfaces as public APIs. Change-Id: If62620b4251cf93f3f97d2fe63099e40fae7da4d --- .../android/bluetooth/BluetoothHealth.java | 75 +++++++++++-------- .../BluetoothHealthAppConfiguration.java | 45 ++++------- .../bluetooth/BluetoothHealthCallback.java | 42 +++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 +- 4 files changed, 104 insertions(+), 62 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothHealthCallback.java diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 52efc071ce6..0a01dcf80e1 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.annotation.SdkConstant; import android.content.Context; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -67,9 +66,6 @@ public final class BluetoothHealth implements BluetoothProfile { */ public static final int CHANNEL_TYPE_ANY = 12; - private final ArrayList mAppConfigs = - new ArrayList(); - /** * Register an application configuration that acts as a Health SINK. * This is the configuration that will be used to communicate with health devices @@ -86,7 +82,7 @@ public final class BluetoothHealth implements BluetoothProfile { * @return If true, callback will be called. */ public boolean registerSinkAppConfiguration(String name, int dataType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (!isEnabled() || name == null) return false; if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); @@ -111,18 +107,18 @@ public final class BluetoothHealth implements BluetoothProfile { * @hide */ public boolean registerAppConfiguration(String name, int dataType, int role, - int channelType, IBluetoothHealthCallback callback) { + int channelType, BluetoothHealthCallback callback) { boolean result = false; if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); BluetoothHealthAppConfiguration config = - new BluetoothHealthAppConfiguration(name, dataType, role, channelType, - callback); + new BluetoothHealthAppConfiguration(name, dataType, role, channelType); if (mService != null) { try { - result = mService.registerAppConfiguration(config); + result = mService.registerAppConfiguration(config, wrapper); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -130,8 +126,6 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - - if (result) mAppConfigs.add(config); return result; } @@ -147,7 +141,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; - if (mService != null && isEnabled() && isValidAppConfig(config)) { + if (mService != null && isEnabled() && config != null) { try { result = mService.unregisterAppConfiguration(config); } catch (RemoteException e) { @@ -157,26 +151,26 @@ public final class BluetoothHealth implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - if (result) mAppConfigs.remove(config); + return result; } /** * Connect to a health device which has the {@link #SOURCE_ROLE}. - * This is an asynchrnous call. If this function returns true, the callback + * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSource(device, config); } catch (RemoteException e) { @@ -197,15 +191,15 @@ public final class BluetoothHealth implements BluetoothProfile { *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. * @hide */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { @@ -226,8 +220,8 @@ public final class BluetoothHealth implements BluetoothProfile { *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registed using - * {@link #registerSinkAppConfiguration(String, int, IBluetoothHealthCallback) } + * @param config The application configuration which has been registered using + * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param fd The file descriptor that was associated with the channel. * @return If true, the callback associated with the application config will be called. * @hide @@ -235,7 +229,7 @@ public final class BluetoothHealth implements BluetoothProfile { public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.disconnectChannel(device, config, fd); } catch (RemoteException e) { @@ -262,7 +256,7 @@ public final class BluetoothHealth implements BluetoothProfile { public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && - isValidAppConfig(config)) { + config != null) { try { return mService.getMainChannelFd(device, config); } catch (RemoteException e) { @@ -290,6 +284,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ + @Override public int getConnectionState(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -317,6 +312,7 @@ public final class BluetoothHealth implements BluetoothProfile { * local adapter. * @return List of devices. The list will be empty on error. */ + @Override public List getConnectedDevices() { if (mService != null && isEnabled()) { try { @@ -348,6 +344,7 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (mService != null && isEnabled()) { try { @@ -361,6 +358,27 @@ public final class BluetoothHealth implements BluetoothProfile { return new ArrayList(); } + private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub { + private BluetoothHealthCallback mCallback; + + public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) { + mCallback = callback; + } + + @Override + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + mCallback.onHealthAppConfigurationStatusChange(config, status); + } + + @Override + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); + } + } + /** Health Channel Connection State - Disconnected */ public static final int STATE_CHANNEL_DISCONNECTED = 0; /** Health Channel Connection State - Connecting */ @@ -379,7 +397,6 @@ public final class BluetoothHealth implements BluetoothProfile { /** Health App Configuration un-registration failure */ public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; - private Context mContext; private ServiceListener mServiceListener; private IBluetooth mService; BluetoothAdapter mAdapter; @@ -420,14 +437,8 @@ public final class BluetoothHealth implements BluetoothProfile { return false; } - private boolean isValidAppConfig(BluetoothHealthAppConfiguration config) { - if (!mAppConfigs.isEmpty() && mAppConfigs.contains(config)) return true; - log("Not a valid config: " + config); - return false; - } - private boolean checkAppParam(String name, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthCallback callback) { if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING && diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index b87aea59138..7020249c4cc 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -17,7 +17,6 @@ package android.bluetooth; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -34,21 +33,18 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { private final int mDataType; private final int mRole; private final int mChannelType; - private final IBluetoothHealthCallback mCallback; /** * Constructor to register the SINK role * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param callback Callback associated with the application configuration. */ - BluetoothHealthAppConfiguration(String name, int dataType, IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType) { mName = name; mDataType = dataType; mRole = BluetoothHealth.SINK_ROLE; mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; - mCallback = callback; } /** @@ -56,17 +52,15 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth.SOURCE_ROLE} or - * {@link BluetoothHealth.SINK_ROLE} - * @param callback Callback associated with the application configuration. + * @param role {@link BluetoothHealth#SOURCE_ROLE} or + * {@link BluetoothHealth#SINK_ROLE} */ - BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType, - IBluetoothHealthCallback callback) { + BluetoothHealthAppConfiguration(String name, int dataType, int role, int + channelType) { mName = name; mDataType = dataType; mRole = role; mChannelType = channelType; - mCallback = callback; } @Override @@ -77,8 +71,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mName.equals(config.getName()) && mDataType == config.getDataType() && mRole == config.getRole() && - mChannelType == config.getChannelType() && - mCallback.equals(config.getCallback()); + mChannelType == config.getChannelType(); } return false; } @@ -90,7 +83,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { result = 31 * result + mDataType; result = 31 * result + mRole; result = 31 * result + mChannelType; - result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0); return result; } @@ -98,9 +90,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { public String toString() { return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + - mChannelType + ",callback=" + mCallback +"]"; + mChannelType + "]"; } + @Override public int describeContents() { return 0; } @@ -144,37 +137,31 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { return mChannelType; } - /** - * Return the callback associated with this application configuration. - * - * @return IBluetoothHealthCallback - */ - public IBluetoothHealthCallback getCallback() { - return mCallback; - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { String name = in.readString(); int type = in.readInt(); int role = in.readInt(); int channelType = in.readInt(); - IBluetoothHealthCallback callback = - IBluetoothHealthCallback.Stub.asInterface(in.readStrongBinder()); - return new BluetoothHealthAppConfiguration(name, type, role, channelType, - callback); + return new BluetoothHealthAppConfiguration(name, type, role, + channelType); } + + @Override public BluetoothHealthAppConfiguration[] newArray(int size) { return new BluetoothHealthAppConfiguration[size]; } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); out.writeInt(mRole); out.writeInt(mChannelType); - out.writeStrongInterface(mCallback); } + + } diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java new file mode 100644 index 00000000000..0d11bb54468 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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.os.ParcelFileDescriptor; +import android.util.Log; + +/** + * This class is used for all the {@link BluetoothHealth} callbacks. + * @hide + */ +public abstract class BluetoothHealthCallback { + + private static final String TAG = "BluetoothHealthCallback"; + + public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, + int status) { + Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status); + } + + public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd) { + Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device + + "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); + } +} diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 28b09b6db21..496bc85ae91 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealthAppConfiguration; import android.os.ParcelUuid; @@ -101,7 +102,8 @@ interface IBluetooth boolean disconnectPanDevice(in BluetoothDevice device); // HDP profile APIs - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, + in IBluetoothHealthCallback callback); boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, -- GitLab From 9f213a1ba81b266160a1d3cf36c4bec9de2ef075 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 19 Jul 2011 15:16:18 -0700 Subject: [PATCH 0182/1408] Incoming connection dialog tweaks. Don't show incoming connection dialog when the device shows the pairing dialog - this means that the device has already been trusted by the user. Change-Id: I98a9f56528f6b62d0f824bbc7612aaa0077ba1e6 --- .../BluetoothDeviceProfileState.java | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index ab3a4267a7f..095cd110eb7 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -120,6 +120,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { private Pair mIncomingConnections; private PowerManager.WakeLock mWakeLock; private PowerManager mPowerManager; + private boolean mPairingRequestRcvd = false; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -187,27 +188,38 @@ public final class BluetoothDeviceProfileState extends StateMachine { Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY); msg.arg1 = val; sendMessage(msg); + } else if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) { + mPairingRequestRcvd = true; + } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { + int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.ERROR); + if (state == BluetoothDevice.BOND_BONDED && mPairingRequestRcvd) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + mPairingRequestRcvd = false; + } else if (state == BluetoothDevice.BOND_NONE) { + mPairingRequestRcvd = false; + } } } }; private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) { - // This works only because these broadcast intents are "sticky" - Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); - if (i != null) { - int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); - if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (device != null && autoConnectDevice.equals(device)) { - return true; - } - } - } - return false; - } + // This works only because these broadcast intents are "sticky" + Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); + if (i != null) { + int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (device != null && autoConnectDevice.equals(device)) { + return true; + } + } + } + return false; + } public BluetoothDeviceProfileState(Context context, String address, - BluetoothService service, BluetoothA2dpService a2dpService) { + BluetoothService service, BluetoothA2dpService a2dpService, boolean setTrust) { super(address); mContext = context; mDevice = new BluetoothDevice(address); @@ -231,6 +243,8 @@ public final class BluetoothDeviceProfileState extends StateMachine { filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); + filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST); + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -247,6 +261,10 @@ public final class BluetoothDeviceProfileState extends StateMachine { PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, TAG); mWakeLock.setReferenceCounted(false); + + if (setTrust) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } } private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = -- GitLab From a2281a9982595fbeb1bc4b84a5d218b852fafdab Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 20 Jul 2011 17:10:40 -0700 Subject: [PATCH 0183/1408] when disconnectHeadsetInternal, allow it when the BT is not disabled The old code allowed the disconnection only when BT is enabled. This caused a bug during BT turning off that Headset connection failed to disconnect. Change-Id: Iba15a1595b6987064f071ad90bbb126a6edfe6b5 --- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 32843618dcd..8f2b3d88c2e 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -661,7 +661,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean disconnectHeadsetInternal(BluetoothDevice device) { if (DBG) log("disconnectHeadsetInternal"); - if (mService != null && isEnabled()) { + if (mService != null && !isDisabled()) { try { return mService.disconnectHeadsetInternal(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} -- GitLab From cb4d968b2d4464d2dba5da8050a3d9b0a3a50f91 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 21 Jul 2011 18:13:38 -0700 Subject: [PATCH 0184/1408] Add ability to turn BT on / off on a per application basis. This changes adds an API for system applications to enable bluetooth without all the side effects like auto connection to headsets etc. Also some tweaks to the adapter state machine Change-Id: Ib9f22d548a26d72334b300101c8eb0d80f08a4bb --- .../android/bluetooth/BluetoothAdapter.java | 63 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 ++ .../IBluetoothStateChangeCallback.aidl | 27 ++++++++ 3 files changed, 94 insertions(+) create mode 100644 framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b993bd8e959..ca6f085dfb9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1143,6 +1143,69 @@ public final class BluetoothAdapter { } } + /** + * Enable control of the Bluetooth Adapter for a single application. + * + *

    Some applications need to use Bluetooth for short periods of time to + * transfer data but don't want all the associated implications like + * automatic connection to headsets etc. + * + *

    Multiple applications can call this. This is reference counted and + * Bluetooth disabled only when no one else is using it. There will be no UI + * shown to the user while bluetooth is being enabled. Any user action will + * override this call. For example, if user wants Bluetooth on and the last + * user of this API wanted to disable Bluetooth, Bluetooth will not be + * turned off. + * + *

    This API is only meant to be used by internal applications. Third + * party applications but use {@link #enable} and {@link #disable} APIs. + * + *

    If this API returns true, it means the callback will be called. + * The callback will be called with the current state of Bluetooth. + * If the state is not what was requested, an internal error would be the + * reason. + * + * @param on True for on, false for off. + * @param callback The callback to notify changes to the state. + * @hide + */ + public boolean changeApplicationBluetoothState(boolean on, + BluetoothStateChangeCallback callback) { + if (callback == null) return false; + + try { + return mService.changeApplicationBluetoothState(on, new + StateChangeCallbackWrapper(callback), new Binder()); + } catch (RemoteException e) { + Log.e(TAG, "changeBluetoothState", e); + } + return false; + } + + /** + * @hide + */ + public interface BluetoothStateChangeCallback { + public void onBluetoothStateChange(boolean on); + } + + /** + * @hide + */ + public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { + private BluetoothStateChangeCallback mCallback; + + StateChangeCallbackWrapper(BluetoothStateChangeCallback + callback) { + mCallback = callback; + } + + @Override + public void onBluetoothStateChange(boolean on) { + mCallback.onBluetoothStateChange(on); + } + } + private Set toDeviceSet(String[] addresses) { Set devices = new HashSet(addresses.length); for (int i = 0; i < addresses.length; i++) { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 183772d62aa..be43c510e6b 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothStateChangeCallback; import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealthAppConfiguration; @@ -52,6 +53,9 @@ interface IBluetooth byte[] readOutOfBandData(); int getAdapterConnectionState(); + boolean changeApplicationBluetoothState(boolean on, + in IBluetoothStateChangeCallback callback, in + IBinder b); boolean createBond(in String address); boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer); diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl new file mode 100644 index 00000000000..feccdce57b9 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011, 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; + +/** + * System private API for Bluetooth state change callback. + * + * {@hide} + */ +interface IBluetoothStateChangeCallback +{ + void onBluetoothStateChange(boolean on); +} -- GitLab From 549181ef2fdbc96523a372fcbb6e72a6556dbc4f Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 26 Jul 2011 18:36:49 -0700 Subject: [PATCH 0185/1408] Provide an API to set the friendly name of a remote device. BluetoothDevice setName overwrite the locally cached remote name. The changed name is saved in the local storage so that the change is preserved over power cycle. bug 5081605 Change-Id: I486966033828d153bfb1076a99e274c8a7f41636 --- .../android/bluetooth/BluetoothDevice.java | 48 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 2 + 2 files changed, 50 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d9525a347fa..4cb82204791 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -565,6 +565,54 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** + * Get the Bluetooth alias of the remote device. + *

    Alias is the locally modified name of a remote device. + * + * @return the Bluetooth alias, or null if no alias or there was a problem + * @hide + */ + public String getAlias() { + try { + return sService.getRemoteAlias(mAddress); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + + /** + * Set the Bluetooth alias of the remote device. + *

    Alias is the locally modified name of a remote device. + *

    This methoid overwrites the alias. The changed + * alias is saved in the local storage so that the change + * is preserved over power cycle. + * + * @return true on success, false on error + * @hide + */ + public boolean setAlias(String alias) { + try { + return sService.setRemoteAlias(mAddress, alias); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the Bluetooth alias of the remote device. + * If Alias is null, get the Bluetooth name instead. + * @see #getAlias() + * @see #getName() + * + * @return the Bluetooth alias, or null if no alias or there was a problem + * @hide + */ + public String getAliasName() { + String name = getAlias(); + if (name == null) { + name = getName(); + } + return name; + } + /** * Start the bonding (pairing) process with the remote device. *

    This is an asynchronous call, it will return immediately. Register diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 183772d62aa..da66b1a863c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -62,6 +62,8 @@ interface IBluetooth boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer); String getRemoteName(in String address); + String getRemoteAlias(in String address); + boolean setRemoteAlias(in String address, in String name); int getRemoteClass(in String address); ParcelUuid[] getRemoteUuids(in String address); boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback); -- GitLab From 3ee910d6a5eb988acfc0561d9e6e320621f884b3 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 1 Aug 2011 19:11:18 -0700 Subject: [PATCH 0186/1408] Fix tethering using BT. 1. Since Input device runs in Bluetooth Service, avoid the proxy call. 2. Accept or reject incoming tethering connections. This broke because of incoming connection request change for phonebook, HID, A2DP. Change-Id: Ia8c537bb79e4dbc66cfb6b23dcad4f99dbf950b3 --- .../BluetoothDeviceProfileState.java | 4 ++-- .../bluetooth/BluetoothInputDevice.java | 22 ------------------- .../java/android/bluetooth/IBluetooth.aidl | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 095cd110eb7..316c474c30e 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -1048,12 +1048,12 @@ public final class BluetoothDeviceProfileState extends StateMachine { break; case CONNECT_HID_INCOMING: if (!accept) { - ret = mService.allowIncomingHidConnect(mDevice, false); + ret = mService.allowIncomingProfileConnect(mDevice, false); sendMessage(TRANSITION_TO_STABLE); updateIncomingAllowedTimer(); } else { writeTimerValue(0); - ret = mService.allowIncomingHidConnect(mDevice, true); + ret = mService.allowIncomingProfileConnect(mDevice, true); } break; default: diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index f6757d9bd36..282b70a42c8 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -308,28 +308,6 @@ public final class BluetoothInputDevice implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - /** - * Allow or disallow incoming connection - * @param device Input device - * @param allow true / false - * @return Success or Failure of the operation - * @hide - */ - public boolean allowIncomingConnect(BluetoothDevice device, boolean allow) { - if (DBG) log("allowIncomingConnect(" + device + ", " + allow + ")"); - - if (mService == null || !isEnabled() || !isValidDevice(device)) { - return false; - } - try { - mService.allowIncomingHidConnect(device, allow); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; - } - return true; - } - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index ddede9c7d10..48dfed82caa 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -85,6 +85,7 @@ interface IBluetooth int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); void removeServiceRecord(int handle); + boolean allowIncomingProfileConnect(in BluetoothDevice device, boolean value); boolean connectHeadset(String address); boolean disconnectHeadset(String address); @@ -98,7 +99,6 @@ interface IBluetooth int getInputDeviceConnectionState(in BluetoothDevice device); boolean setInputDevicePriority(in BluetoothDevice device, int priority); int getInputDevicePriority(in BluetoothDevice device); - boolean allowIncomingHidConnect(in BluetoothDevice device, boolean value); boolean isTetheringOn(); void setBluetoothTethering(boolean value); -- GitLab From 70d20032ee4e2f86ebf29409b1ba0206960c1c28 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 16 Jun 2011 21:43:29 -0700 Subject: [PATCH 0187/1408] DO NOT MERGE Incoming Bluetooth Connection requests - dialog. This sends the intents to the Settings app to show the dialogs for the incoming connection requests. Change-Id: Ibe267f7cda28ce2a46c25800df2e8ac685b0b9e6 --- .../java/android/bluetooth/BluetoothA2dp.java | 16 ++ .../android/bluetooth/BluetoothDevice.java | 27 ++ .../BluetoothDeviceProfileState.java | 246 +++++++++++++++++- .../android/bluetooth/BluetoothHeadset.java | 17 ++ .../android/bluetooth/IBluetoothA2dp.aidl | 2 + .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 6 files changed, 301 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7e5f858f050..8218c0352ac 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -269,6 +269,22 @@ public final class BluetoothA2dp { } } + /** + * Allow or disallow incoming connection + * @param device Sink + * @param value True / False + * @return Success or Failure of the binder call. + */ + public boolean allowIncomingConnect(BluetoothDevice device, boolean value) { + if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")"); + try { + return mService.allowIncomingConnect(device, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** Helper for converting a state to a string. * For debug use only - strings are not internationalized. * @hide diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index aee6ad86561..e67ace08f7c 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -276,6 +276,33 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REQUEST = + "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_REPLY = + "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_ACCESS_CANCEL = + "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; + /** + * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. + * @hide + */ + public static final String EXTRA_CONNECTION_ACCESS_RESULT = + "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; + + /**@hide*/ + public static final int CONNECTION_ACCESS_YES = 1; + + /**@hide*/ + public static final int CONNECTION_ACCESS_NO = 2; + /** A bond attempt succeeded * @hide */ public static final int BOND_SUCCESS = 0; diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index f6d7073cb30..5f564761dd3 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -21,9 +21,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Message; +import android.os.PowerManager; import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.util.Log; +import android.util.Pair; import com.android.internal.util.HierarchicalState; import com.android.internal.util.HierarchicalStateMachine; @@ -73,9 +75,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine public static final int AUTO_CONNECT_PROFILES = 101; public static final int TRANSITION_TO_STABLE = 102; public static final int CONNECT_OTHER_PROFILES = 103; + private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs + private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs + private static final int CONNECTION_ACCESS_UNDEFINED = -1; + private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec + private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours + + private static final String PREFS_NAME = "ConnectionAccess"; private BondedDevice mBondedDevice = new BondedDevice(); private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); @@ -90,10 +100,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothPbap mPbapService; private boolean mHeadsetServiceConnected; private boolean mPbapServiceConnected; + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private BluetoothDevice mDevice; private int mHeadsetState; private int mA2dpState; + private long mIncomingRejectTimer; + private boolean mConnectionAccessReplyReceived = false; + private Pair mIncomingConnections; + private PowerManager.WakeLock mWakeLock; + private PowerManager mPowerManager; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -108,6 +124,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine int initiator = intent.getIntExtra( BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR, BluetoothHeadset.LOCAL_DISCONNECT); + // We trust this device now + if (newState == BluetoothHeadset.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } mHeadsetState = newState; if (newState == BluetoothHeadset.STATE_DISCONNECTED && initiator == BluetoothHeadset.REMOTE_DISCONNECT) { @@ -121,6 +141,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0); int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0); mA2dpState = newState; + // We trust this device now + if (newState == BluetoothA2dp.STATE_CONNECTED) { + setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); + } if ((oldState == BluetoothA2dp.STATE_CONNECTED || oldState == BluetoothA2dp.STATE_PLAYING) && newState == BluetoothA2dp.STATE_DISCONNECTED) { @@ -134,6 +158,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // This is technically not needed, but we can get stuck sometimes. // For example, if incoming A2DP fails, we are not informed by Bluez sendMessage(TRANSITION_TO_STABLE); + } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { + mWakeLock.release(); + int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + BluetoothDevice.CONNECTION_ACCESS_NO); + Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY); + msg.arg1 = val; + sendMessage(msg); } } }; @@ -174,11 +205,20 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); mContext.registerReceiver(mBroadcastReceiver, filter); HeadsetServiceListener l = new HeadsetServiceListener(); PbapServiceListener p = new PbapServiceListener(); + + mIncomingConnections = mService.getIncomingState(address); + mIncomingRejectTimer = readTimerValue(); + mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | + PowerManager.ACQUIRE_CAUSES_WAKEUP | + PowerManager.ON_AFTER_RELEASE, TAG); + mWakeLock.setReferenceCounted(false); } private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener { @@ -438,6 +478,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // Ignore Log.e(TAG, "Error: Incoming connection with a pending incoming connection"); break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + + handleIncomingConnection(CONNECT_HFP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_HFP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_INCOMING: // Serialize the commands. deferMessage(message); @@ -608,6 +666,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_A2DP_INCOMING: // ignore break; + case CONNECTION_ACCESS_REQUEST_REPLY: + int val = message.arg1; + mConnectionAccessReplyReceived = true; + boolean value = false; + if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { + value = true; + } + setTrust(val); + handleIncomingConnection(CONNECT_A2DP_INCOMING, value); + break; + case CONNECTION_ACCESS_REQUEST_EXPIRY: + // The check protects the race condition between REQUEST_REPLY + // and the timer expiry. + if (!mConnectionAccessReplyReceived) { + handleIncomingConnection(CONNECT_A2DP_INCOMING, false); + sendConnectionAccessRemovalIntent(); + sendMessage(TRANSITION_TO_STABLE); + } + break; case CONNECT_A2DP_OUTGOING: // Defer message and retry deferMessage(message); @@ -663,8 +740,138 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(msg); } + private void updateIncomingAllowedTimer() { + // Not doing a perfect exponential backoff because + // we want two different rates. For all practical + // purposes, this is good enough. + if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER; + + mIncomingRejectTimer *= 5; + if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) { + mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER; + } + writeTimerValue(mIncomingRejectTimer); + } + + private boolean handleIncomingConnection(int command, boolean accept) { + boolean ret = false; + Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept); + switch (command) { + case CONNECT_HFP_INCOMING: + if (!accept) { + ret = mHeadsetService.rejectIncomingConnect(mDevice); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { + writeTimerValue(0); + ret = mHeadsetService.acceptIncomingConnect(mDevice); + } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { + writeTimerValue(0); + handleConnectionOfOtherProfiles(command); + ret = mHeadsetService.createIncomingConnect(mDevice); + } + break; + case CONNECT_A2DP_INCOMING: + if (!accept) { + ret = mA2dpService.allowIncomingConnect(mDevice, false); + sendMessage(TRANSITION_TO_STABLE); + updateIncomingAllowedTimer(); + } else { + writeTimerValue(0); + ret = mA2dpService.allowIncomingConnect(mDevice, true); + handleConnectionOfOtherProfiles(command); + } + break; + default: + Log.e(TAG, "Waiting for incoming connection but state changed to:" + command); + break; + } + return ret; + } + + private void sendConnectionAccessIntent() { + mConnectionAccessReplyReceived = false; + + if (!mPowerManager.isScreenOn()) mWakeLock.acquire(); + + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private void sendConnectionAccessRemovalIntent() { + mWakeLock.release(); + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + + private int getTrust() { + String address = mDevice.getAddress(); + if (mIncomingConnections != null) return mIncomingConnections.first; + return CONNECTION_ACCESS_UNDEFINED; + } + + + private String getStringValue(long value) { + StringBuilder sbr = new StringBuilder(); + sbr.append(Long.toString(System.currentTimeMillis())); + sbr.append("-"); + sbr.append(Long.toString(value)); + return sbr.toString(); + } + + private void setTrust(int value) { + String second; + if (mIncomingConnections == null) { + second = getStringValue(INIT_INCOMING_REJECT_TIMER); + } else { + second = mIncomingConnections.second; + } + + mIncomingConnections = new Pair(value, second); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private void writeTimerValue(long value) { + Integer first; + if (mIncomingConnections == null) { + first = CONNECTION_ACCESS_UNDEFINED; + } else { + first = mIncomingConnections.first; + } + mIncomingConnections = new Pair(first, getStringValue(value)); + mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); + } + + private long readTimerValue() { + if (mIncomingConnections == null) + return 0; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + return Long.parseLong(splits[1]); + } + return 0; + } + + private boolean readIncomingAllowedValue() { + if (readTimerValue() == 0) return true; + String value = mIncomingConnections.second; + String[] splits = value.split("-"); + if (splits != null && splits.length == 2) { + long val1 = Long.parseLong(splits[0]); + long val2 = Long.parseLong(splits[1]); + if (val1 + val2 <= System.currentTimeMillis()) { + return true; + } + } + return false; + } + synchronized boolean processCommand(int command) { - Log.i(TAG, "Processing command:" + command); + Log.e(TAG, "Processing command:" + command); + Message msg; switch(command) { case CONNECT_HFP_OUTGOING: if (mHeadsetService != null) { @@ -674,11 +881,21 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_HFP_INCOMING: if (!mHeadsetServiceConnected) { deferProfileServiceMessage(command); - } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { - return mHeadsetService.acceptIncomingConnect(mDevice); - } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { - handleConnectionOfOtherProfiles(command); - return mHeadsetService.createIncomingConnect(mDevice); + } else { + // Check if device is already trusted + int access = getTrust(); + if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { + handleIncomingConnection(command, true); + } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && + !readIncomingAllowedValue()) { + handleIncomingConnection(command, false); + } else { + sendConnectionAccessIntent(); + msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); + sendMessageDelayed(msg, + CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); + } + return true; } break; case CONNECT_A2DP_OUTGOING: @@ -687,8 +904,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case CONNECT_A2DP_INCOMING: - handleConnectionOfOtherProfiles(command); - // ignore, Bluez takes care + // Check if device is already trusted + int access = getTrust(); + if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { + handleIncomingConnection(command, true); + } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && + !readIncomingAllowedValue()) { + handleIncomingConnection(command, false); + } else { + sendConnectionAccessIntent(); + msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); + sendMessageDelayed(msg, + CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); + } return true; case DISCONNECT_HFP_OUTGOING: if (!mHeadsetServiceConnected) { @@ -729,6 +957,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } break; case UNPAIR: + writeTimerValue(INIT_INCOMING_REJECT_TIMER); + setTrust(CONNECTION_ACCESS_UNDEFINED); return mService.removeBondInternal(mDevice.getAddress()); default: Log.e(TAG, "Error: Unknown Command"); diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 197b022cad0..61b5a0b0161 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -456,6 +456,23 @@ public final class BluetoothHeadset { return false; } + /** + * Reject the incoming connection. + * @hide + */ + public boolean rejectIncomingConnect(BluetoothDevice device) { + if (DBG) log("rejectIncomingConnect"); + if (mService != null) { + try { + return mService.rejectIncomingConnect(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; + } + /** * Connect to a Bluetooth Headset. * Note: This is an internal function and shouldn't be exposed diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 40f10583b63..0c2cf1b1c62 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -36,4 +36,6 @@ interface IBluetoothA2dp { boolean connectSinkInternal(in BluetoothDevice device); boolean disconnectSinkInternal(in BluetoothDevice device); + boolean allowIncomingConnect(in BluetoothDevice device, boolean value); + } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index d96f0ca0de8..62bceeed5e9 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -37,6 +37,7 @@ interface IBluetoothHeadset { boolean createIncomingConnect(in BluetoothDevice device); boolean acceptIncomingConnect(in BluetoothDevice device); + boolean rejectIncomingConnect(in BluetoothDevice device); boolean cancelConnectThread(); boolean connectHeadsetInternal(in BluetoothDevice device); boolean disconnectHeadsetInternal(in BluetoothDevice device); -- GitLab From a13f8e871192642e66c38b0bc8166aa2629476f6 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 3 Aug 2011 14:17:22 -0700 Subject: [PATCH 0188/1408] Enforce permission for changeApplicationState function. The ADMIN permission is for use cases where we want to manage BT connection at a deeper level. So just the Bluetooth permission is good enough here. Change-Id: Iddd038fe9f9a26f155b4edc9484ba1fe27b178ba --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ca6f085dfb9..28bc424d937 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1163,7 +1163,10 @@ public final class BluetoothAdapter { *

    If this API returns true, it means the callback will be called. * The callback will be called with the current state of Bluetooth. * If the state is not what was requested, an internal error would be the - * reason. + * reason. If Bluetooth is already on and if this function is called to turn + * it on, the api will return true and a callback will be called. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} * * @param on True for on, false for off. * @param callback The callback to notify changes to the state. -- GitLab From 5317842b3d84997bc5327a404cea1d6bcd9d515d Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Fri, 19 Aug 2011 10:26:32 -0700 Subject: [PATCH 0189/1408] Add Api to get profile connection state. This gets the current connection state of the profile with respect to the local Bluetooth adapter. Change-Id: I7cff6c9201d29a8b45413cff7384b7483f21bd5c --- .../android/bluetooth/BluetoothAdapter.java | 25 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 6 +++++ .../java/android/bluetooth/IBluetooth.aidl | 3 ++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 28bc424d937..264db1917d1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -773,6 +773,31 @@ public final class BluetoothAdapter { return BluetoothAdapter.STATE_DISCONNECTED; } + /** + * Get the current connection state of a profile. + * This function can be used to check whether the local Bluetooth adapter + * is connected to any remote device for a specific profile. + * Profile can be one of {@link BluetoothProfile.HEADSET}, + * {@link BluetoothProfile.A2DP}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH}. + * + *

    Return value can be one of + * {@link * BluetoothProfile.STATE_DISCONNECTED}, + * {@link * BluetoothProfile.STATE_CONNECTING}, + * {@link * BluetoothProfile.STATE_CONNECTED}, + * {@link * BluetoothProfile.STATE_DISCONNECTING} + * @hide + */ + public int getProfileConnectionState(int profile) { + if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; + try { + return mService.getProfileConnectionState(profile); + } catch (RemoteException e) {Log.e(TAG, "getProfileConnectionState:", e);} + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** /** * Picks RFCOMM channels until none are left. * Avoids reserved channels. diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 6cd81fde722..58b38683879 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -82,6 +82,12 @@ public interface BluetoothProfile { */ public static final int PAN = 5; + /** + * PBAP + * @hide + */ + public static final int PBAP = 6; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 48dfed82caa..d4e7f7d48ed 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -53,6 +53,7 @@ interface IBluetooth byte[] readOutOfBandData(); int getAdapterConnectionState(); + int getProfileConnectionState(int profile); boolean changeApplicationBluetoothState(boolean on, in IBluetoothStateChangeCallback callback, in IBinder b); @@ -121,5 +122,5 @@ interface IBluetooth List getHealthDevicesMatchingConnectionStates(in int[] states); int getHealthDeviceConnectionState(in BluetoothDevice device); - void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState); + void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); } -- GitLab From c875f3a646d871711988575642b38a17bf79f0f8 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 23 Aug 2011 12:21:55 -0700 Subject: [PATCH 0190/1408] Make profile connection state API public. Change-Id: I1f2810d4e820142435f7bbda051c98ec3e3cf3eb --- .../android/bluetooth/BluetoothAdapter.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 264db1917d1..223692859d8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -777,23 +777,24 @@ public final class BluetoothAdapter { * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter * is connected to any remote device for a specific profile. - * Profile can be one of {@link BluetoothProfile.HEADSET}, - * {@link BluetoothProfile.A2DP}. + * Profile can be one of {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}. * *

    Requires {@link android.Manifest.permission#BLUETOOTH}. * *

    Return value can be one of - * {@link * BluetoothProfile.STATE_DISCONNECTED}, - * {@link * BluetoothProfile.STATE_CONNECTING}, - * {@link * BluetoothProfile.STATE_CONNECTED}, - * {@link * BluetoothProfile.STATE_DISCONNECTING} - * @hide + * {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_CONNECTING}, + * {@link BluetoothProfile#STATE_CONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING} */ public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { return mService.getProfileConnectionState(profile); - } catch (RemoteException e) {Log.e(TAG, "getProfileConnectionState:", e);} + } catch (RemoteException e) { + Log.e(TAG, "getProfileConnectionState:", e); + } return BluetoothProfile.STATE_DISCONNECTED; } -- GitLab From 826fc9b7a2820415618a842433035e8c3eca1a5d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 19 Aug 2011 02:24:24 -0700 Subject: [PATCH 0191/1408] Proactively disable data when over policy limit. Add policy controls to NetworkStateTracker which are combined with other user preference and internal flags to decide if data connection should be established. Better locking around enabled flags. When data network would be over limit, proactively disable data on that network. Enable when policy is snoozed or when cycle resets. Track and dismiss notifications from now-stale policies. Bug: 4587023, 5178147 Change-Id: Ibfcc9f73cda7c369209af701b46eddd3d1943f2d --- .../bluetooth/BluetoothTetheringDataTracker.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index a7b00375e84..83d1bda8210 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpInfoInternal; -import android.net.LinkAddress; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -30,7 +29,6 @@ import android.os.Handler; import android.os.Message; import android.util.Log; -import java.net.InetAddress; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -184,11 +182,14 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return -1; } - /** - * @param enabled - */ - public void setDataEnable(boolean enabled) { - android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled); + @Override + public void setUserDataEnable(boolean enabled) { + Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); + } + + @Override + public void setPolicyDataEnable(boolean enabled) { + Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); } /** -- GitLab From 6e1b765e5b8cc70bacbaf04862d00cf8cc8c4a99 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 25 Aug 2011 16:45:58 -0700 Subject: [PATCH 0192/1408] Move Bluetooth remove service record handler to the app main thread Move Bluetooth remove service record handler to the app main thread. This removes the dependency of caller thread that constructs the BluetoothAdapter singleton instance. The caller thread could stop while BluetoothAdapter singleton exists but lose the handler context. Make the BluetoothService.removeServiceRecord return quickly without blocking on native call. bug 4999443 Change-Id: I302534baa381f4aca8192e1e4a9ea21793b07ba6 --- .../android/bluetooth/BluetoothAdapter.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 223692859d8..254c98ff8cf 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; @@ -348,6 +349,8 @@ public final class BluetoothAdapter { private final IBluetooth mService; + private Handler mServiceRecordHandler; + /** * Get a handle to the default local Bluetooth adapter. *

    Currently Android only supports one Bluetooth adapter, but the API @@ -376,6 +379,7 @@ public final class BluetoothAdapter { throw new IllegalArgumentException("service is null"); } mService = service; + mServiceRecordHandler = null; } /** @@ -1011,7 +1015,21 @@ public final class BluetoothAdapter { } catch (IOException e) {} throw new IOException("Not able to register SDP record for " + name); } - socket.setCloseHandler(mHandler, handle); + + if (mServiceRecordHandler == null) { + mServiceRecordHandler = new Handler(Looper.getMainLooper()) { + public void handleMessage(Message msg) { + /* handle socket closing */ + int handle = msg.what; + try { + if (DBG) Log.d(TAG, "Removing service record " + + Integer.toHexString(handle)); + mService.removeServiceRecord(handle); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + }; + } + socket.setCloseHandler(mServiceRecordHandler, handle); return socket; } @@ -1243,17 +1261,6 @@ public final class BluetoothAdapter { return Collections.unmodifiableSet(devices); } - private Handler mHandler = new Handler() { - public void handleMessage(Message msg) { - /* handle socket closing */ - int handle = msg.what; - try { - if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle)); - mService.removeServiceRecord(handle); - } catch (RemoteException e) {Log.e(TAG, "", e);} - } - }; - /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" *

    Alphabetic characters must be uppercase to be valid. -- GitLab From 81b34d6f896f7a390e53db26c7c17dae6f308251 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 2 Sep 2011 14:20:56 -0700 Subject: [PATCH 0193/1408] Fix issue 5252593: any app can restart the runtime Replace null device address string by empty sting. Change-Id: I285c35f3345334e6d2190493b1a8a5aca1a361a4 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 2 +- framework/java/android/bluetooth/BluetoothProfileState.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 316c474c30e..48d0203b06f 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -127,7 +127,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (!device.equals(mDevice)) return; + if (device == null || !device.equals(mDevice)) return; if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java index 98afdb82a8e..b0c0a0bc463 100644 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ b/framework/java/android/bluetooth/BluetoothProfileState.java @@ -59,7 +59,9 @@ public class BluetoothProfileState extends StateMachine { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - + if (device == null) { + return; + } if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED || -- GitLab From 090847e4abeee69744aaf1a75d6d8b30ff20bd21 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 31 Aug 2011 15:36:05 -0700 Subject: [PATCH 0194/1408] Make Bluetooth Health APIs public. Fix a few bugs: a) Pass a integer token to identify the channel. b) Close fds in case of errors. Change-Id: I2046787be5008769435f2f72a5bd67c19b749da0 --- .../android/bluetooth/BluetoothHealth.java | 52 ++++++++++++------- .../BluetoothHealthAppConfiguration.java | 8 ++- .../bluetooth/BluetoothHealthCallback.java | 44 ++++++++++++---- .../java/android/bluetooth/IBluetooth.aidl | 2 +- .../bluetooth/IBluetoothHealthCallback.aidl | 3 +- 5 files changed, 75 insertions(+), 34 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 0a01dcf80e1..c165d9231d6 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -32,10 +32,25 @@ import java.util.List; *

    BluetoothHealth is a proxy object for controlling the Bluetooth * Service via IPC. * - *

    Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHealth proxy object. Use - * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. - * @hide + *

    How to connect to a health device which is acting in the source role. + *

  • Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHealth proxy object.
  • + *
  • Create an {@link BluetoothHealth} callback and call + * {@link #registerSinkAppConfiguration} to register an application + * configuration
  • + *
  • Pair with the remote device. This currently needs to be done manually + * from Bluetooth Settings
  • + *
  • Connect to a health device using {@link #connectChannelToSource}. Some + * devices will connect the channel automatically. The {@link BluetoothHealth} + * callback will inform the application of channel state change.
  • + *
  • Use the file descriptor provided with a connected channel to read and + * write data to the health channel.
  • + *
  • The received data needs to be interpreted using a health manager which + * implements the IEEE 11073-xxxxx specifications. + *
  • When done, close the health channel by calling {@link #disconnectChannel} + * and unregister the application configuration calling + * {@link #unregisterAppConfiguration} + * */ public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; @@ -137,7 +152,6 @@ public final class BluetoothHealth implements BluetoothProfile { * * @param config The health app configuration * @return Success or failure. - * @hide */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; @@ -222,16 +236,15 @@ public final class BluetoothHealth implements BluetoothProfile { * @param device The remote Bluetooth device. * @param config The application configuration which has been registered using * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @param fd The file descriptor that was associated with the channel. + * @param channelId The channel id associated with the channel * @return If true, the callback associated with the application config will be called. - * @hide */ public boolean disconnectChannel(BluetoothDevice device, - BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { + BluetoothHealthAppConfiguration config, int channelId) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.disconnectChannel(device, config, fd); + return mService.disconnectChannel(device, config, channelId); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -248,11 +261,13 @@ public final class BluetoothHealth implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + *

    Its the responsibility of the caller to close the ParcelFileDescriptor + * when done. + * * @param device The remote Bluetooth health device * @param config The application configuration * @return null on failure, ParcelFileDescriptor on success. */ - public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && @@ -300,7 +315,7 @@ public final class BluetoothHealth implements BluetoothProfile { } /** - * Get connected devices for this specific profile. + * Get connected devices for the health profile. * *

    Return the set of devices which are in state {@link #STATE_CONNECTED} * @@ -368,14 +383,15 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, int status) { - mCallback.onHealthAppConfigurationStatusChange(config, status); + mCallback.onHealthAppConfigurationStatusChange(config, status); } @Override public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd) { - mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); + ParcelFileDescriptor fd, int channelId) { + mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd, + channelId); } } @@ -389,13 +405,13 @@ public final class BluetoothHealth implements BluetoothProfile { public static final int STATE_CHANNEL_DISCONNECTING = 3; /** Health App Configuration registration success */ - public static final int APPLICATION_REGISTRATION_SUCCESS = 0; + public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; /** Health App Configuration registration failure */ - public static final int APPLICATION_REGISTRATION_FAILURE = 1; + public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; /** Health App Configuration un-registration success */ - public static final int APPLICATION_UNREGISTRATION_SUCCESS = 2; + public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; /** Health App Configuration un-registration failure */ - public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; + public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; private ServiceListener mServiceListener; private IBluetooth mService; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 7020249c4cc..15a9101452e 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -26,7 +26,6 @@ import android.os.Parcelable; * that the Bluetooth Health third party application will register to communicate with the * remote Bluetooth health device. * - * @hide */ public final class BluetoothHealthAppConfiguration implements Parcelable { private final String mName; @@ -39,6 +38,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device + * @hide */ BluetoothHealthAppConfiguration(String name, int dataType) { mName = name; @@ -54,6 +54,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * @param dataType Data Type of the remote Bluetooth Health device * @param role {@link BluetoothHealth#SOURCE_ROLE} or * {@link BluetoothHealth#SINK_ROLE} + * @hide */ BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType) { @@ -93,7 +94,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { mChannelType + "]"; } - @Override public int describeContents() { return 0; } @@ -132,6 +132,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or * {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or * {@link BluetoothHealth#CHANNEL_TYPE_ANY}. + * @hide */ public int getChannelType() { return mChannelType; @@ -155,13 +156,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { } }; - @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); out.writeInt(mRole); out.writeInt(mChannelType); } - - } diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index 0d11bb54468..baf2adee849 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -21,22 +21,48 @@ import android.os.ParcelFileDescriptor; import android.util.Log; /** - * This class is used for all the {@link BluetoothHealth} callbacks. - * @hide + * This abstract class is used to implement {@link BluetoothHealth} callbacks. */ public abstract class BluetoothHealthCallback { - private static final String TAG = "BluetoothHealthCallback"; + /** + * Callback to inform change in registration state of the health + * application. + *

    This callback is called on the binder thread (not on the UI thread) + * + * @param config Bluetooth Health app configuration + * @param status Success or failure of the registration or unregistration + * calls. Can be one of + * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or + * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or + * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or + * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} + */ public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status); + int status) { + Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); } + /** + * Callback to inform change in channel state. + *

    Its the responsibility of the implementor of this callback to close the + * parcel file descriptor when done. This callback is called on the Binder + * thread (not the UI thread) + * + * @param config The Health app configutation + * @param device The Bluetooth Device + * @param prevState The previous state of the channel + * @param newState The new state of the channel. + * @param fd The Parcel File Descriptor when the channel state is connected. + * @param channelId The id associated with the channel. This id will be used + * in future calls like when disconnecting the channel. + */ public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd) { - Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device + - "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); + BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, + int channelId) { + Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + + "ChannelId:" + channelId); } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index d4e7f7d48ed..fefeb9335da 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -116,7 +116,7 @@ interface IBluetooth boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, in ParcelFileDescriptor fd); + boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); List getConnectedHealthDevices(); List getHealthDevicesMatchingConnectionStates(in int[] states); diff --git a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl index 9fe5335c5ae..0ace9fe1230 100644 --- a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl @@ -27,5 +27,6 @@ interface IBluetoothHealthCallback { void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, - in BluetoothDevice device, int prevState, int newState, in ParcelFileDescriptor fd); + in BluetoothDevice device, int prevState, int newState, in + ParcelFileDescriptor fd, int id); } -- GitLab From 2c7f14821529bd1b44094faead3242bec64892f2 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 7 Sep 2011 14:16:52 -0700 Subject: [PATCH 0195/1408] Add error codes for channel disconnection / connection. Channel connection / disconnection was handled as boolean, doesn't capture all the values. Also make it asynchronous instead of the dbus call being synchronous. Change-Id: If30177b9f93b7c83f162fbbc1233edf3e46dbfea --- .../java/android/bluetooth/BluetoothHealth.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index c165d9231d6..9b2b8cad20f 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -81,6 +81,20 @@ public final class BluetoothHealth implements BluetoothProfile { */ public static final int CHANNEL_TYPE_ANY = 12; + /** @hide */ + public static final int HEALTH_OPERATION_SUCCESS = 6000; + /** @hide */ + public static final int HEALTH_OPERATION_ERROR = 6001; + /** @hide */ + public static final int HEALTH_OPERATION_INVALID_ARGS = 6002; + /** @hide */ + public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003; + /** @hide */ + public static final int HEALTH_OPERATION_NOT_FOUND = 6004; + /** @hide */ + public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005; + + /** * Register an application configuration that acts as a Health SINK. * This is the configuration that will be used to communicate with health devices -- GitLab From 9240efb4a7a8dbe87b268f3c52d927f718d52492 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 21 Sep 2011 15:53:20 -0700 Subject: [PATCH 0196/1408] Make Bluetooth Health constant public. Change-Id: I366f1231056fe978d85ee80c773d5911badb22e2 --- framework/java/android/bluetooth/BluetoothProfile.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 58b38683879..f7ccfbd35fb 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -66,7 +66,6 @@ public interface BluetoothProfile { /** * Health Profile - * @hide */ public static final int HEALTH = 3; -- GitLab From 3c0c5bee534ff1aa5dbf50385eacc3b9a8bcfee6 Mon Sep 17 00:00:00 2001 From: Scott Main Date: Mon, 26 Sep 2011 22:59:38 -0700 Subject: [PATCH 0197/1408] docs: mix of BT and NFC javadoc updates Add Health profile to various discussions about profiles Add descriptions to NFC interfaces, tweak some desciptions, and fix some broken links Change-Id: Ib89434c78a4ad60b4358dca9a6c335451d1c4125 --- framework/java/android/bluetooth/BluetoothAdapter.java | 10 +++++----- framework/java/android/bluetooth/BluetoothProfile.java | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 254c98ff8cf..ea5c3db8657 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -781,7 +781,7 @@ public final class BluetoothAdapter { * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter * is connected to any remote device for a specific profile. - * Profile can be one of {@link BluetoothProfile#HEADSET}, + * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}. * *

    Requires {@link android.Manifest.permission#BLUETOOTH}. @@ -1132,15 +1132,15 @@ public final class BluetoothAdapter { /** * Get the profile proxy object associated with the profile. * - *

    Profile can be one of {@link BluetoothProfile#HEADSET} or + *

    Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or * {@link BluetoothProfile#A2DP}. Clients must implements * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. - * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET} - * or {@link BluetoothProfile#A2DP}. + * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, + * {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, @@ -1172,7 +1172,7 @@ public final class BluetoothAdapter { * *

    Clients should call this when they are no longer using * the proxy obtained from {@link #getProfileProxy}. - * Profile can be one of {@link BluetoothProfile#HEADSET} or + * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or * {@link BluetoothProfile#A2DP} * * @param profile diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f7ccfbd35fb..1920efa52f8 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -161,9 +161,9 @@ public interface BluetoothProfile { /** * Called to notify the client when the proxy object has been * connected to the service. - * @param profile - One of {@link #HEADSET} or + * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or * {@link #A2DP} - * @param proxy - One of {@link BluetoothHeadset} or + * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or * {@link BluetoothA2dp} */ public void onServiceConnected(int profile, BluetoothProfile proxy); @@ -171,7 +171,7 @@ public interface BluetoothProfile { /** * Called to notify the client that this proxy object has been * disconnected from the service. - * @param profile - One of {@link #HEADSET} or + * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or * {@link #A2DP} */ public void onServiceDisconnected(int profile); -- GitLab From ba75fdeab7aa8f0cb4ee7f5090ead18f725caa05 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 26 Oct 2011 15:03:08 -0700 Subject: [PATCH 0198/1408] Fix auto connection of headset profile. Sometimes when headset service doesn't get bound, the auto connection fails. Based on a patch by: Chunho Park Change-Id: Ia24613b47487717f51895953c7e8bc52abffecb5 --- .../android/bluetooth/BluetoothDeviceProfileState.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 48d0203b06f..7addd4a56d1 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -110,6 +110,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { private BluetoothHeadset mHeadsetService; private BluetoothPbap mPbapService; private boolean mPbapServiceConnected; + private boolean mAutoConnectionPending; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private BluetoothDevice mDevice; @@ -272,6 +273,10 @@ public final class BluetoothDeviceProfileState extends StateMachine { public void onServiceConnected(int profile, BluetoothProfile proxy) { synchronized(BluetoothDeviceProfileState.this) { mHeadsetService = (BluetoothHeadset) proxy; + if (mAutoConnectionPending) { + sendMessage(AUTO_CONNECT_PROFILES); + mAutoConnectionPending = false; + } } } public void onServiceDisconnected(int profile) { @@ -360,8 +365,9 @@ public final class BluetoothDeviceProfileState extends StateMachine { // Don't auto connect to docks. break; } else { - if (mHeadsetService != null && - mHeadsetService.getPriority(mDevice) == + if (mHeadsetService == null) { + mAutoConnectionPending = true; + } else if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_AUTO_CONNECT && mHeadsetService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, -- GitLab From 6e9f6f07aed500ecbfe7e5d7f311c402d60909b0 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 27 Oct 2011 11:31:12 -0700 Subject: [PATCH 0199/1408] Make public APIs of ACTION_UUID, getUuids, and fetchUuidsWithSdp Give APP a way to find out if a service is supported by a remote device bug 5487202 Change-Id: Ie778b3227f29a24cdf61fa0365b82f9a45d439dc --- .../android/bluetooth/BluetoothDevice.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4cb82204791..e8c368c142e 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -249,11 +249,10 @@ public final class BluetoothDevice implements Parcelable { *

    Always contains the extra field {@link #EXTRA_DEVICE} *

    Always contains the extra filed {@link #EXTRA_UUID} *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = - "android.bleutooth.device.action.UUID"; + "android.bluetooth.device.action.UUID"; /** * Broadcast Action: Indicates a failure to retrieve the name of a remote @@ -770,7 +769,18 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ + /** + * Returns the supported features (UUIDs) of the remote device. + * + *

    This method does not start a service discovery procedure to retrieve the UUIDs + * from the remote device. Instead, the local cached copy of the service + * UUIDs are returned. + *

    Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. + *

    Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return the supported features (UUIDs) of the remote device, + * or null on error + */ public ParcelUuid[] getUuids() { try { return sService.getRemoteUuids(mAddress); @@ -779,18 +789,19 @@ public final class BluetoothDevice implements Parcelable { } /** - * Perform a SDP query on the remote device to get the UUIDs - * supported. This API is asynchronous and an Intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * an Intent is sent with the UUIDs that is currently present in the - * cache. Clients should use the {@link #getUuids} to get UUIDs - * is SDP is not to be performed. + * Perform a service discovery on the remote device to get the UUIDs supported. + * + *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently + * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * if service discovery is not to be performed. + *

    Requires {@link android.Manifest.permission#BLUETOOTH}. * - * @return False if the sanity check fails, True if the process + * @return False if the sanity check fails, True if the process * of initiating an ACL connection to the remote device * was started. - * @hide */ public boolean fetchUuidsWithSdp() { try { -- GitLab From 6adbff6e1e306f3ba517f1a6936d4bc941989c83 Mon Sep 17 00:00:00 2001 From: Ed Heyl Date: Tue, 8 Nov 2011 05:58:37 -0800 Subject: [PATCH 0200/1408] Fix build break; Revert "Make public APIs of ACTION_UUID, getUuids, and fetchUuidsWithSdp" This reverts commit 6e9f6f07aed500ecbfe7e5d7f311c402d60909b0. --- .../android/bluetooth/BluetoothDevice.java | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e8c368c142e..4cb82204791 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -249,10 +249,11 @@ public final class BluetoothDevice implements Parcelable { *

    Always contains the extra field {@link #EXTRA_DEVICE} *

    Always contains the extra filed {@link #EXTRA_UUID} *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = - "android.bluetooth.device.action.UUID"; + "android.bleutooth.device.action.UUID"; /** * Broadcast Action: Indicates a failure to retrieve the name of a remote @@ -769,18 +770,7 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** - * Returns the supported features (UUIDs) of the remote device. - * - *

    This method does not start a service discovery procedure to retrieve the UUIDs - * from the remote device. Instead, the local cached copy of the service - * UUIDs are returned. - *

    Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. - *

    Requires {@link android.Manifest.permission#BLUETOOTH}. - * - * @return the supported features (UUIDs) of the remote device, - * or null on error - */ + /** @hide */ public ParcelUuid[] getUuids() { try { return sService.getRemoteUuids(mAddress); @@ -789,19 +779,18 @@ public final class BluetoothDevice implements Parcelable { } /** - * Perform a service discovery on the remote device to get the UUIDs supported. - * - *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently - * present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. - *

    Requires {@link android.Manifest.permission#BLUETOOTH}. + * Perform a SDP query on the remote device to get the UUIDs + * supported. This API is asynchronous and an Intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * an Intent is sent with the UUIDs that is currently present in the + * cache. Clients should use the {@link #getUuids} to get UUIDs + * is SDP is not to be performed. * - * @return False if the sanity check fails, True if the process + * @return False if the sanity check fails, True if the process * of initiating an ACL connection to the remote device * was started. + * @hide */ public boolean fetchUuidsWithSdp() { try { -- GitLab From dca90f52d7bc835f7b5a65839c997ad3ecf0c825 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 8 Nov 2011 10:58:12 -0800 Subject: [PATCH 0201/1408] Make public APIs of ACTION_UUID, EXTRA_UUID, getUuids, and fetchUuidsWithSdp Give APP a way to find out if a service is supported by a remote device bug 5487202 Change-Id: I5a2d050cf24d945e1c9875963c34177ed1ce773a --- .../android/bluetooth/BluetoothDevice.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4cb82204791..03065218ee9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -247,13 +247,12 @@ public final class BluetoothDevice implements Parcelable { * has been fetched. This intent is sent only when the UUIDs of the remote * device are requested to be fetched using Service Discovery Protocol *

    Always contains the extra field {@link #EXTRA_DEVICE} - *

    Always contains the extra filed {@link #EXTRA_UUID} + *

    Always contains the extra field {@link #EXTRA_UUID} *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = - "android.bleutooth.device.action.UUID"; + "android.bluetooth.device.action.UUID"; /** * Broadcast Action: Indicates a failure to retrieve the name of a remote @@ -451,7 +450,6 @@ public final class BluetoothDevice implements Parcelable { * Used as an extra field in {@link #ACTION_UUID} intents, * Contains the {@link android.os.ParcelUuid}s of the remote device which * is a parcelable version of {@link UUID}. - * @hide */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; @@ -770,7 +768,18 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ + /** + * Returns the supported features (UUIDs) of the remote device. + * + *

    This method does not start a service discovery procedure to retrieve the UUIDs + * from the remote device. Instead, the local cached copy of the service + * UUIDs are returned. + *

    Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. + *

    Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return the supported features (UUIDs) of the remote device, + * or null on error + */ public ParcelUuid[] getUuids() { try { return sService.getRemoteUuids(mAddress); @@ -779,18 +788,19 @@ public final class BluetoothDevice implements Parcelable { } /** - * Perform a SDP query on the remote device to get the UUIDs - * supported. This API is asynchronous and an Intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * an Intent is sent with the UUIDs that is currently present in the - * cache. Clients should use the {@link #getUuids} to get UUIDs - * is SDP is not to be performed. + * Perform a service discovery on the remote device to get the UUIDs supported. + * + *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently + * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * if service discovery is not to be performed. + *

    Requires {@link android.Manifest.permission#BLUETOOTH}. * - * @return False if the sanity check fails, True if the process + * @return False if the sanity check fails, True if the process * of initiating an ACL connection to the remote device * was started. - * @hide */ public boolean fetchUuidsWithSdp() { try { -- GitLab From 1662c96b9d1d12a1f34b1c316870737b276e0149 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 10 Nov 2011 00:03:28 -0800 Subject: [PATCH 0202/1408] Send CONNECT_OTHER_PROFILE to Device profile for hfp and a2dp incoming connect Send CONNECT_OTHER_PROFILE to Device profile for low priority hfp and a2dp incoming connect. In the case when HFP autoconnect is off but a2dp autoconnect is on, if HF autoconnect to HFP, phone will reject HFP but connect a2dp. Before this fix, phone reject HFP. A2dp will not get connected unless the HF do media auto-connect, which most carkits do not do. Also do similar change for incoming a2dp connection bug 5091838 Change-Id: Ife1815f527bcd94e0d9ffc645028484fa9c49a43 --- .../java/android/bluetooth/BluetoothDeviceProfileState.java | 2 +- framework/java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index 7addd4a56d1..b1d007071df 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -86,7 +86,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; - private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs + public static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs private static final int CONNECTION_ACCESS_UNDEFINED = -1; private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index fefeb9335da..deea2b80103 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -90,7 +90,7 @@ interface IBluetooth boolean connectHeadset(String address); boolean disconnectHeadset(String address); - boolean notifyIncomingConnection(String address); + boolean notifyIncomingConnection(String address, boolean rejected); // HID profile APIs boolean connectInputDevice(in BluetoothDevice device); -- GitLab From 70c4b33caa3d366f887e9d94848b6421ba327205 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 16 Nov 2011 12:27:57 -0800 Subject: [PATCH 0203/1408] Check the bluetooth state for getUuid call Donnot make the bluetoothservice.getUuids call if the bluetooth is not on. Also get rid of all the necessary locks on BluetoothService for get property call. It had a lock on BluetoothAdapterProperty. bug5472114 Change-Id: I383472ae6006fc1f0129c960c8a44ed0df027a43 --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ea5c3db8657..d9716525653 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -533,6 +533,7 @@ public final class BluetoothAdapter { * @hide */ public ParcelUuid[] getUuids() { + if (getState() != STATE_ON) return null; try { return mService.getUuids(); } catch (RemoteException e) {Log.e(TAG, "", e);} -- GitLab From 88e2affd9d5eade2397954b254e66e4a706e9352 Mon Sep 17 00:00:00 2001 From: Eric Rowe Date: Tue, 22 Nov 2011 19:16:50 -0800 Subject: [PATCH 0204/1408] Fix SCO start stop tests. Change-Id: Idf292bc244a494e2ffb11359e83c0d072907ea3b Bug-id: http://b/5644683 --- framework/tests/AndroidManifest.xml | 2 ++ .../src/android/bluetooth/BluetoothStressTest.java | 11 +++++++++++ .../src/android/bluetooth/BluetoothTestUtils.java | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index 58f158ce410..60b6dc18f0c 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -19,6 +19,8 @@ + + diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java index abd7d9af224..755e7c4504c 100644 --- a/framework/tests/src/android/bluetooth/BluetoothStressTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -32,6 +32,8 @@ import android.test.InstrumentationTestCase; public class BluetoothStressTest extends InstrumentationTestCase { private static final String TAG = "BluetoothStressTest"; private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt"; + /** The amount of time to sleep between issuing start/stop SCO in ms. */ + private static final long SCO_SLEEP_TIME = 2 * 1000; private BluetoothTestUtils mTestUtils; @@ -380,11 +382,20 @@ public class BluetoothStressTest extends InstrumentationTestCase { for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations); mTestUtils.startSco(adapter, device); + sleep(SCO_SLEEP_TIME); mTestUtils.stopSco(adapter, device); + sleep(SCO_SLEEP_TIME); } mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); mTestUtils.unpair(adapter, device); mTestUtils.disable(adapter); } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 42e5cd17f2a..4858be8c2a8 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -1425,7 +1425,7 @@ public class BluetoothTestUtils extends Assert { } private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) { - String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED}; + String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED}; StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags); addReceiver(receiver, actions); return receiver; -- GitLab From 05180edf7371643b6a9c7643c888b12a461d1a01 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 28 Nov 2011 09:59:08 -0800 Subject: [PATCH 0205/1408] Cleanup references when turning BT off. Bug: 5572649 Change-Id: I62f9e0620832b69995d5c6e1c24634c9a3895a4b --- .../java/android/bluetooth/BluetoothA2dp.java | 4 +++ .../android/bluetooth/BluetoothAdapter.java | 26 ++++++++++++++--- .../BluetoothDeviceProfileState.java | 28 +++++++++++++++++-- .../android/bluetooth/BluetoothHeadset.java | 1 + .../android/bluetooth/BluetoothHealth.java | 4 +++ .../bluetooth/BluetoothInputDevice.java | 4 +++ .../java/android/bluetooth/BluetoothPan.java | 6 +++- .../java/android/bluetooth/BluetoothPbap.java | 3 +- 8 files changed, 67 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 96f3290160f..7300107dd51 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -129,6 +129,10 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d9716525653..5f5ba504133 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1180,11 +1180,29 @@ public final class BluetoothAdapter { * @param proxy Profile proxy object */ public void closeProfileProxy(int profile, BluetoothProfile proxy) { - if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = (BluetoothHeadset)proxy; - if (headset != null) { + if (proxy == null) return; + + switch (profile) { + case BluetoothProfile.HEADSET: + BluetoothHeadset headset = (BluetoothHeadset)proxy; headset.close(); - } + break; + case BluetoothProfile.A2DP: + BluetoothA2dp a2dp = (BluetoothA2dp)proxy; + a2dp.close(); + break; + case BluetoothProfile.INPUT_DEVICE: + BluetoothInputDevice iDev = (BluetoothInputDevice)proxy; + iDev.close(); + break; + case BluetoothProfile.PAN: + BluetoothPan pan = (BluetoothPan)proxy; + pan.close(); + break; + case BluetoothProfile.HEALTH: + BluetoothHealth health = (BluetoothHealth)proxy; + health.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index b1d007071df..c9603bf3ed5 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -109,6 +109,8 @@ public final class BluetoothDeviceProfileState extends StateMachine { private BluetoothA2dpService mA2dpService; private BluetoothHeadset mHeadsetService; private BluetoothPbap mPbapService; + private PbapServiceListener mPbap; + private BluetoothAdapter mAdapter; private boolean mPbapServiceConnected; private boolean mAutoConnectionPending; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; @@ -249,11 +251,11 @@ public final class BluetoothDeviceProfileState extends StateMachine { mContext.registerReceiver(mBroadcastReceiver, filter); - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); // TODO(): Convert PBAP to the new Profile APIs. - PbapServiceListener p = new PbapServiceListener(); + mPbap = new PbapServiceListener(); mIncomingConnections = mService.getIncomingState(address); mIncomingRejectTimer = readTimerValue(); @@ -414,6 +416,26 @@ public final class BluetoothDeviceProfileState extends StateMachine { case TRANSITION_TO_STABLE: // ignore. break; + case SM_QUIT_CMD: + mContext.unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); + mBluetoothProfileServiceListener = null; + mOutgoingHandsfree = null; + mPbap = null; + mPbapService.close(); + mPbapService = null; + mIncomingHid = null; + mOutgoingHid = null; + mIncomingHandsfree = null; + mOutgoingHandsfree = null; + mIncomingA2dp = null; + mOutgoingA2dp = null; + mBondedDevice = null; + // There is a problem in the State Machine code + // where things are not cleaned up properly, when quit message + // is handled so return NOT_HANDLED as a workaround. + return NOT_HANDLED; default: return NOT_HANDLED; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8f2b3d88c2e..2bbf008a769 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -245,6 +245,7 @@ public final class BluetoothHeadset implements BluetoothProfile { mContext.unbindService(mConnection); mConnection = null; } + mServiceListener = null; } /** diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 9b2b8cad20f..f850c0224a5 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -452,6 +452,10 @@ public final class BluetoothHealth implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 282b70a42c8..1a9e011ad7f 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -118,6 +118,10 @@ public final class BluetoothInputDevice implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 7490f9ee86b..5d9d8bea8e0 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -139,6 +139,10 @@ public final class BluetoothPan implements BluetoothProfile { } } + /*package*/ void close() { + mServiceListener = null; + } + /** * Initiate connection to a profile of the remote bluetooth device. * @@ -299,4 +303,4 @@ public final class BluetoothPan implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } -} \ No newline at end of file +} diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 4be077c0e7b..2683befeecd 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -69,7 +69,7 @@ public class BluetoothPbap { private IBluetoothPbap mService; private final Context mContext; - private final ServiceListener mServiceListener; + private ServiceListener mServiceListener; /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -138,6 +138,7 @@ public class BluetoothPbap { mContext.unbindService(mConnection); mConnection = null; } + mServiceListener = null; } /** -- GitLab From d3fca95609666ed243262f7476621538c929b99b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 7 Dec 2011 15:03:55 -0800 Subject: [PATCH 0206/1408] Add BluetoothAdapter.getRemoteDevice(byte[]) This is useful for NFC->BT hand-over, where we are already working with bytes. Change-Id: I2fff0b4fa0cefe9bfdf9a13f7ec6f557776a8a03 --- .../android/bluetooth/BluetoothAdapter.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5f5ba504133..e420bfd8c1b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -398,6 +398,25 @@ public final class BluetoothAdapter { return new BluetoothDevice(address); } + /** + * Get a {@link BluetoothDevice} object for the given Bluetooth hardware + * address. + *

    Valid Bluetooth hardware addresses must be 6 bytes. This method + * expects the address in network byte order (MSB first). + *

    A {@link BluetoothDevice} will always be returned for a valid + * hardware address, even if this adapter has never seen that device. + * + * @param address Bluetooth MAC address (6 bytes) + * @throws IllegalArgumentException if address is invalid + */ + public BluetoothDevice getRemoteDevice(byte[] address) { + if (address == null || address.length != 6) { + throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); + } + return new BluetoothDevice(String.format("%02X:%02X:%02X:%02X:%02X:%02X", + address[0], address[1], address[2], address[3], address[4], address[5])); + } + /** * Return true if Bluetooth is currently enabled and ready for use. *

    Equivalent to: @@ -1281,7 +1300,7 @@ public final class BluetoothAdapter { } /** - * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" *

    Alphabetic characters must be uppercase to be valid. * * @param address Bluetooth address as string -- GitLab From da4e2ab44781662822e2f8ee1f94f2085309d66d Mon Sep 17 00:00:00 2001 From: Joe Fernandez Date: Tue, 20 Dec 2011 10:38:34 -0800 Subject: [PATCH 0207/1408] docs: Add developer guide cross-references, Project ACRE, round 4 Change-Id: I1b43414aaec8ea217b39a0d780c80a25409d0991 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ++++++ framework/java/android/bluetooth/BluetoothDevice.java | 6 ++++++ framework/java/android/bluetooth/BluetoothServerSocket.java | 6 ++++++ framework/java/android/bluetooth/BluetoothSocket.java | 6 ++++++ framework/java/android/bluetooth/package.html | 5 +++-- 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5f5ba504133..899816c4e1a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -62,6 +62,12 @@ import java.util.UUID; * permission and some also require the * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * + *

    + *

    Developer Guides

    + *

    For more information about using Bluetooth, read the + * Bluetooth developer guide.

    + *
    + * * {@see BluetoothDevice} * {@see BluetoothServerSocket} */ diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 03065218ee9..189e8fc24a6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -54,6 +54,12 @@ import java.util.UUID; *

    Note: * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. * + *

    + *

    Developer Guides

    + *

    For more information about using Bluetooth, read the + * Bluetooth developer guide.

    + *
    + * * {@see BluetoothAdapter} * {@see BluetoothSocket} */ diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index acce182189d..4021f7b61af 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -55,6 +55,12 @@ import java.io.IOException; *

    Note: * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. * + *

    + *

    Developer Guides

    + *

    For more information about using Bluetooth, read the + * Bluetooth developer guide.

    + *
    + * * {@see BluetoothSocket} */ public final class BluetoothServerSocket implements Closeable { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 9a13c3eb070..19d13ef3455 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -65,6 +65,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; *

    Note: * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. * + *

    + *

    Developer Guides

    + *

    For more information about using Bluetooth, read the + * Bluetooth developer guide.

    + *
    + * * {@see BluetoothServerSocket} * {@see java.io.InputStream} * {@see java.io.OutputStream} diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 37505fd4047..81bf1cfe5d2 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -3,8 +3,8 @@

    Provides classes that manage Bluetooth functionality, such as scanning for devices, connecting with devices, and managing data transfer between devices.

    -

    For more information, see the Bluetooth developer guide.

    +

    For more information, see the +Bluetooth developer guide.

    {@more}

    The Bluetooth APIs let applications:

    @@ -26,5 +26,6 @@ permission.

    Note: Not all Android-powered devices provide Bluetooth functionality.

    + -- GitLab From a7dc5b5be59ea86f9c4272a884e2c53e448e1718 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 9 Mar 2012 19:56:35 +0900 Subject: [PATCH 0208/1408] Fix obvious typos under frameworks/base/core Change-Id: Ia5fc3db1bb51824e7523885553be926bcc42d736 --- framework/java/android/bluetooth/AtCommandResult.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothPbap.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java index 375a6ddba86..9675234dd07 100644 --- a/framework/java/android/bluetooth/AtCommandResult.java +++ b/framework/java/android/bluetooth/AtCommandResult.java @@ -17,7 +17,7 @@ package android.bluetooth; /** - * The result of execution of an single AT command.

    + * The result of execution of a single AT command.

    * * * This class can represent the final response to an AT command line, and also diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 189e8fc24a6..04af5f75ba7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -193,7 +193,7 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; /** - * Used as an Parcelable {@link BluetoothClass} extra field in {@link + * Used as a Parcelable {@link BluetoothClass} extra field in {@link * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. */ public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 2683befeecd..639ae1a96d5 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -60,7 +60,7 @@ public class BluetoothPbap { public static final String PBAP_PREVIOUS_STATE = "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; - /** Indicates the state of an pbap connection state has changed. + /** Indicates the state of a pbap connection state has changed. * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and * BluetoothIntent.ADDRESS extras. */ -- GitLab From befab9e8b5aa0b8c08005f18224a8c0e11a2b708 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 20 Apr 2012 19:28:00 -0700 Subject: [PATCH 0209/1408] Get alias for Bluetooth devices. Bluetooth devices can be renamed by the user. Make the input system aware of the user-specified name and transparently pass it down to applications. This enables the keyboard layout picker Settings UI to use device names that are consistent with what the user set in the Bluetooth UI. Bug: 6363157 Change-Id: I8eea26ce2c69c2a3f09c8de02e9e847610e0419c --- .../java/android/bluetooth/BluetoothDevice.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 04af5f75ba7..56e17354cca 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -159,6 +159,18 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"; + /** + * Broadcast Action: Indicates the alias of a remote device has been + * changed. + *

    Always contains the extra field {@link #EXTRA_DEVICE}. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ALIAS_CHANGED = + "android.bluetooth.device.action.ALIAS_CHANGED"; + /** * Broadcast Action: Indicates a change in the bond state of a remote * device. For example, if a device is bonded (paired). -- GitLab From a9110b8e7ebe9d6aef89ed9be1ccead99f87760b Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 18 Apr 2012 13:01:15 -0700 Subject: [PATCH 0210/1408] Allow enabling Bluetooth without auto-connecting. This is a feature used for NFC-to-Bluetooth handover: we want to enable BT for file transfer, and disconnect it when we're done. During this period we don't want to auto-connect other devices - it should be transparent to the user that Bluetooth is used. Also, don't allow A2DP/HSP incoming connections. Change-Id: I0a03e8084c439b1271b6a80f4d9da5aacfe19c45 --- .../java/android/bluetooth/BluetoothAdapter.java | 12 ++++++++++++ framework/java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 13 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 600ce6f71e1..8e3df472a3f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1231,6 +1231,18 @@ public final class BluetoothAdapter { } } + /** + * Enable the Bluetooth Adapter, but don't auto-connect devices + * and don't persist state. Only for use by system applications. + * @hide + */ + public boolean enableNoAutoConnect() { + try { + return mService.enableNoAutoConnect(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Enable control of the Bluetooth Adapter for a single application. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index deea2b80103..6075363e51b 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -34,6 +34,7 @@ interface IBluetooth boolean isEnabled(); int getBluetoothState(); boolean enable(); + boolean enableNoAutoConnect(); boolean disable(boolean persistSetting); String getAddress(); -- GitLab From f73ea4f505cc15fc5c2c32c21beb7cc485a0ba47 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 3 May 2012 19:46:01 -0700 Subject: [PATCH 0211/1408] Avoid calling BluetoothSocket.destroyNative twice If the destroyNative JNI method is called twice, it will attempt to free the socketData twice, and the second free will cause an assertion failure. To avoid this, check whether mSocketState has already been set to CLOSED while holding the write lock. If it has, then destroyNative has already been called and should not be called again. Bug: 3096033 Change-Id: Ic7c0bbf21823d389b26919da8c533d8ac4d49064 --- framework/java/android/bluetooth/BluetoothSocket.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 19d13ef3455..20e8515608a 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -242,8 +242,10 @@ public final class BluetoothSocket implements Closeable { // abortNative(), so this lock should immediately acquire mLock.writeLock().lock(); try { - mSocketState = SocketState.CLOSED; - destroyNative(); + if (mSocketState != SocketState.CLOSED) { + mSocketState = SocketState.CLOSED; + destroyNative(); + } } finally { mLock.writeLock().unlock(); } -- GitLab From 127e156cd936669d2b85099658a65d27b2a13f7f Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Tue, 29 May 2012 12:40:46 -0700 Subject: [PATCH 0212/1408] Enhance StateMachine Quitting and logging support. Make StateMachine#quit non-conditional and remove the need to process the SM_QUIT_CMD it is now private. Rename halting to onHalting. Add onQuitting Change the message specific logging to be more generic and change the xxxProcessedMessagesYyy methods to xxxLogRecXyy names. Also add addLogRec(String) and addLogRec(String, State) as the generic logging methods. bug: 5678189 Change-Id: I22f66d11828bfd70498db625fe1be728b90478b7 --- .../BluetoothDeviceProfileState.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index c9603bf3ed5..020f051b77b 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -304,6 +304,25 @@ public final class BluetoothDeviceProfileState extends StateMachine { } } + @Override + protected void onQuitting() { + mContext.unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); + mBluetoothProfileServiceListener = null; + mOutgoingHandsfree = null; + mPbap = null; + mPbapService.close(); + mPbapService = null; + mIncomingHid = null; + mOutgoingHid = null; + mIncomingHandsfree = null; + mOutgoingHandsfree = null; + mIncomingA2dp = null; + mOutgoingA2dp = null; + mBondedDevice = null; + } + private class BondedDevice extends State { @Override public void enter() { @@ -416,26 +435,6 @@ public final class BluetoothDeviceProfileState extends StateMachine { case TRANSITION_TO_STABLE: // ignore. break; - case SM_QUIT_CMD: - mContext.unregisterReceiver(mBroadcastReceiver); - mBroadcastReceiver = null; - mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); - mBluetoothProfileServiceListener = null; - mOutgoingHandsfree = null; - mPbap = null; - mPbapService.close(); - mPbapService = null; - mIncomingHid = null; - mOutgoingHid = null; - mIncomingHandsfree = null; - mOutgoingHandsfree = null; - mIncomingA2dp = null; - mOutgoingA2dp = null; - mBondedDevice = null; - // There is a problem in the State Machine code - // where things are not cleaned up properly, when quit message - // is handled so return NOT_HANDLED as a workaround. - return NOT_HANDLED; default: return NOT_HANDLED; } @@ -1341,6 +1340,15 @@ public final class BluetoothDeviceProfileState extends StateMachine { return mDevice; } + /** + * Quit the state machine, only to be called by BluetoothService. + * + * @hide + */ + public void doQuit() { + this.quit(); + } + private void log(String message) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); -- GitLab From dfa230e9e220dce132f2bbf39e5566c0b91a7c7b Mon Sep 17 00:00:00 2001 From: Scott Main Date: Fri, 22 Jun 2012 12:35:08 -0700 Subject: [PATCH 0213/1408] docs: fix several links Change-Id: I89d9fd64dc22c90680bb05415cc966c255165af9 --- framework/java/android/bluetooth/package.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 81bf1cfe5d2..ba75034555e 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -4,7 +4,7 @@ devices, connecting with devices, and managing data transfer between devices.

    For more information, see the -Bluetooth developer guide.

    +Bluetooth guide.

    {@more}

    The Bluetooth APIs let applications:

    -- GitLab From 1c7b9babf1ccd1dd132ffecf3bc7abfdf353a1dc Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Tue, 29 May 2012 12:40:46 -0700 Subject: [PATCH 0214/1408] Enhance StateMachine Quitting and logging support. DO NOT MERGE Make StateMachine#quit non-conditional and remove the need to process the SM_QUIT_CMD it is now private. Rename halting to onHalting. Add onQuitting Change the message specific logging to be more generic and change the xxxProcessedMessagesYyy methods to xxxLogRecXyy names. Also add addLogRec(String) and addLogRec(String, State) as the generic logging methods. bug: 5678189 Change-Id: I22f66d11828bfd70498db625fe1be728b90478b7 Conflicts: services/java/com/android/server/NsdService.java --- .../BluetoothDeviceProfileState.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java index c9603bf3ed5..020f051b77b 100644 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -304,6 +304,25 @@ public final class BluetoothDeviceProfileState extends StateMachine { } } + @Override + protected void onQuitting() { + mContext.unregisterReceiver(mBroadcastReceiver); + mBroadcastReceiver = null; + mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); + mBluetoothProfileServiceListener = null; + mOutgoingHandsfree = null; + mPbap = null; + mPbapService.close(); + mPbapService = null; + mIncomingHid = null; + mOutgoingHid = null; + mIncomingHandsfree = null; + mOutgoingHandsfree = null; + mIncomingA2dp = null; + mOutgoingA2dp = null; + mBondedDevice = null; + } + private class BondedDevice extends State { @Override public void enter() { @@ -416,26 +435,6 @@ public final class BluetoothDeviceProfileState extends StateMachine { case TRANSITION_TO_STABLE: // ignore. break; - case SM_QUIT_CMD: - mContext.unregisterReceiver(mBroadcastReceiver); - mBroadcastReceiver = null; - mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); - mBluetoothProfileServiceListener = null; - mOutgoingHandsfree = null; - mPbap = null; - mPbapService.close(); - mPbapService = null; - mIncomingHid = null; - mOutgoingHid = null; - mIncomingHandsfree = null; - mOutgoingHandsfree = null; - mIncomingA2dp = null; - mOutgoingA2dp = null; - mBondedDevice = null; - // There is a problem in the State Machine code - // where things are not cleaned up properly, when quit message - // is handled so return NOT_HANDLED as a workaround. - return NOT_HANDLED; default: return NOT_HANDLED; } @@ -1341,6 +1340,15 @@ public final class BluetoothDeviceProfileState extends StateMachine { return mDevice; } + /** + * Quit the state machine, only to be called by BluetoothService. + * + * @hide + */ + public void doQuit() { + this.quit(); + } + private void log(String message) { if (DBG) { Log.i(TAG, "Device:" + mDevice + " Message:" + message); -- GitLab From 6da63327768636eea96a996e16176fd7833dce2a Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 17 Jan 2012 15:24:01 -0800 Subject: [PATCH 0215/1408] Delete various Bluetooth files for stack integration. --- .../android/bluetooth/AtCommandHandler.java | 94 -- .../android/bluetooth/AtCommandResult.java | 115 -- .../java/android/bluetooth/AtParser.java | 367 ----- .../java/android/bluetooth/BluetoothA2dp.java | 4 +- .../BluetoothDeviceProfileState.java | 1357 ----------------- .../bluetooth/BluetoothProfileState.java | 160 -- .../java/android/bluetooth/HeadsetBase.java | 298 ---- 7 files changed, 2 insertions(+), 2393 deletions(-) delete mode 100644 framework/java/android/bluetooth/AtCommandHandler.java delete mode 100644 framework/java/android/bluetooth/AtCommandResult.java delete mode 100644 framework/java/android/bluetooth/AtParser.java delete mode 100644 framework/java/android/bluetooth/BluetoothDeviceProfileState.java delete mode 100644 framework/java/android/bluetooth/BluetoothProfileState.java delete mode 100644 framework/java/android/bluetooth/HeadsetBase.java diff --git a/framework/java/android/bluetooth/AtCommandHandler.java b/framework/java/android/bluetooth/AtCommandHandler.java deleted file mode 100644 index 6deab34ed48..00000000000 --- a/framework/java/android/bluetooth/AtCommandHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2007 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.bluetooth.AtCommandResult; - -/** - * Handler Interface for {@link AtParser}.

    - * @hide - */ -public abstract class AtCommandHandler { - - /** - * Handle Basic commands "ATA".

    - * These are single letter commands such as ATA and ATD. Anything following - * the single letter command ('A' and 'D' respectively) will be passed as - * 'arg'.

    - * For example, "ATDT1234" would result in the call - * handleBasicCommand("T1234").

    - * @param arg Everything following the basic command character. - * @return The result of this command. - */ - public AtCommandResult handleBasicCommand(String arg) { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Actions command "AT+FOO".

    - * Action commands are part of the Extended command syntax, and are - * typically used to signal an action on "FOO".

    - * @return The result of this command. - */ - public AtCommandResult handleActionCommand() { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Read command "AT+FOO?".

    - * Read commands are part of the Extended command syntax, and are - * typically used to read the value of "FOO".

    - * @return The result of this command. - */ - public AtCommandResult handleReadCommand() { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Set command "AT+FOO=...".

    - * Set commands are part of the Extended command syntax, and are - * typically used to set the value of "FOO". Multiple arguments can be - * sent.

    - * AT+FOO=[[,[,...]]]

    - * Each argument will be either numeric (Integer) or String. - * handleSetCommand is passed a generic Object[] array in which each - * element will be an Integer (if it can be parsed with parseInt()) or - * String.

    - * Missing arguments ",," are set to empty Strings.

    - * @param args Array of String and/or Integer's. There will always be at - * least one element in this array. - * @return The result of this command. - */ - // Typically used to set this parameter - public AtCommandResult handleSetCommand(Object[] args) { - return new AtCommandResult(AtCommandResult.ERROR); - } - - /** - * Handle Test command "AT+FOO=?".

    - * Test commands are part of the Extended command syntax, and are typically - * used to request an indication of the range of legal values that "FOO" - * can take.

    - * By default we return an OK result, to indicate that this command is at - * least recognized.

    - * @return The result of this command. - */ - public AtCommandResult handleTestCommand() { - return new AtCommandResult(AtCommandResult.OK); - } - -} diff --git a/framework/java/android/bluetooth/AtCommandResult.java b/framework/java/android/bluetooth/AtCommandResult.java deleted file mode 100644 index 9675234dd07..00000000000 --- a/framework/java/android/bluetooth/AtCommandResult.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2007 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; - -/** - * The result of execution of a single AT command.

    - * - * - * This class can represent the final response to an AT command line, and also - * intermediate responses to a single command within a chained AT command - * line.

    - * - * The actual responses that are intended to be send in reply to the AT command - * line are stored in a string array. The final response is stored as an - * int enum, converted to a string when toString() is called. Only a single - * final response is sent from multiple commands chained into a single command - * line.

    - * @hide - */ -public class AtCommandResult { - // Result code enumerations - public static final int OK = 0; - public static final int ERROR = 1; - public static final int UNSOLICITED = 2; - - private static final String OK_STRING = "OK"; - private static final String ERROR_STRING = "ERROR"; - - private int mResultCode; // Result code - private StringBuilder mResponse; // Response with CRLF line breaks - - /** - * Construct a new AtCommandResult with given result code, and an empty - * response array. - * @param resultCode One of OK, ERROR or UNSOLICITED. - */ - public AtCommandResult(int resultCode) { - mResultCode = resultCode; - mResponse = new StringBuilder(); - } - - /** - * Construct a new AtCommandResult with result code OK, and the specified - * single line response. - * @param response The single line response. - */ - public AtCommandResult(String response) { - this(OK); - addResponse(response); - } - - public int getResultCode() { - return mResultCode; - } - - /** - * Add another line to the response. - */ - public void addResponse(String response) { - appendWithCrlf(mResponse, response); - } - - /** - * Add the given result into this AtCommandResult object.

    - * Used to combine results from multiple commands in a single command line - * (command chaining). - * @param result The AtCommandResult to add to this result. - */ - public void addResult(AtCommandResult result) { - if (result != null) { - appendWithCrlf(mResponse, result.mResponse.toString()); - mResultCode = result.mResultCode; - } - } - - /** - * Generate the string response ready to send - */ - public String toString() { - StringBuilder result = new StringBuilder(mResponse.toString()); - switch (mResultCode) { - case OK: - appendWithCrlf(result, OK_STRING); - break; - case ERROR: - appendWithCrlf(result, ERROR_STRING); - break; - } - return result.toString(); - } - - /** Append a string to a string builder, joining with a double - * CRLF. Used to create multi-line AT command replies - */ - public static void appendWithCrlf(StringBuilder str1, String str2) { - if (str1.length() > 0 && str2.length() > 0) { - str1.append("\r\n\r\n"); - } - str1.append(str2); - } -}; diff --git a/framework/java/android/bluetooth/AtParser.java b/framework/java/android/bluetooth/AtParser.java deleted file mode 100644 index 328fb2bbd59..00000000000 --- a/framework/java/android/bluetooth/AtParser.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2007 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 java.util.*; - -/** - * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard. - *

    - * - * Conformant with the subset of V.250 required for implementation of the - * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP - * specifications. Also implements some V.250 features not required by - * Bluetooth - such as chained commands.

    - * - * Command handlers are registered with an AtParser object. These handlers are - * invoked when command lines are processed by AtParser's process() method.

    - * - * The AtParser object accepts a new command line to parse via its process() - * method. It breaks each command line into one or more commands. Each command - * is parsed for name, type, and (optional) arguments, and an appropriate - * external handler method is called through the AtCommandHandler interface. - * - * The command types are

      - *
    • Basic Command. For example "ATDT1234567890". Basic command names are a - * single character (e.g. "D"), and everything following this character is - * passed to the handler as a string argument (e.g. "T1234567890"). - *
    • Action Command. For example "AT+CIMI". The command name is "CIMI", and - * there are no arguments for action commands. - *
    • Read Command. For example "AT+VGM?". The command name is "VGM", and there - * are no arguments for get commands. - *
    • Set Command. For example "AT+VGM=14". The command name is "VGM", and - * there is a single integer argument in this case. In the general case then - * can be zero or more arguments (comma delimited) each of integer or string - * form. - *
    • Test Command. For example "AT+VGM=?. No arguments. - *
    - * - * In V.250 the last four command types are known as Extended Commands, and - * they are used heavily in Bluetooth.

    - * - * Basic commands cannot be chained in this implementation. For Bluetooth - * headset/handsfree use this is acceptable, because they only use the basic - * commands ATA and ATD, which are not allowed to be chained. For general V.250 - * use we would need to improve this class to allow Basic command chaining - - * however it's tricky to get right because there is no delimiter for Basic - * command chaining.

    - * - * Extended commands can be chained. For example:

    - * AT+VGM?;+VGM=14;+CIMI

    - * This is equivalent to:

    - * AT+VGM? - * AT+VGM=14 - * AT+CIMI - * Except that only one final result code is return (although several - * intermediate responses may be returned), and as soon as one command in the - * chain fails the rest are abandoned.

    - * - * Handlers are registered by there command name via register(Char c, ...) or - * register(String s, ...). Handlers for Basic command should be registered by - * the basic command character, and handlers for Extended commands should be - * registered by String.

    - * - * Refer to:

      - *
    • ITU-T Recommendation V.250 - *
    • ETSI TS 127.007 (AT Command set for User Equipment, 3GPP TS 27.007) - *
    • Bluetooth Headset Profile Spec (K6) - *
    • Bluetooth Handsfree Profile Spec (HFP 1.5) - *
    - * @hide - */ -public class AtParser { - - // Extended command type enumeration, only used internally - private static final int TYPE_ACTION = 0; // AT+FOO - private static final int TYPE_READ = 1; // AT+FOO? - private static final int TYPE_SET = 2; // AT+FOO= - private static final int TYPE_TEST = 3; // AT+FOO=? - - private HashMap mExtHandlers; - private HashMap mBasicHandlers; - - private String mLastInput; // for "A/" (repeat last command) support - - /** - * Create a new AtParser.

    - * No handlers are registered. - */ - public AtParser() { - mBasicHandlers = new HashMap(); - mExtHandlers = new HashMap(); - mLastInput = ""; - } - - /** - * Register a Basic command handler.

    - * Basic command handlers are later called via their - * handleBasicCommand(String args) method. - * @param command Command name - a single character - * @param handler Handler to register - */ - public void register(Character command, AtCommandHandler handler) { - mBasicHandlers.put(command, handler); - } - - /** - * Register an Extended command handler.

    - * Extended command handlers are later called via:

      - *
    • handleActionCommand() - *
    • handleGetCommand() - *
    • handleSetCommand() - *
    • handleTestCommand() - *
    - * Only one method will be called for each command processed. - * @param command Command name - can be multiple characters - * @param handler Handler to register - */ - public void register(String command, AtCommandHandler handler) { - mExtHandlers.put(command, handler); - } - - - /** - * Strip input of whitespace and force Uppercase - except sections inside - * quotes. Also fixes unmatched quotes (by appending a quote). Double - * quotes " are the only quotes allowed by V.250 - */ - static private String clean(String input) { - StringBuilder out = new StringBuilder(input.length()); - - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '"') { - int j = input.indexOf('"', i + 1 ); // search for closing " - if (j == -1) { // unmatched ", insert one. - out.append(input.substring(i, input.length())); - out.append('"'); - break; - } - out.append(input.substring(i, j + 1)); - i = j; - } else if (c != ' ') { - out.append(Character.toUpperCase(c)); - } - } - - return out.toString(); - } - - static private boolean isAtoZ(char c) { - return (c >= 'A' && c <= 'Z'); - } - - /** - * Find a character ch, ignoring quoted sections. - * Return input.length() if not found. - */ - static private int findChar(char ch, String input, int fromIndex) { - for (int i = fromIndex; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '"') { - i = input.indexOf('"', i + 1); - if (i == -1) { - return input.length(); - } - } else if (c == ch) { - return i; - } - } - return input.length(); - } - - /** - * Break an argument string into individual arguments (comma delimited). - * Integer arguments are turned into Integer objects. Otherwise a String - * object is used. - */ - static private Object[] generateArgs(String input) { - int i = 0; - int j; - ArrayList out = new ArrayList(); - while (i <= input.length()) { - j = findChar(',', input, i); - - String arg = input.substring(i, j); - try { - out.add(new Integer(arg)); - } catch (NumberFormatException e) { - out.add(arg); - } - - i = j + 1; // move past comma - } - return out.toArray(); - } - - /** - * Return the index of the end of character after the last character in - * the extended command name. Uses the V.250 spec for allowed command - * names. - */ - static private int findEndExtendedName(String input, int index) { - for (int i = index; i < input.length(); i++) { - char c = input.charAt(i); - - // V.250 defines the following chars as legal extended command - // names - if (isAtoZ(c)) continue; - if (c >= '0' && c <= '9') continue; - switch (c) { - case '!': - case '%': - case '-': - case '.': - case '/': - case ':': - case '_': - continue; - default: - return i; - } - } - return input.length(); - } - - /** - * Processes an incoming AT command line.

    - * This method will invoke zero or one command handler methods for each - * command in the command line.

    - * @param raw_input The AT input, without EOL delimiter (e.g. ). - * @return Result object for this command line. This can be - * converted to a String[] response with toStrings(). - */ - public AtCommandResult process(String raw_input) { - String input = clean(raw_input); - - // Handle "A/" (repeat previous line) - if (input.regionMatches(0, "A/", 0, 2)) { - input = new String(mLastInput); - } else { - mLastInput = new String(input); - } - - // Handle empty line - no response necessary - if (input.equals("")) { - // Return [] - return new AtCommandResult(AtCommandResult.UNSOLICITED); - } - - // Anything else deserves an error - if (!input.regionMatches(0, "AT", 0, 2)) { - // Return ["ERROR"] - return new AtCommandResult(AtCommandResult.ERROR); - } - - // Ok we have a command that starts with AT. Process it - int index = 2; - AtCommandResult result = - new AtCommandResult(AtCommandResult.UNSOLICITED); - while (index < input.length()) { - char c = input.charAt(index); - - if (isAtoZ(c)) { - // Option 1: Basic Command - // Pass the rest of the line as is to the handler. Do not - // look for any more commands on this line. - String args = input.substring(index + 1); - if (mBasicHandlers.containsKey((Character)c)) { - result.addResult(mBasicHandlers.get( - (Character)c).handleBasicCommand(args)); - return result; - } else { - // no handler - result.addResult( - new AtCommandResult(AtCommandResult.ERROR)); - return result; - } - // control never reaches here - } - - if (c == '+') { - // Option 2: Extended Command - // Search for first non-name character. Short-circuit if - // we don't handle this command name. - int i = findEndExtendedName(input, index + 1); - String commandName = input.substring(index, i); - if (!mExtHandlers.containsKey(commandName)) { - // no handler - result.addResult( - new AtCommandResult(AtCommandResult.ERROR)); - return result; - } - AtCommandHandler handler = mExtHandlers.get(commandName); - - // Search for end of this command - this is usually the end of - // line - int endIndex = findChar(';', input, index); - - // Determine what type of command this is. - // Default to TYPE_ACTION if we can't find anything else - // obvious. - int type; - - if (i >= endIndex) { - type = TYPE_ACTION; - } else if (input.charAt(i) == '?') { - type = TYPE_READ; - } else if (input.charAt(i) == '=') { - if (i + 1 < endIndex) { - if (input.charAt(i + 1) == '?') { - type = TYPE_TEST; - } else { - type = TYPE_SET; - } - } else { - type = TYPE_SET; - } - } else { - type = TYPE_ACTION; - } - - // Call this command. Short-circuit as soon as a command fails - switch (type) { - case TYPE_ACTION: - result.addResult(handler.handleActionCommand()); - break; - case TYPE_READ: - result.addResult(handler.handleReadCommand()); - break; - case TYPE_TEST: - result.addResult(handler.handleTestCommand()); - break; - case TYPE_SET: - Object[] args = - generateArgs(input.substring(i + 1, endIndex)); - result.addResult(handler.handleSetCommand(args)); - break; - } - if (result.getResultCode() != AtCommandResult.OK) { - return result; // short-circuit - } - - index = endIndex; - } else { - // Can't tell if this is a basic or extended command. - // Push forwards and hope we hit something. - index++; - } - } - // Finished processing (and all results were ok) - return result; - } -} diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7300107dd51..c8e60f1504d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -23,7 +23,6 @@ import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; -import android.server.BluetoothA2dpService; import android.util.Log; import java.util.ArrayList; @@ -112,7 +111,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * */ /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) { - IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE); + //TODO(BT): Fix this + IBinder b = null; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); if (b != null) { diff --git a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java b/framework/java/android/bluetooth/BluetoothDeviceProfileState.java deleted file mode 100644 index 020f051b77b..00000000000 --- a/framework/java/android/bluetooth/BluetoothDeviceProfileState.java +++ /dev/null @@ -1,1357 +0,0 @@ -/* - * Copyright (C) 2010 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.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Message; -import android.bluetooth.BluetoothAdapter; -import android.os.PowerManager; -import android.server.BluetoothA2dpService; -import android.server.BluetoothService; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import java.util.Set; - -/** - * This class is the Profile connection state machine associated with a remote - * device. When the device bonds an instance of this class is created. - * This tracks incoming and outgoing connections of all the profiles. Incoming - * connections are preferred over outgoing connections and HFP preferred over - * A2DP. When the device is unbonded, the instance is removed. - * - * States: - * {@link BondedDevice}: This state represents a bonded device. When in this - * state none of the profiles are in transition states. - * - * {@link OutgoingHandsfree}: Handsfree profile connection is in a transition - * state because of a outgoing Connect or Disconnect. - * - * {@link IncomingHandsfree}: Handsfree profile connection is in a transition - * state because of a incoming Connect or Disconnect. - * - * {@link IncomingA2dp}: A2dp profile connection is in a transition - * state because of a incoming Connect or Disconnect. - * - * {@link OutgoingA2dp}: A2dp profile connection is in a transition - * state because of a outgoing Connect or Disconnect. - * - * Todo(): Write tests for this class, when the Android Mock support is completed. - * @hide - */ -public final class BluetoothDeviceProfileState extends StateMachine { - private static final String TAG = "BluetoothDeviceProfileState"; - private static final boolean DBG = false; - - // TODO(): Restructure the state machine to make it scalable with regard to profiles. - public static final int CONNECT_HFP_OUTGOING = 1; - public static final int CONNECT_HFP_INCOMING = 2; - public static final int CONNECT_A2DP_OUTGOING = 3; - public static final int CONNECT_A2DP_INCOMING = 4; - public static final int CONNECT_HID_OUTGOING = 5; - public static final int CONNECT_HID_INCOMING = 6; - - public static final int DISCONNECT_HFP_OUTGOING = 50; - private static final int DISCONNECT_HFP_INCOMING = 51; - public static final int DISCONNECT_A2DP_OUTGOING = 52; - public static final int DISCONNECT_A2DP_INCOMING = 53; - public static final int DISCONNECT_HID_OUTGOING = 54; - public static final int DISCONNECT_HID_INCOMING = 55; - public static final int DISCONNECT_PBAP_OUTGOING = 56; - - public static final int UNPAIR = 100; - public static final int AUTO_CONNECT_PROFILES = 101; - public static final int TRANSITION_TO_STABLE = 102; - public static final int CONNECT_OTHER_PROFILES = 103; - private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; - private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; - - public static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs - private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs - private static final int CONNECTION_ACCESS_UNDEFINED = -1; - private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec - private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours - - private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; - private static final String ACCESS_AUTHORITY_CLASS = - "com.android.settings.bluetooth.BluetoothPermissionRequest"; - - private BondedDevice mBondedDevice = new BondedDevice(); - private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree(); - private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree(); - private IncomingA2dp mIncomingA2dp = new IncomingA2dp(); - private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp(); - private OutgoingHid mOutgoingHid = new OutgoingHid(); - private IncomingHid mIncomingHid = new IncomingHid(); - - private Context mContext; - private BluetoothService mService; - private BluetoothA2dpService mA2dpService; - private BluetoothHeadset mHeadsetService; - private BluetoothPbap mPbapService; - private PbapServiceListener mPbap; - private BluetoothAdapter mAdapter; - private boolean mPbapServiceConnected; - private boolean mAutoConnectionPending; - private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; - - private BluetoothDevice mDevice; - private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED; - private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED; - private long mIncomingRejectTimer; - private boolean mConnectionAccessReplyReceived = false; - private Pair mIncomingConnections; - private PowerManager.WakeLock mWakeLock; - private PowerManager mPowerManager; - private boolean mPairingRequestRcvd = false; - - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (device == null || !device.equals(mDevice)) return; - - if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - // We trust this device now - if (newState == BluetoothHeadset.STATE_CONNECTED) { - setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); - } - mA2dpState = newState; - if (oldState == BluetoothA2dp.STATE_CONNECTED && - newState == BluetoothA2dp.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_A2DP_INCOMING); - } - if (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - // We trust this device now - if (newState == BluetoothHeadset.STATE_CONNECTED) { - setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); - } - mHeadsetState = newState; - if (oldState == BluetoothHeadset.STATE_CONNECTED && - newState == BluetoothHeadset.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_HFP_INCOMING); - } - if (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - int oldState = - intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0); - // We trust this device now - if (newState == BluetoothHeadset.STATE_CONNECTED) { - setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); - } - if (oldState == BluetoothProfile.STATE_CONNECTED && - newState == BluetoothProfile.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_HID_INCOMING); - } - if (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { - // This is technically not needed, but we can get stuck sometimes. - // For example, if incoming A2DP fails, we are not informed by Bluez - sendMessage(TRANSITION_TO_STABLE); - } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { - mWakeLock.release(); - int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, - BluetoothDevice.CONNECTION_ACCESS_NO); - Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY); - msg.arg1 = val; - sendMessage(msg); - } else if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) { - mPairingRequestRcvd = true; - } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { - int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, - BluetoothDevice.ERROR); - if (state == BluetoothDevice.BOND_BONDED && mPairingRequestRcvd) { - setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); - mPairingRequestRcvd = false; - } else if (state == BluetoothDevice.BOND_NONE) { - mPairingRequestRcvd = false; - } - } - } - }; - - private boolean isPhoneDocked(BluetoothDevice autoConnectDevice) { - // This works only because these broadcast intents are "sticky" - Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); - if (i != null) { - int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); - if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) { - BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (device != null && autoConnectDevice.equals(device)) { - return true; - } - } - } - return false; - } - - public BluetoothDeviceProfileState(Context context, String address, - BluetoothService service, BluetoothA2dpService a2dpService, boolean setTrust) { - super(address); - mContext = context; - mDevice = new BluetoothDevice(address); - mService = service; - mA2dpService = a2dpService; - - addState(mBondedDevice); - addState(mOutgoingHandsfree); - addState(mIncomingHandsfree); - addState(mIncomingA2dp); - addState(mOutgoingA2dp); - addState(mOutgoingHid); - addState(mIncomingHid); - setInitialState(mBondedDevice); - - IntentFilter filter = new IntentFilter(); - // Fine-grained state broadcasts - filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); - filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); - filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); - filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST); - filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - - mContext.registerReceiver(mBroadcastReceiver, filter); - - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mAdapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, - BluetoothProfile.HEADSET); - // TODO(): Convert PBAP to the new Profile APIs. - mPbap = new PbapServiceListener(); - - mIncomingConnections = mService.getIncomingState(address); - mIncomingRejectTimer = readTimerValue(); - mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | - PowerManager.ACQUIRE_CAUSES_WAKEUP | - PowerManager.ON_AFTER_RELEASE, TAG); - mWakeLock.setReferenceCounted(false); - - if (setTrust) { - setTrust(BluetoothDevice.CONNECTION_ACCESS_YES); - } - } - - private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = - new BluetoothProfile.ServiceListener() { - public void onServiceConnected(int profile, BluetoothProfile proxy) { - synchronized(BluetoothDeviceProfileState.this) { - mHeadsetService = (BluetoothHeadset) proxy; - if (mAutoConnectionPending) { - sendMessage(AUTO_CONNECT_PROFILES); - mAutoConnectionPending = false; - } - } - } - public void onServiceDisconnected(int profile) { - synchronized(BluetoothDeviceProfileState.this) { - mHeadsetService = null; - } - } - }; - - private class PbapServiceListener implements BluetoothPbap.ServiceListener { - public PbapServiceListener() { - mPbapService = new BluetoothPbap(mContext, this); - } - public void onServiceConnected() { - synchronized(BluetoothDeviceProfileState.this) { - mPbapServiceConnected = true; - } - } - public void onServiceDisconnected() { - synchronized(BluetoothDeviceProfileState.this) { - mPbapServiceConnected = false; - } - } - } - - @Override - protected void onQuitting() { - mContext.unregisterReceiver(mBroadcastReceiver); - mBroadcastReceiver = null; - mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mHeadsetService); - mBluetoothProfileServiceListener = null; - mOutgoingHandsfree = null; - mPbap = null; - mPbapService.close(); - mPbapService = null; - mIncomingHid = null; - mOutgoingHid = null; - mIncomingHandsfree = null; - mOutgoingHandsfree = null; - mIncomingA2dp = null; - mOutgoingA2dp = null; - mBondedDevice = null; - } - - private class BondedDevice extends State { - @Override - public void enter() { - Log.i(TAG, "Entering ACL Connected state with: " + getCurrentMessage().what); - Message m = new Message(); - m.copyFrom(getCurrentMessage()); - sendMessageAtFrontOfQueue(m); - } - @Override - public boolean processMessage(Message message) { - log("ACL Connected State -> Processing Message: " + message.what); - switch(message.what) { - case CONNECT_HFP_OUTGOING: - case DISCONNECT_HFP_OUTGOING: - transitionTo(mOutgoingHandsfree); - break; - case CONNECT_HFP_INCOMING: - transitionTo(mIncomingHandsfree); - break; - case DISCONNECT_HFP_INCOMING: - transitionTo(mIncomingHandsfree); - break; - case CONNECT_A2DP_OUTGOING: - case DISCONNECT_A2DP_OUTGOING: - transitionTo(mOutgoingA2dp); - break; - case CONNECT_A2DP_INCOMING: - case DISCONNECT_A2DP_INCOMING: - transitionTo(mIncomingA2dp); - break; - case CONNECT_HID_OUTGOING: - case DISCONNECT_HID_OUTGOING: - transitionTo(mOutgoingHid); - break; - case CONNECT_HID_INCOMING: - case DISCONNECT_HID_INCOMING: - transitionTo(mIncomingHid); - break; - case DISCONNECT_PBAP_OUTGOING: - processCommand(DISCONNECT_PBAP_OUTGOING); - break; - case UNPAIR: - if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_HFP_OUTGOING); - deferMessage(message); - break; - } else if (mA2dpState != BluetoothA2dp.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_A2DP_OUTGOING); - deferMessage(message); - break; - } else if (mService.getInputDeviceConnectionState(mDevice) != - BluetoothInputDevice.STATE_DISCONNECTED) { - sendMessage(DISCONNECT_HID_OUTGOING); - deferMessage(message); - break; - } - processCommand(UNPAIR); - break; - case AUTO_CONNECT_PROFILES: - if (isPhoneDocked(mDevice)) { - // Don't auto connect to docks. - break; - } else { - if (mHeadsetService == null) { - mAutoConnectionPending = true; - } else if (mHeadsetService.getPriority(mDevice) == - BluetoothHeadset.PRIORITY_AUTO_CONNECT && - mHeadsetService.getDevicesMatchingConnectionStates( - new int[] {BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}).size() == 0) { - mHeadsetService.connect(mDevice); - } - if (mA2dpService != null && - mA2dpService.getPriority(mDevice) == - BluetoothA2dp.PRIORITY_AUTO_CONNECT && - mA2dpService.getDevicesMatchingConnectionStates( - new int[] {BluetoothA2dp.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}).size() == 0) { - mA2dpService.connect(mDevice); - } - if (mService.getInputDevicePriority(mDevice) == - BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { - mService.connectInputDevice(mDevice); - } - } - break; - case CONNECT_OTHER_PROFILES: - if (isPhoneDocked(mDevice)) { - break; - } - if (message.arg1 == CONNECT_A2DP_OUTGOING) { - if (mA2dpService != null && - mA2dpService.getConnectedDevices().size() == 0) { - Log.i(TAG, "A2dp:Connect Other Profiles"); - mA2dpService.connect(mDevice); - } - } else if (message.arg1 == CONNECT_HFP_OUTGOING) { - if (mHeadsetService == null) { - deferMessage(message); - } else { - if (mHeadsetService.getConnectedDevices().size() == 0) { - Log.i(TAG, "Headset:Connect Other Profiles"); - mHeadsetService.connect(mDevice); - } - } - } - break; - case TRANSITION_TO_STABLE: - // ignore. - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class OutgoingHandsfree extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - Log.i(TAG, "Entering OutgoingHandsfree state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_HFP_OUTGOING && - mCommand != DISCONNECT_HFP_OUTGOING) { - Log.e(TAG, "Error: OutgoingHandsfree state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) { - sendMessage(TRANSITION_TO_STABLE); - mService.sendProfileStateMessage(BluetoothProfileState.HFP, - BluetoothProfileState.TRANSITION_TO_STABLE); - } - } - - @Override - public boolean processMessage(Message message) { - log("OutgoingHandsfree State -> Processing Message: " + message.what); - Message deferMsg = new Message(); - int command = message.what; - switch(command) { - case CONNECT_HFP_OUTGOING: - if (command != mCommand) { - // Disconnect followed by a connect - defer - deferMessage(message); - } - break; - case CONNECT_HFP_INCOMING: - if (mCommand == CONNECT_HFP_OUTGOING) { - // Cancel outgoing connect, accept incoming - cancelCommand(CONNECT_HFP_OUTGOING); - transitionTo(mIncomingHandsfree); - } else { - // We have done the disconnect but we are not - // sure which state we are in at this point. - deferMessage(message); - } - break; - case CONNECT_A2DP_INCOMING: - // accept incoming A2DP, retry HFP_OUTGOING - transitionTo(mIncomingA2dp); - - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case CONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_HFP_OUTGOING: - if (mCommand == CONNECT_HFP_OUTGOING) { - // Cancel outgoing connect - cancelCommand(CONNECT_HFP_OUTGOING); - processCommand(DISCONNECT_HFP_OUTGOING); - } - // else ignore - break; - case DISCONNECT_HFP_INCOMING: - // When this happens the socket would be closed and the headset - // state moved to DISCONNECTED, cancel the outgoing thread. - // if it still is in CONNECTING state - cancelCommand(CONNECT_HFP_OUTGOING); - break; - case DISCONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_A2DP_INCOMING: - // Bluez will handle the disconnect. If because of this the outgoing - // handsfree connection has failed, then retry. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case CONNECT_HID_OUTGOING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - case CONNECT_HID_INCOMING: - transitionTo(mIncomingHid); - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case DISCONNECT_HID_INCOMING: - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; // ignore - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - case CONNECT_OTHER_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class IncomingHandsfree extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - Log.i(TAG, "Entering IncomingHandsfree state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_HFP_INCOMING && - mCommand != DISCONNECT_HFP_INCOMING) { - Log.e(TAG, "Error: IncomingHandsfree state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) { - sendMessage(TRANSITION_TO_STABLE); - mService.sendProfileStateMessage(BluetoothProfileState.HFP, - BluetoothProfileState.TRANSITION_TO_STABLE); - } - } - - @Override - public boolean processMessage(Message message) { - log("IncomingHandsfree State -> Processing Message: " + message.what); - switch(message.what) { - case CONNECT_HFP_OUTGOING: - deferMessage(message); - break; - case CONNECT_HFP_INCOMING: - // Ignore - Log.e(TAG, "Error: Incoming connection with a pending incoming connection"); - break; - case CONNECTION_ACCESS_REQUEST_REPLY: - int val = message.arg1; - mConnectionAccessReplyReceived = true; - boolean value = false; - if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { - value = true; - } - setTrust(val); - - handleIncomingConnection(CONNECT_HFP_INCOMING, value); - break; - case CONNECTION_ACCESS_REQUEST_EXPIRY: - if (!mConnectionAccessReplyReceived) { - handleIncomingConnection(CONNECT_HFP_INCOMING, false); - sendConnectionAccessRemovalIntent(); - sendMessage(TRANSITION_TO_STABLE); - } - break; - case CONNECT_A2DP_INCOMING: - // Serialize the commands. - deferMessage(message); - break; - case CONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_HFP_OUTGOING: - // We don't know at what state we are in the incoming HFP connection state. - // We can be changing from DISCONNECTED to CONNECTING, or - // from CONNECTING to CONNECTED, so serializing this command is - // the safest option. - deferMessage(message); - break; - case DISCONNECT_HFP_INCOMING: - // Nothing to do here, we will already be DISCONNECTED - // by this point. - break; - case DISCONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_A2DP_INCOMING: - // Bluez handles incoming A2DP disconnect. - // If this causes incoming HFP to fail, it is more of a headset problem - // since both connections are incoming ones. - break; - case CONNECT_HID_OUTGOING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - case CONNECT_HID_INCOMING: - case DISCONNECT_HID_INCOMING: - break; // ignore - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - case CONNECT_OTHER_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class OutgoingA2dp extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - Log.i(TAG, "Entering OutgoingA2dp state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_A2DP_OUTGOING && - mCommand != DISCONNECT_A2DP_OUTGOING) { - Log.e(TAG, "Error: OutgoingA2DP state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) { - sendMessage(TRANSITION_TO_STABLE); - mService.sendProfileStateMessage(BluetoothProfileState.A2DP, - BluetoothProfileState.TRANSITION_TO_STABLE); - } - } - - @Override - public boolean processMessage(Message message) { - log("OutgoingA2dp State->Processing Message: " + message.what); - Message deferMsg = new Message(); - switch(message.what) { - case CONNECT_HFP_OUTGOING: - processCommand(CONNECT_HFP_OUTGOING); - - // Don't cancel A2DP outgoing as there is no guarantee it - // will get canceled. - // It might already be connected but we might not have got the - // A2DP_SINK_STATE_CHANGE. Hence, no point disconnecting here. - // The worst case, the connection will fail, retry. - // The same applies to Disconnecting an A2DP connection. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case CONNECT_HFP_INCOMING: - processCommand(CONNECT_HFP_INCOMING); - - // Don't cancel A2DP outgoing as there is no guarantee - // it will get canceled. - // The worst case, the connection will fail, retry. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case CONNECT_A2DP_INCOMING: - // Bluez will take care of conflicts between incoming and outgoing - // connections. - transitionTo(mIncomingA2dp); - break; - case CONNECT_A2DP_OUTGOING: - // Ignore - break; - case DISCONNECT_HFP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_HFP_INCOMING: - // At this point, we are already disconnected - // with HFP. Sometimes A2DP connection can - // fail due to the disconnection of HFP. So add a retry - // for the A2DP. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case DISCONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_A2DP_INCOMING: - // Ignore, will be handled by Bluez - break; - case CONNECT_HID_OUTGOING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - case CONNECT_HID_INCOMING: - transitionTo(mIncomingHid); - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case DISCONNECT_HID_INCOMING: - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; // ignore - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - case CONNECT_OTHER_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class IncomingA2dp extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - Log.i(TAG, "Entering IncomingA2dp state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_A2DP_INCOMING && - mCommand != DISCONNECT_A2DP_INCOMING) { - Log.e(TAG, "Error: IncomingA2DP state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) { - sendMessage(TRANSITION_TO_STABLE); - mService.sendProfileStateMessage(BluetoothProfileState.A2DP, - BluetoothProfileState.TRANSITION_TO_STABLE); - } - } - - @Override - public boolean processMessage(Message message) { - log("IncomingA2dp State->Processing Message: " + message.what); - switch(message.what) { - case CONNECT_HFP_OUTGOING: - deferMessage(message); - break; - case CONNECT_HFP_INCOMING: - // Shouldn't happen, but serialize the commands. - deferMessage(message); - break; - case CONNECT_A2DP_INCOMING: - // ignore - break; - case CONNECTION_ACCESS_REQUEST_REPLY: - int val = message.arg1; - mConnectionAccessReplyReceived = true; - boolean value = false; - if (val == BluetoothDevice.CONNECTION_ACCESS_YES) { - value = true; - } - setTrust(val); - handleIncomingConnection(CONNECT_A2DP_INCOMING, value); - break; - case CONNECTION_ACCESS_REQUEST_EXPIRY: - // The check protects the race condition between REQUEST_REPLY - // and the timer expiry. - if (!mConnectionAccessReplyReceived) { - handleIncomingConnection(CONNECT_A2DP_INCOMING, false); - sendConnectionAccessRemovalIntent(); - sendMessage(TRANSITION_TO_STABLE); - } - break; - case CONNECT_A2DP_OUTGOING: - // Defer message and retry - deferMessage(message); - break; - case DISCONNECT_HFP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_HFP_INCOMING: - // Shouldn't happen but if does, we can handle it. - // Depends if the headset can handle it. - // Incoming A2DP will be handled by Bluez, Disconnect HFP - // the socket would have already been closed. - // ignore - break; - case DISCONNECT_A2DP_OUTGOING: - deferMessage(message); - break; - case DISCONNECT_A2DP_INCOMING: - // Ignore, will be handled by Bluez - break; - case CONNECT_HID_OUTGOING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - case CONNECT_HID_INCOMING: - case DISCONNECT_HID_INCOMING: - break; // ignore - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - case CONNECT_OTHER_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - - private class OutgoingHid extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - log("Entering OutgoingHid state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_HID_OUTGOING && - mCommand != DISCONNECT_HID_OUTGOING) { - Log.e(TAG, "Error: OutgoingHid state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); - } - - @Override - public boolean processMessage(Message message) { - log("OutgoingHid State->Processing Message: " + message.what); - Message deferMsg = new Message(); - switch(message.what) { - // defer all outgoing messages - case CONNECT_HFP_OUTGOING: - case CONNECT_A2DP_OUTGOING: - case CONNECT_HID_OUTGOING: - case DISCONNECT_HFP_OUTGOING: - case DISCONNECT_A2DP_OUTGOING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - - case CONNECT_HFP_INCOMING: - transitionTo(mIncomingHandsfree); - break; - case CONNECT_A2DP_INCOMING: - transitionTo(mIncomingA2dp); - - // Don't cancel HID outgoing as there is no guarantee it - // will get canceled. - // It might already be connected but we might not have got the - // INPUT_DEVICE_STATE_CHANGE. Hence, no point disconnecting here. - // The worst case, the connection will fail, retry. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case CONNECT_HID_INCOMING: - // Bluez will take care of the conflicts - transitionTo(mIncomingHid); - break; - - case DISCONNECT_HFP_INCOMING: - case DISCONNECT_A2DP_INCOMING: - // At this point, we are already disconnected - // with HFP. Sometimes HID connection can - // fail due to the disconnection of HFP. So add a retry - // for the HID. - if (mStatus) { - deferMsg.what = mCommand; - deferMessage(deferMsg); - } - break; - case DISCONNECT_HID_INCOMING: - // Ignore, will be handled by Bluez - break; - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private class IncomingHid extends State { - private boolean mStatus = false; - private int mCommand; - - @Override - public void enter() { - log("Entering IncomingHid state with: " + getCurrentMessage().what); - mCommand = getCurrentMessage().what; - if (mCommand != CONNECT_HID_INCOMING && - mCommand != DISCONNECT_HID_INCOMING) { - Log.e(TAG, "Error: IncomingHid state with command:" + mCommand); - } - mStatus = processCommand(mCommand); - if (!mStatus) sendMessage(TRANSITION_TO_STABLE); - } - - @Override - public boolean processMessage(Message message) { - log("IncomingHid State->Processing Message: " + message.what); - Message deferMsg = new Message(); - switch(message.what) { - case CONNECT_HFP_OUTGOING: - case CONNECT_HFP_INCOMING: - case DISCONNECT_HFP_OUTGOING: - case CONNECT_A2DP_INCOMING: - case CONNECT_A2DP_OUTGOING: - case DISCONNECT_A2DP_OUTGOING: - case CONNECT_HID_OUTGOING: - case CONNECT_HID_INCOMING: - case DISCONNECT_HID_OUTGOING: - deferMessage(message); - break; - case CONNECTION_ACCESS_REQUEST_REPLY: - mConnectionAccessReplyReceived = true; - int val = message.arg1; - setTrust(val); - handleIncomingConnection(CONNECT_HID_INCOMING, - val == BluetoothDevice.CONNECTION_ACCESS_YES); - break; - case CONNECTION_ACCESS_REQUEST_EXPIRY: - if (!mConnectionAccessReplyReceived) { - handleIncomingConnection(CONNECT_HID_INCOMING, false); - sendConnectionAccessRemovalIntent(); - sendMessage(TRANSITION_TO_STABLE); - } - break; - case DISCONNECT_HFP_INCOMING: - // Shouldn't happen but if does, we can handle it. - // Depends if the headset can handle it. - // Incoming HID will be handled by Bluez, Disconnect HFP - // the socket would have already been closed. - // ignore - break; - case DISCONNECT_HID_INCOMING: - case DISCONNECT_A2DP_INCOMING: - // Ignore, will be handled by Bluez - break; - case DISCONNECT_PBAP_OUTGOING: - case UNPAIR: - case AUTO_CONNECT_PROFILES: - deferMessage(message); - break; - case TRANSITION_TO_STABLE: - transitionTo(mBondedDevice); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - - synchronized void cancelCommand(int command) { - if (command == CONNECT_HFP_OUTGOING ) { - // Cancel the outgoing thread. - if (mHeadsetService != null) { - mHeadsetService.cancelConnectThread(); - } - // HeadsetService is down. Phone process most likely crashed. - // The thread would have got killed. - } - } - - synchronized void deferProfileServiceMessage(int command) { - Message msg = new Message(); - msg.what = command; - deferMessage(msg); - } - - private void updateIncomingAllowedTimer() { - // Not doing a perfect exponential backoff because - // we want two different rates. For all practical - // purposes, this is good enough. - if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER; - - mIncomingRejectTimer *= 5; - if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) { - mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER; - } - writeTimerValue(mIncomingRejectTimer); - } - - private boolean handleIncomingConnection(int command, boolean accept) { - boolean ret = false; - Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept); - switch (command) { - case CONNECT_HFP_INCOMING: - if (!accept) { - ret = mHeadsetService.rejectIncomingConnect(mDevice); - sendMessage(TRANSITION_TO_STABLE); - updateIncomingAllowedTimer(); - } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) { - writeTimerValue(0); - ret = mHeadsetService.acceptIncomingConnect(mDevice); - } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) { - writeTimerValue(0); - handleConnectionOfOtherProfiles(command); - ret = mHeadsetService.createIncomingConnect(mDevice); - } - break; - case CONNECT_A2DP_INCOMING: - if (!accept) { - ret = mA2dpService.allowIncomingConnect(mDevice, false); - sendMessage(TRANSITION_TO_STABLE); - updateIncomingAllowedTimer(); - } else { - writeTimerValue(0); - ret = mA2dpService.allowIncomingConnect(mDevice, true); - handleConnectionOfOtherProfiles(command); - } - break; - case CONNECT_HID_INCOMING: - if (!accept) { - ret = mService.allowIncomingProfileConnect(mDevice, false); - sendMessage(TRANSITION_TO_STABLE); - updateIncomingAllowedTimer(); - } else { - writeTimerValue(0); - ret = mService.allowIncomingProfileConnect(mDevice, true); - } - break; - default: - Log.e(TAG, "Waiting for incoming connection but state changed to:" + command); - break; - } - return ret; - } - - private void sendConnectionAccessIntent() { - mConnectionAccessReplyReceived = false; - - if (!mPowerManager.isScreenOn()) mWakeLock.acquire(); - - Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); - intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); - intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, - BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - } - - private void sendConnectionAccessRemovalIntent() { - mWakeLock.release(); - Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); - mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); - } - - private int getTrust() { - String address = mDevice.getAddress(); - if (mIncomingConnections != null) return mIncomingConnections.first; - return CONNECTION_ACCESS_UNDEFINED; - } - - - private String getStringValue(long value) { - StringBuilder sbr = new StringBuilder(); - sbr.append(Long.toString(System.currentTimeMillis())); - sbr.append("-"); - sbr.append(Long.toString(value)); - return sbr.toString(); - } - - private void setTrust(int value) { - String second; - if (mIncomingConnections == null) { - second = getStringValue(INIT_INCOMING_REJECT_TIMER); - } else { - second = mIncomingConnections.second; - } - - mIncomingConnections = new Pair(value, second); - mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); - } - - private void writeTimerValue(long value) { - Integer first; - if (mIncomingConnections == null) { - first = CONNECTION_ACCESS_UNDEFINED; - } else { - first = mIncomingConnections.first; - } - mIncomingConnections = new Pair(first, getStringValue(value)); - mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections); - } - - private long readTimerValue() { - if (mIncomingConnections == null) - return 0; - String value = mIncomingConnections.second; - String[] splits = value.split("-"); - if (splits != null && splits.length == 2) { - return Long.parseLong(splits[1]); - } - return 0; - } - - private boolean readIncomingAllowedValue() { - if (readTimerValue() == 0) return true; - String value = mIncomingConnections.second; - String[] splits = value.split("-"); - if (splits != null && splits.length == 2) { - long val1 = Long.parseLong(splits[0]); - long val2 = Long.parseLong(splits[1]); - if (val1 + val2 <= System.currentTimeMillis()) { - return true; - } - } - return false; - } - - synchronized boolean processCommand(int command) { - log("Processing command:" + command); - switch(command) { - case CONNECT_HFP_OUTGOING: - if (mHeadsetService == null) { - deferProfileServiceMessage(command); - } else { - return mHeadsetService.connectHeadsetInternal(mDevice); - } - break; - case CONNECT_HFP_INCOMING: - if (mHeadsetService == null) { - deferProfileServiceMessage(command); - } else { - processIncomingConnectCommand(command); - return true; - } - break; - case CONNECT_A2DP_OUTGOING: - if (mA2dpService != null) { - return mA2dpService.connectSinkInternal(mDevice); - } - break; - case CONNECT_A2DP_INCOMING: - processIncomingConnectCommand(command); - return true; - case CONNECT_HID_OUTGOING: - return mService.connectInputDeviceInternal(mDevice); - case CONNECT_HID_INCOMING: - processIncomingConnectCommand(command); - return true; - case DISCONNECT_HFP_OUTGOING: - if (mHeadsetService == null) { - deferProfileServiceMessage(command); - } else { - // Disconnect PBAP - // TODO(): Add PBAP to the state machine. - Message m = new Message(); - m.what = DISCONNECT_PBAP_OUTGOING; - deferMessage(m); - if (mHeadsetService.getPriority(mDevice) == - BluetoothHeadset.PRIORITY_AUTO_CONNECT) { - mHeadsetService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); - } - return mHeadsetService.disconnectHeadsetInternal(mDevice); - } - break; - case DISCONNECT_HFP_INCOMING: - // ignore - return true; - case DISCONNECT_A2DP_INCOMING: - // ignore - return true; - case DISCONNECT_A2DP_OUTGOING: - if (mA2dpService != null) { - if (mA2dpService.getPriority(mDevice) == - BluetoothA2dp.PRIORITY_AUTO_CONNECT) { - mA2dpService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON); - } - return mA2dpService.disconnectSinkInternal(mDevice); - } - break; - case DISCONNECT_HID_INCOMING: - // ignore - return true; - case DISCONNECT_HID_OUTGOING: - if (mService.getInputDevicePriority(mDevice) == - BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { - mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON); - } - return mService.disconnectInputDeviceInternal(mDevice); - case DISCONNECT_PBAP_OUTGOING: - if (!mPbapServiceConnected) { - deferProfileServiceMessage(command); - } else { - return mPbapService.disconnect(); - } - break; - case UNPAIR: - writeTimerValue(INIT_INCOMING_REJECT_TIMER); - setTrust(CONNECTION_ACCESS_UNDEFINED); - return mService.removeBondInternal(mDevice.getAddress()); - default: - Log.e(TAG, "Error: Unknown Command"); - } - return false; - } - - private void processIncomingConnectCommand(int command) { - // Check if device is already trusted - int access = getTrust(); - if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { - handleIncomingConnection(command, true); - } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO && - !readIncomingAllowedValue()) { - handleIncomingConnection(command, false); - } else { - sendConnectionAccessIntent(); - Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY); - sendMessageDelayed(msg, - CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT); - } - } - - private void handleConnectionOfOtherProfiles(int command) { - // The white paper recommendations mentions that when there is a - // link loss, it is the responsibility of the remote device to connect. - // Many connect only 1 profile - and they connect the second profile on - // some user action (like play being pressed) and so we need this code. - // Auto Connect code only connects to the last connected device - which - // is useful in cases like when the phone reboots. But consider the - // following case: - // User is connected to the car's phone and A2DP profile. - // User comes to the desk and places the phone in the dock - // (or any speaker or music system or even another headset) and thus - // gets connected to the A2DP profile. User goes back to the car. - // Ideally the car's system is supposed to send incoming connections - // from both Handsfree and A2DP profile. But they don't. The Auto - // connect code, will not work here because we only auto connect to the - // last connected device for that profile which in this case is the dock. - // Now suppose a user is using 2 headsets simultaneously, one for the - // phone profile one for the A2DP profile. If this is the use case, we - // expect the user to use the preference to turn off the A2DP profile in - // the Settings screen for the first headset. Else, after link loss, - // there can be an incoming connection from the first headset which - // might result in the connection of the A2DP profile (if the second - // headset is slower) and thus the A2DP profile on the second headset - // will never get connected. - // - // TODO(): Handle other profiles here. - switch (command) { - case CONNECT_HFP_INCOMING: - // Connect A2DP if there is no incoming connection - // If the priority is OFF - don't auto connect. - if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON || - mA2dpService.getPriority(mDevice) == - BluetoothProfile.PRIORITY_AUTO_CONNECT) { - Message msg = new Message(); - msg.what = CONNECT_OTHER_PROFILES; - msg.arg1 = CONNECT_A2DP_OUTGOING; - sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); - } - break; - case CONNECT_A2DP_INCOMING: - // This is again against spec. HFP incoming connections should be made - // before A2DP, so we should not hit this case. But many devices - // don't follow this. - if (mHeadsetService != null && - (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON || - mHeadsetService.getPriority(mDevice) == - BluetoothProfile.PRIORITY_AUTO_CONNECT)) { - Message msg = new Message(); - msg.what = CONNECT_OTHER_PROFILES; - msg.arg1 = CONNECT_HFP_OUTGOING; - sendMessageDelayed(msg, CONNECT_OTHER_PROFILES_DELAY); - } - break; - default: - break; - } - - } - - /*package*/ BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Quit the state machine, only to be called by BluetoothService. - * - * @hide - */ - public void doQuit() { - this.quit(); - } - - private void log(String message) { - if (DBG) { - Log.i(TAG, "Device:" + mDevice + " Message:" + message); - } - } -} diff --git a/framework/java/android/bluetooth/BluetoothProfileState.java b/framework/java/android/bluetooth/BluetoothProfileState.java deleted file mode 100644 index b0c0a0bc463..00000000000 --- a/framework/java/android/bluetooth/BluetoothProfileState.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2010 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.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Message; -import android.util.Log; - -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -/** - * This state machine is used to serialize the connections - * to a particular profile. Currently, we only allow one device - * to be connected to a particular profile. - * States: - * {@link StableState} : No pending commands. Send the - * command to the appropriate remote device specific state machine. - * - * {@link PendingCommandState} : A profile connection / disconnection - * command is being executed. This will result in a profile state - * change. Defer all commands. - * @hide - */ - -public class BluetoothProfileState extends StateMachine { - private static final boolean DBG = true; - private static final String TAG = "BluetoothProfileState"; - - public static final int HFP = 0; - public static final int A2DP = 1; - public static final int HID = 2; - - static final int TRANSITION_TO_STABLE = 100; - - private int mProfile; - private BluetoothDevice mPendingDevice; - private PendingCommandState mPendingCommandState = new PendingCommandState(); - private StableState mStableState = new StableState(); - - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (device == null) { - return; - } - if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED)) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED)) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) { - int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0); - if (mProfile == HID && (newState == BluetoothProfile.STATE_CONNECTED || - newState == BluetoothProfile.STATE_DISCONNECTED)) { - sendMessage(TRANSITION_TO_STABLE); - } - } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { - if (device.equals(mPendingDevice)) { - sendMessage(TRANSITION_TO_STABLE); - } - } - } - }; - - public BluetoothProfileState(Context context, int profile) { - super("BluetoothProfileState:" + profile); - mProfile = profile; - addState(mStableState); - addState(mPendingCommandState); - setInitialState(mStableState); - - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); - context.registerReceiver(mBroadcastReceiver, filter); - } - - private class StableState extends State { - @Override - public void enter() { - log("Entering Stable State"); - mPendingDevice = null; - } - - @Override - public boolean processMessage(Message msg) { - if (msg.what != TRANSITION_TO_STABLE) { - transitionTo(mPendingCommandState); - } - return true; - } - } - - private class PendingCommandState extends State { - @Override - public void enter() { - log("Entering PendingCommandState State"); - dispatchMessage(getCurrentMessage()); - } - - @Override - public boolean processMessage(Message msg) { - if (msg.what == TRANSITION_TO_STABLE) { - transitionTo(mStableState); - } else { - dispatchMessage(msg); - } - return true; - } - - private void dispatchMessage(Message msg) { - BluetoothDeviceProfileState deviceProfileMgr = - (BluetoothDeviceProfileState)msg.obj; - int cmd = msg.arg1; - if (mPendingDevice == null || mPendingDevice.equals(deviceProfileMgr.getDevice())) { - mPendingDevice = deviceProfileMgr.getDevice(); - deviceProfileMgr.sendMessage(cmd); - } else { - Message deferMsg = new Message(); - deferMsg.arg1 = cmd; - deferMsg.obj = deviceProfileMgr; - deferMessage(deferMsg); - } - } - } - - private void log(String message) { - if (DBG) { - Log.i(TAG, "Message:" + message); - } - } -} diff --git a/framework/java/android/bluetooth/HeadsetBase.java b/framework/java/android/bluetooth/HeadsetBase.java deleted file mode 100644 index 9ef2eb5f3ce..00000000000 --- a/framework/java/android/bluetooth/HeadsetBase.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2007 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.os.Handler; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; - -/** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * - * The base RFCOMM (service) connection for a headset or handsfree device. - * - * In the future this class will be removed. - * - * @hide - */ -public final class HeadsetBase { - private static final String TAG = "Bluetooth HeadsetBase"; - private static final boolean DBG = false; - - public static final int RFCOMM_DISCONNECTED = 1; - - public static final int DIRECTION_INCOMING = 1; - public static final int DIRECTION_OUTGOING = 2; - - private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ - - private final BluetoothAdapter mAdapter; - private final BluetoothDevice mRemoteDevice; - private final String mAddress; // for native code - private final int mRfcommChannel; - private int mNativeData; - private Thread mEventThread; - private volatile boolean mEventThreadInterrupted; - private Handler mEventThreadHandler; - private int mTimeoutRemainingMs; - private final int mDirection; - private final long mConnectTimestamp; - - protected AtParser mAtParser; - - private WakeLock mWakeLock; // held while processing an AT command - - private native static void classInitNative(); - static { - classInitNative(); - } - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - releaseWakeLock(); - } finally { - super.finalize(); - } - } - - private native void cleanupNativeDataNative(); - - public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, - BluetoothDevice device, int rfcommChannel) { - mDirection = DIRECTION_OUTGOING; - mConnectTimestamp = System.currentTimeMillis(); - mAdapter = adapter; - mRemoteDevice = device; - mAddress = device.getAddress(); - mRfcommChannel = rfcommChannel; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); - mWakeLock.setReferenceCounted(false); - initializeAtParser(); - // Must be called after this.mAddress is set. - initializeNativeDataNative(-1); - } - - /* Create from an existing rfcomm connection */ - public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, - BluetoothDevice device, - int socketFd, int rfcommChannel, Handler handler) { - mDirection = DIRECTION_INCOMING; - mConnectTimestamp = System.currentTimeMillis(); - mAdapter = adapter; - mRemoteDevice = device; - mAddress = device.getAddress(); - mRfcommChannel = rfcommChannel; - mEventThreadHandler = handler; - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase"); - mWakeLock.setReferenceCounted(false); - initializeAtParser(); - // Must be called after this.mAddress is set. - initializeNativeDataNative(socketFd); - } - - private native void initializeNativeDataNative(int socketFd); - - /* Process an incoming AT command line - */ - protected void handleInput(String input) { - acquireWakeLock(); - long timestamp; - - synchronized(HeadsetBase.class) { - if (sAtInputCount == Integer.MAX_VALUE) { - sAtInputCount = 0; - } else { - sAtInputCount++; - } - } - - if (DBG) timestamp = System.currentTimeMillis(); - AtCommandResult result = mAtParser.process(input); - if (DBG) Log.d(TAG, "Processing " + input + " took " + - (System.currentTimeMillis() - timestamp) + " ms"); - - if (result.getResultCode() == AtCommandResult.ERROR) { - Log.i(TAG, "Error processing <" + input + ">"); - } - - sendURC(result.toString()); - - releaseWakeLock(); - } - - /** - * Register AT commands that are common to all Headset / Handsets. This - * function is called by the HeadsetBase constructor. - */ - protected void initializeAtParser() { - mAtParser = new AtParser(); - - //TODO(): Get rid of this as there are no parsers registered. But because of dependencies - // it needs to be done as part of refactoring HeadsetBase and BluetoothHandsfree - } - - public AtParser getAtParser() { - return mAtParser; - } - - public void startEventThread() { - mEventThread = - new Thread("HeadsetBase Event Thread") { - public void run() { - int last_read_error; - while (!mEventThreadInterrupted) { - String input = readNative(500); - if (input != null) { - handleInput(input); - } else { - last_read_error = getLastReadStatusNative(); - if (last_read_error != 0) { - Log.i(TAG, "headset read error " + last_read_error); - if (mEventThreadHandler != null) { - mEventThreadHandler.obtainMessage(RFCOMM_DISCONNECTED) - .sendToTarget(); - } - disconnectNative(); - break; - } - } - } - } - }; - mEventThreadInterrupted = false; - mEventThread.start(); - } - - private native String readNative(int timeout_ms); - private native int getLastReadStatusNative(); - - private void stopEventThread() { - mEventThreadInterrupted = true; - mEventThread.interrupt(); - try { - mEventThread.join(); - } catch (java.lang.InterruptedException e) { - // FIXME: handle this, - } - mEventThread = null; - } - - public boolean connect(Handler handler) { - if (mEventThread == null) { - if (!connectNative()) return false; - mEventThreadHandler = handler; - } - return true; - } - private native boolean connectNative(); - - /* - * Returns true when either the asynchronous connect is in progress, or - * the connect is complete. Call waitForAsyncConnect() to find out whether - * the connect is actually complete, or disconnect() to cancel. - */ - - public boolean connectAsync() { - int ret = connectAsyncNative(); - return (ret == 0) ? true : false; - } - private native int connectAsyncNative(); - - public int getRemainingAsyncConnectWaitingTimeMs() { - return mTimeoutRemainingMs; - } - - /* - * Returns 1 when an async connect is complete, 0 on timeout, and -1 on - * error. On error, handler will be called, and you need to re-initiate - * the async connect. - */ - public int waitForAsyncConnect(int timeout_ms, Handler handler) { - int res = waitForAsyncConnectNative(timeout_ms); - if (res > 0) { - mEventThreadHandler = handler; - } - return res; - } - private native int waitForAsyncConnectNative(int timeout_ms); - - public void disconnect() { - if (mEventThread != null) { - stopEventThread(); - } - disconnectNative(); - } - private native void disconnectNative(); - - - /* - * Note that if a remote side disconnects, this method will still return - * true until disconnect() is called. You know when a remote side - * disconnects because you will receive the intent - * IBluetoothService.REMOTE_DEVICE_DISCONNECTED_ACTION. If, when you get - * this intent, method isConnected() returns true, you know that the - * disconnect was initiated by the remote device. - */ - - public boolean isConnected() { - return mEventThread != null; - } - - public BluetoothDevice getRemoteDevice() { - return mRemoteDevice; - } - - public int getDirection() { - return mDirection; - } - - public long getConnectTimestamp() { - return mConnectTimestamp; - } - - public synchronized boolean sendURC(String urc) { - if (urc.length() > 0) { - boolean ret = sendURCNative(urc); - return ret; - } - return true; - } - private native boolean sendURCNative(String urc); - - private synchronized void acquireWakeLock() { - if (!mWakeLock.isHeld()) { - mWakeLock.acquire(); - } - } - - private synchronized void releaseWakeLock() { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - } - - public static int getAtInputCount() { - return sAtInputCount; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} -- GitLab From 884452306d321a159f432fbec2573d2f37e513c5 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Wed, 25 Jan 2012 16:16:48 -0800 Subject: [PATCH 0216/1408] Changes for new Bluetooth APIs. Changes to Bluetooth Adapter, Device and IBluetooth interfaces for new Bluetooth APIs. Delete AudioGateway. Change-Id: Ib51b31187eafde261441b9311b5e7e13c8bff82f --- .../android/bluetooth/BluetoothAdapter.java | 45 ++-- .../bluetooth/BluetoothAudioGateway.java | 202 ------------------ .../android/bluetooth/BluetoothDevice.java | 82 ++++--- .../android/bluetooth/BluetoothHealth.java | 36 +++- .../bluetooth/BluetoothInputDevice.java | 28 ++- .../java/android/bluetooth/BluetoothPan.java | 29 ++- .../android/bluetooth/BluetoothSocket.java | 18 +- .../java/android/bluetooth/IBluetooth.aidl | 95 ++------ 8 files changed, 183 insertions(+), 352 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothAudioGateway.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8e3df472a3f..755884c2702 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -367,10 +367,12 @@ public final class BluetoothAdapter { */ public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + IBinder b = ServiceManager.getService("bluetooth"); if (b != null) { IBluetooth service = IBluetooth.Stub.asInterface(b); sAdapter = new BluetoothAdapter(service); + } else { + Log.e(TAG, "Bluetooth binder is null"); } } return sAdapter; @@ -378,9 +380,8 @@ public final class BluetoothAdapter { /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. - * @hide */ - public BluetoothAdapter(IBluetooth service) { + BluetoothAdapter(IBluetooth service) { if (service == null) { throw new IllegalArgumentException("service is null"); } @@ -450,8 +451,9 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter */ public int getState() { + if (mService == null) return STATE_OFF; try { - return mService.getBluetoothState(); + return mService.getState(); } catch (RemoteException e) {Log.e(TAG, "", e);} return STATE_OFF; } @@ -516,7 +518,7 @@ public final class BluetoothAdapter { */ public boolean disable() { try { - return mService.disable(true); + return mService.disable(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -774,10 +776,10 @@ public final class BluetoothAdapter { */ public Set getBondedDevices() { if (getState() != STATE_ON) { - return toDeviceSet(new String[0]); + return toDeviceSet(new BluetoothDevice[0]); } try { - return toDeviceSet(mService.listBonds()); + return toDeviceSet(mService.getBondedDevices()); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -999,7 +1001,6 @@ public final class BluetoothAdapter { private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { RfcommChannelPicker picker = new RfcommChannelPicker(uuid); - BluetoothServerSocket socket; int channel; int errno; @@ -1031,10 +1032,11 @@ public final class BluetoothAdapter { } int handle = -1; - try { + //TODO(BT): + /*try { handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel, new Binder()); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ if (handle == -1) { try { socket.close(); @@ -1047,11 +1049,13 @@ public final class BluetoothAdapter { public void handleMessage(Message msg) { /* handle socket closing */ int handle = msg.what; + // TODO(BT): + /* try { if (DBG) Log.d(TAG, "Removing service record " + - Integer.toHexString(handle)); - mService.removeServiceRecord(handle); + Integer.toHexString(handle)); } catch (RemoteException e) {Log.e(TAG, "", e);} + */ } }; } @@ -1134,6 +1138,8 @@ public final class BluetoothAdapter { */ public Pair readOutOfBandData() { if (getState() != STATE_ON) return null; + //TODO(BT + /* try { byte[] hash; byte[] randomizer; @@ -1151,7 +1157,7 @@ public final class BluetoothAdapter { } return new Pair(hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return null; } @@ -1276,12 +1282,14 @@ public final class BluetoothAdapter { BluetoothStateChangeCallback callback) { if (callback == null) return false; + //TODO(BT) + /* try { return mService.changeApplicationBluetoothState(on, new StateChangeCallbackWrapper(callback), new Binder()); } catch (RemoteException e) { Log.e(TAG, "changeBluetoothState", e); - } + }*/ return false; } @@ -1309,12 +1317,9 @@ public final class BluetoothAdapter { } } - private Set toDeviceSet(String[] addresses) { - Set devices = new HashSet(addresses.length); - for (int i = 0; i < addresses.length; i++) { - devices.add(getRemoteDevice(addresses[i])); - } - return Collections.unmodifiableSet(devices); + private Set toDeviceSet(BluetoothDevice[] devices) { + Set deviceSet = new HashSet(Arrays.asList(devices)); + return Collections.unmodifiableSet(deviceSet); } /** diff --git a/framework/java/android/bluetooth/BluetoothAudioGateway.java b/framework/java/android/bluetooth/BluetoothAudioGateway.java deleted file mode 100644 index 93513937285..00000000000 --- a/framework/java/android/bluetooth/BluetoothAudioGateway.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2007 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 java.lang.Thread; - -import android.os.Message; -import android.os.Handler; -import android.util.Log; - -/** - * Listens for incoming RFCOMM connection for the headset / handsfree service. - * - * TODO: Use the new generic BluetoothSocket class instead of this legacy code - * - * @hide - */ -public final class BluetoothAudioGateway { - private static final String TAG = "BT Audio Gateway"; - private static final boolean DBG = false; - - private int mNativeData; - static { classInitNative(); } - - /* in */ - private int mHandsfreeAgRfcommChannel = -1; - private int mHeadsetAgRfcommChannel = -1; - - /* out - written by native code */ - private String mConnectingHeadsetAddress; - private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ - private int mConnectingHeadsetSocketFd; - private String mConnectingHandsfreeAddress; - private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ - private int mConnectingHandsfreeSocketFd; - private int mTimeoutRemainingMs; /* in/out */ - - private final BluetoothAdapter mAdapter; - - public static final int DEFAULT_HF_AG_CHANNEL = 10; - public static final int DEFAULT_HS_AG_CHANNEL = 11; - - public BluetoothAudioGateway(BluetoothAdapter adapter) { - this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); - } - - public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel, - int headsetAgRfcommChannel) { - mAdapter = adapter; - mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; - mHeadsetAgRfcommChannel = headsetAgRfcommChannel; - initializeNativeDataNative(); - } - - private Thread mConnectThead; - private volatile boolean mInterrupted; - private static final int SELECT_WAIT_TIMEOUT = 1000; - - private Handler mCallback; - - public class IncomingConnectionInfo { - public BluetoothAdapter mAdapter; - public BluetoothDevice mRemoteDevice; - public int mSocketFd; - public int mRfcommChan; - IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice, - int socketFd, int rfcommChan) { - mAdapter = adapter; - mRemoteDevice = remoteDevice; - mSocketFd = socketFd; - mRfcommChan = rfcommChan; - } - } - - public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; - public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101; - - public synchronized boolean start(Handler callback) { - - if (mConnectThead == null) { - mCallback = callback; - mConnectThead = new Thread(TAG) { - public void run() { - if (DBG) log("Connect Thread starting"); - while (!mInterrupted) { - //Log.i(TAG, "waiting for connect"); - mConnectingHeadsetRfcommChannel = -1; - mConnectingHandsfreeRfcommChannel = -1; - if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) { - if (mTimeoutRemainingMs > 0) { - try { - Log.i(TAG, "select thread timed out, but " + - mTimeoutRemainingMs + "ms of waiting remain."); - Thread.sleep(mTimeoutRemainingMs); - } catch (InterruptedException e) { - Log.i(TAG, "select thread was interrupted (2), exiting"); - mInterrupted = true; - } - } - } - else { - Log.i(TAG, "connect notification!"); - /* A device connected (most likely just one, but - it is possible for two separate devices, one - a headset and one a handsfree, to connect - simultaneously. - */ - if (mConnectingHeadsetRfcommChannel >= 0) { - Log.i(TAG, "Incoming connection from headset " + - mConnectingHeadsetAddress + " on channel " + - mConnectingHeadsetRfcommChannel); - Message msg = Message.obtain(mCallback); - msg.what = MSG_INCOMING_HEADSET_CONNECTION; - msg.obj = new IncomingConnectionInfo( - mAdapter, - mAdapter.getRemoteDevice(mConnectingHeadsetAddress), - mConnectingHeadsetSocketFd, - mConnectingHeadsetRfcommChannel); - msg.sendToTarget(); - } - if (mConnectingHandsfreeRfcommChannel >= 0) { - Log.i(TAG, "Incoming connection from handsfree " + - mConnectingHandsfreeAddress + " on channel " + - mConnectingHandsfreeRfcommChannel); - Message msg = Message.obtain(); - msg.setTarget(mCallback); - msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; - msg.obj = new IncomingConnectionInfo( - mAdapter, - mAdapter.getRemoteDevice(mConnectingHandsfreeAddress), - mConnectingHandsfreeSocketFd, - mConnectingHandsfreeRfcommChannel); - msg.sendToTarget(); - } - } - } - if (DBG) log("Connect Thread finished"); - } - }; - - if (setUpListeningSocketsNative() == false) { - Log.e(TAG, "Could not set up listening socket, exiting"); - return false; - } - - mInterrupted = false; - mConnectThead.start(); - } - - return true; - } - - public synchronized void stop() { - if (mConnectThead != null) { - if (DBG) log("stopping Connect Thread"); - mInterrupted = true; - try { - mConnectThead.interrupt(); - if (DBG) log("waiting for thread to terminate"); - mConnectThead.join(); - mConnectThead = null; - mCallback = null; - tearDownListeningSocketsNative(); - } catch (InterruptedException e) { - Log.w(TAG, "Interrupted waiting for Connect Thread to join"); - } - } - } - - protected void finalize() throws Throwable { - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - - private static native void classInitNative(); - private native void initializeNativeDataNative(); - private native void cleanupNativeDataNative(); - private native boolean waitForHandsfreeConnectNative(int timeoutMs); - private native boolean setUpListeningSocketsNative(); - private native void tearDownListeningSocketsNative(); - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 56e17354cca..bd089ff6dac 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -576,7 +576,7 @@ public final class BluetoothDevice implements Parcelable { */ public String getName() { try { - return sService.getRemoteName(mAddress); + return sService.getRemoteName(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -590,7 +590,7 @@ public final class BluetoothDevice implements Parcelable { */ public String getAlias() { try { - return sService.getRemoteAlias(mAddress); + return sService.getRemoteAlias(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -607,7 +607,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean setAlias(String alias) { try { - return sService.setRemoteAlias(mAddress, alias); + return sService.setRemoteAlias(this, alias); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -643,7 +643,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean createBond() { try { - return sService.createBond(mAddress); + return sService.createBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -668,9 +668,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) { + //TODO(BT) + /* try { - return sService.createBondOutOfBand(mAddress, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.createBondOutOfBand(this, hash, randomizer); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } @@ -688,9 +690,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) { + //TODO(BT) + /* try { - return sService.setDeviceOutOfBandData(mAddress, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.setDeviceOutOfBandData(this, hash, randomizer); + } catch (RemoteException e) {Log.e(TAG, "", e);} */ return false; } @@ -703,7 +707,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean cancelBondProcess() { try { - return sService.cancelBondProcess(mAddress); + return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -720,7 +724,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean removeBond() { try { - return sService.removeBond(mAddress); + return sService.removeBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -737,7 +741,7 @@ public final class BluetoothDevice implements Parcelable { */ public int getBondState() { try { - return sService.getBondState(mAddress); + return sService.getBondState(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return BOND_NONE; } @@ -750,7 +754,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothClass getBluetoothClass() { try { - int classInt = sService.getRemoteClass(mAddress); + int classInt = sService.getRemoteClass(this); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -763,11 +767,13 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean getTrustState() { + //TODO(BT) + /* try { - return sService.getTrustState(mAddress); + return sService.getTrustState(this); } catch (RemoteException e) { Log.e(TAG, "", e); - } + }*/ return false; } @@ -778,11 +784,13 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setTrust(boolean value) { + //TODO(BT) + /* try { - return sService.setTrust(mAddress, value); + return sService.setTrust(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); - } + }*/ return false; } @@ -800,7 +808,7 @@ public final class BluetoothDevice implements Parcelable { */ public ParcelUuid[] getUuids() { try { - return sService.getRemoteUuids(mAddress); + return sService.getRemoteUuids(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -821,65 +829,77 @@ public final class BluetoothDevice implements Parcelable { * was started. */ public boolean fetchUuidsWithSdp() { + //TODO(BT) + /* try { - return sService.fetchRemoteUuids(mAddress, null, null); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.fetchRemoteUuids(this, null, null); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } /** @hide */ public int getServiceChannel(ParcelUuid uuid) { + //TODO(BT) + /* try { - return sService.getRemoteServiceChannel(mAddress, uuid); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.getRemoteServiceChannel(this, uuid); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return BluetoothDevice.ERROR; } /** @hide */ public boolean setPin(byte[] pin) { try { - return sService.setPin(mAddress, pin); + return sService.setPin(this, true, pin.length, pin); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** @hide */ public boolean setPasskey(int passkey) { + //TODO(BT) + /* try { - return sService.setPasskey(mAddress, passkey); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.setPasskey(this, true, 4, passkey); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } /** @hide */ public boolean setPairingConfirmation(boolean confirm) { try { - return sService.setPairingConfirmation(mAddress, confirm); + return sService.setPairingConfirmation(this, confirm); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } /** @hide */ public boolean setRemoteOutOfBandData() { + // TODO(BT) + /* try { - return sService.setRemoteOutOfBandData(mAddress); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.setRemoteOutOfBandData(this); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } /** @hide */ public boolean cancelPairingUserInput() { + // TODO(BT) + /* try { - return sService.cancelPairingUserInput(mAddress); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.cancelPairingUserInput(this); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } /** @hide */ public boolean isBluetoothDock() { + // TODO(BT) + /* try { - return sService.isBluetoothDock(mAddress); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return sService.isBluetoothDock(this); + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ return false; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index f850c0224a5..f4aee98af25 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -146,11 +146,13 @@ public final class BluetoothHealth implements BluetoothProfile { new BluetoothHealthAppConfiguration(name, dataType, role, channelType); if (mService != null) { + //TODO(BT + /* try { result = mService.registerAppConfiguration(config, wrapper); } 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())); @@ -170,11 +172,13 @@ public final class BluetoothHealth implements BluetoothProfile { public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; if (mService != null && isEnabled() && config != null) { + //TODO(BT + /* try { result = mService.unregisterAppConfiguration(config); } 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())); @@ -199,11 +203,13 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + //TODO(BT + /* try { return mService.connectChannelToSource(device, config); } 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())); @@ -228,11 +234,13 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config, int channelType) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + //TODO(BT + /* try { return mService.connectChannelToSink(device, config, channelType); } 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())); @@ -257,11 +265,13 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config, int channelId) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + //TODO(BT + /* try { return mService.disconnectChannel(device, config, channelId); } 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())); @@ -286,11 +296,13 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + //TODO(BT + /* try { return mService.getMainChannelFd(device, config); } 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())); @@ -316,11 +328,13 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT + /* try { return mService.getHealthDeviceConnectionState(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())); @@ -344,12 +358,14 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public List getConnectedDevices() { if (mService != null && isEnabled()) { + //TODO(BT + /* try { return mService.getConnectedHealthDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -376,12 +392,14 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (mService != null && isEnabled()) { + //TODO(BT + /* try { return mService.getHealthDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 1a9e011ad7f..62c7bdec972 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -146,12 +146,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT) + /* try { return mService.connectInputDevice(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -187,12 +189,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT) + /* try { return mService.disconnectInputDevice(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -204,12 +208,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { + //TODO(BT) + /* try { return mService.getConnectedInputDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -221,12 +227,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { + //TODO(BT) + /* try { return mService.getInputDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -239,12 +247,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT) + /* try { return mService.getInputDeviceConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -273,12 +283,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { priority != BluetoothProfile.PRIORITY_ON) { return false; } + //TODO(BT) + /* try { return mService.setInputDevicePriority(device, priority); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -301,12 +313,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT) + /* try { return mService.getInputDevicePriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 5d9d8bea8e0..13526e81fa5 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -165,12 +165,14 @@ public final class BluetoothPan implements BluetoothProfile { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT + /* try { return mService.connectPanDevice(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -206,12 +208,14 @@ public final class BluetoothPan implements BluetoothProfile { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT + /* try { return mService.disconnectPanDevice(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -223,12 +227,14 @@ public final class BluetoothPan implements BluetoothProfile { public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { + //TODO(BT + /* try { return mService.getConnectedPanDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -240,12 +246,14 @@ public final class BluetoothPan implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { + //TODO(BT + /* try { return mService.getPanDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -258,12 +266,14 @@ public final class BluetoothPan implements BluetoothProfile { if (DBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { + //TODO(BT + /* try { return mService.getPanDeviceConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } + }*/ } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -271,21 +281,26 @@ public final class BluetoothPan implements BluetoothProfile { public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); + //TODO(BT + /* try { mService.setBluetoothTethering(value); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } + }*/ } public boolean isTetheringOn() { if (DBG) log("isTetheringOn()"); + //TODO(BT + /* try { return mService.isTetheringOn(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } + }*/ + return false; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 20e8515608a..2662f391694 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -150,11 +150,14 @@ public final class BluetoothSocket implements Closeable { } else { mAddress = device.getAddress(); } + + //TODO(BT) + /* if (fd == -1) { initSocketNative(); } else { initSocketFromFdNative(fd); - } + }*/ mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); mSocketState = SocketState.INIT; @@ -227,6 +230,8 @@ public final class BluetoothSocket implements Closeable { */ public void close() throws IOException { // abort blocking operations on the socket + //TODO(BT) + /* mLock.readLock().lock(); try { if (mSocketState == SocketState.CLOSED) return; @@ -248,7 +253,7 @@ public final class BluetoothSocket implements Closeable { } } finally { mLock.writeLock().unlock(); - } + }*/ } /** @@ -296,13 +301,16 @@ public final class BluetoothSocket implements Closeable { * so that BluetoothAdapter can check the error code for EADDRINUSE */ /*package*/ int bindListen() { + return -1; + //TODO(BT) + /* mLock.readLock().lock(); try { if (mSocketState == SocketState.CLOSED) return EBADFD; return bindListenNative(); } finally { mLock.readLock().unlock(); - } + }*/ } /*package*/ BluetoothSocket accept(int timeout) throws IOException { @@ -388,9 +396,11 @@ public final class BluetoothSocket implements Closeable { channel = -1; boolean inProgress = false; + //TODO(BT) + /* try { inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) {Log.e(TAG, "", e);}*/ if (!inProgress) throw new IOException("Unable to start Service Discovery"); diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 6075363e51b..21f2ae33212 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -32,15 +32,14 @@ import android.os.ParcelFileDescriptor; interface IBluetooth { boolean isEnabled(); - int getBluetoothState(); + int getState(); boolean enable(); - boolean enableNoAutoConnect(); - boolean disable(boolean persistSetting); + boolean disable(); String getAddress(); - String getName(); - boolean setName(in String name); ParcelUuid[] getUuids(); + boolean setName(in String name); + String getName(); int getScanMode(); boolean setScanMode(int mode, int duration); @@ -51,77 +50,29 @@ interface IBluetooth boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); - byte[] readOutOfBandData(); int getAdapterConnectionState(); int getProfileConnectionState(int profile); - boolean changeApplicationBluetoothState(boolean on, - in IBluetoothStateChangeCallback callback, in - IBinder b); - - boolean createBond(in String address); - boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer); - boolean cancelBondProcess(in String address); - boolean removeBond(in String address); - String[] listBonds(); - int getBondState(in String address); - boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer); - - String getRemoteName(in String address); - String getRemoteAlias(in String address); - boolean setRemoteAlias(in String address, in String name); - int getRemoteClass(in String address); - ParcelUuid[] getRemoteUuids(in String address); - boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback); - int getRemoteServiceChannel(in String address, in ParcelUuid uuid); - - boolean setPin(in String address, in byte[] pin); - boolean setPasskey(in String address, int passkey); - boolean setPairingConfirmation(in String address, boolean confirm); - boolean setRemoteOutOfBandData(in String addres); - boolean cancelPairingUserInput(in String address); - - boolean setTrust(in String address, in boolean value); - boolean getTrustState(in String address); - boolean isBluetoothDock(in String address); - - int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); - void removeServiceRecord(int handle); - boolean allowIncomingProfileConnect(in BluetoothDevice device, boolean value); - - boolean connectHeadset(String address); - boolean disconnectHeadset(String address); - boolean notifyIncomingConnection(String address, boolean rejected); - - // HID profile APIs - boolean connectInputDevice(in BluetoothDevice device); - boolean disconnectInputDevice(in BluetoothDevice device); - List getConnectedInputDevices(); - List getInputDevicesMatchingConnectionStates(in int[] states); - int getInputDeviceConnectionState(in BluetoothDevice device); - boolean setInputDevicePriority(in BluetoothDevice device, int priority); - int getInputDevicePriority(in BluetoothDevice device); - - boolean isTetheringOn(); - void setBluetoothTethering(boolean value); - int getPanDeviceConnectionState(in BluetoothDevice device); - List getConnectedPanDevices(); - List getPanDevicesMatchingConnectionStates(in int[] states); - boolean connectPanDevice(in BluetoothDevice device); - boolean disconnectPanDevice(in BluetoothDevice device); - // HDP profile APIs - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, - in IBluetoothHealthCallback callback); - boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); - boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, - int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); - ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - List getConnectedHealthDevices(); - List getHealthDevicesMatchingConnectionStates(in int[] states); - int getHealthDeviceConnectionState(in BluetoothDevice device); + BluetoothDevice[] getBondedDevices(); + boolean createBond(in BluetoothDevice device); + boolean cancelBondProcess(in BluetoothDevice device); + boolean removeBond(in BluetoothDevice device); + int getBondState(in BluetoothDevice device); + + String getRemoteName(in BluetoothDevice device); + String getRemoteAlias(in BluetoothDevice device); + boolean setRemoteAlias(in BluetoothDevice device, in String name); + int getRemoteClass(in BluetoothDevice device); + ParcelUuid[] getRemoteUuids(in BluetoothDevice device); + //TODO(BT) + //boolean fetchRemoteUuids(in BluetoothDevice device, in ParcelUuid uuid, + // in IBluetoothCallback callback); + + boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); + boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] + passkey); + boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); } -- GitLab From d95bd9c8fbae3ce3b5d7c011b58df67ba1602ba7 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 6 Mar 2012 17:15:16 -0800 Subject: [PATCH 0217/1408] Add a new version of the disable API. This allows for the setting to be persisted or not. Also turn on Bluetooth in System Server if needed. It won't work currently because the service wouldn't have started. Change-Id: I15fa2bff93aa32134c1b565fcbe90ba68614b6a1 --- .../android/bluetooth/BluetoothAdapter.java | 19 ++++++++++++++++++- .../java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 755884c2702..8d4c3afe63c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -518,7 +518,24 @@ public final class BluetoothAdapter { */ public boolean disable() { try { - return mService.disable(); + return mService.disable(true); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Turn off the local Bluetooth adapter and don't persist the setting. + * + *

    Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission + * + * @return true to indicate adapter shutdown has begun, or false on + * immediate error + * @hide + */ + public boolean disable(boolean persist) { + try { + return mService.disable(persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 21f2ae33212..c59c62caf63 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -34,7 +34,7 @@ interface IBluetooth boolean isEnabled(); int getState(); boolean enable(); - boolean disable(); + boolean disable(boolean persist); String getAddress(); ParcelUuid[] getUuids(); -- GitLab From f3ee35141f3a0c013652ed80c479f22117ef3b2a Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 16 Feb 2012 16:57:18 -0800 Subject: [PATCH 0218/1408] Framework changes for HFP and A2DP profile implementation of the new stack. Add IBluetoothHeadsetPhone.aidl for a small service in Phone to get phone state changes Change-Id: I1015e4a69720c4e9cd18ae4236ccd0dbff2e1b2c --- .../java/android/bluetooth/BluetoothA2dp.java | 121 +++--------- .../android/bluetooth/BluetoothHeadset.java | 174 +++++++++++------- .../android/bluetooth/IBluetoothA2dp.aidl | 8 - .../android/bluetooth/IBluetoothHeadset.aidl | 12 +- .../bluetooth/IBluetoothHeadsetPhone.aidl | 26 +++ 5 files changed, 165 insertions(+), 176 deletions(-) create mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c8e60f1504d..c59a5aa85a7 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -18,11 +18,13 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; @@ -42,7 +44,7 @@ import java.util.List; */ public final class BluetoothA2dp implements BluetoothProfile { private static final String TAG = "BluetoothA2dp"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Intent used to broadcast the change in connection state of the A2DP @@ -101,6 +103,7 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public static final int STATE_NOT_PLAYING = 11; + private Context mContext; private ServiceListener mServiceListener; private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; @@ -110,22 +113,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * Bluetooth A2DP service. * */ - /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) { - //TODO(BT): Fix this - IBinder b = null; + /*package*/ BluetoothA2dp(Context context, ServiceListener l) { + mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (b != null) { - mService = IBluetoothA2dp.Stub.asInterface(b); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this); - } - } else { - Log.w(TAG, "Bluetooth A2DP service not available!"); - - // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later when it is actually used. - mService = null; + if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); } } @@ -346,67 +339,6 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - /** - * Initiate suspend from an A2DP sink. - * - *

    This API will return false in scenarios like the A2DP - * device is not in connected state etc. When this API returns, - * true, it is guaranteed that {@link #ACTION_CONNECTION_STATE_CHANGED} - * intent will be broadcasted with the state. Users can get the - * state of the A2DP device from this intent. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Remote A2DP sink - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean suspendSink(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { - return mService.suspendSink(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - - /** - * Initiate resume from a suspended A2DP sink. - * - *

    This API will return false in scenarios like the A2DP - * device is not in suspended state etc. When this API returns, - * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED} - * intent will be broadcasted with the state. Users can get the - * state of the A2DP device from this intent. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * - * @param device Remote A2DP sink - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean resumeSink(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { - return mService.resumeSink(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - /** * This function checks if the remote device is an AVCRP * target and thus whether we should send volume keys @@ -427,23 +359,6 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - /** - * Allow or disallow incoming connection - * @param device Sink - * @param value True / False - * @return Success or Failure of the binder call. - * @hide - */ - public boolean allowIncomingConnect(BluetoothDevice device, boolean value) { - if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")"); - try { - return mService.allowIncomingConnect(device, value); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - /** * Helper for converting a state to a string. * @@ -469,6 +384,24 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothA2dp.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + } + } + }; + private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 2bbf008a769..adf07b4ffb5 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -561,25 +561,6 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_sco_off_call); } - /** - * Cancel the outgoing connection. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - public boolean cancelConnectThread() { - if (DBG) log("cancelConnectThread"); - if (mService != null && isEnabled()) { - try { - return mService.cancelConnectThread(); - } 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; - } - /** * Accept the incoming connection. * Note: This is an internal function and shouldn't be exposed @@ -599,25 +580,6 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - /** - * Create the connect thread for the incoming connection. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - public boolean createIncomingConnect(BluetoothDevice device) { - if (DBG) log("createIncomingConnect"); - if (mService != null && isEnabled()) { - try { - return mService.createIncomingConnect(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; - } - /** * Reject the incoming connection. * @hide @@ -636,55 +598,63 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Connect to a Bluetooth Headset. + * Get the current audio state of the Headset. * Note: This is an internal function and shouldn't be exposed * * @hide */ - public boolean connectHeadsetInternal(BluetoothDevice device) { - if (DBG) log("connectHeadsetInternal"); - if (mService != null && isEnabled()) { + public int getAudioState(BluetoothDevice device) { + if (DBG) log("getAudioState"); + if (mService != null && !isDisabled()) { try { - return mService.connectHeadsetInternal(device); + return mService.getAudioState(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; + return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; } /** - * Disconnect a Bluetooth Headset. - * Note: This is an internal function and shouldn't be exposed + * Check if Bluetooth SCO audio is connected. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + * @return true if SCO is connected, + * false otherwise or on error * @hide */ - public boolean disconnectHeadsetInternal(BluetoothDevice device) { - if (DBG) log("disconnectHeadsetInternal"); - if (mService != null && !isDisabled()) { + public boolean isAudioOn() { + if (DBG) log("isAudioOn()"); + if (mService != null && isEnabled()) { try { - return mService.disconnectHeadsetInternal(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 mService.isAudioOn(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; + } /** - * Set the audio state of the Headset. - * Note: This is an internal function and shouldn't be exposed + * Initiates a connection of headset audio. + * It setup SCO channel with remote connected headset device. * + * @return true if successful + * false if there was some error such as + * there is no connected headset * @hide */ - public boolean setAudioState(BluetoothDevice device, int state) { - if (DBG) log("setAudioState"); - if (mService != null && !isDisabled()) { + public boolean connectAudio() { + if (mService != null && isEnabled()) { try { - return mService.setAudioState(device, state); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return mService.connectAudio(); + } 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())); @@ -693,22 +663,26 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Get the current audio state of the Headset. - * Note: This is an internal function and shouldn't be exposed + * Initiates a disconnection of headset audio. + * It tears down the SCO channel from remote headset device. * + * @return true if successful + * false if there was some error such as + * there is no connected SCO channel * @hide */ - public int getAudioState(BluetoothDevice device) { - if (DBG) log("getAudioState"); - if (mService != null && !isDisabled()) { + public boolean disconnectAudio() { + if (mService != null && isEnabled()) { try { - return mService.getAudioState(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return mService.disconnectAudio(); + } 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 BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + return false; } /** @@ -760,6 +734,68 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Notify Headset of phone state change. + * This is a backdoor for phone app to call BluetoothHeadset since + * there is currently not a good way to get precise call state change outside + * of phone app. + * + * @hide + */ + public void phoneStateChanged(int numActive, int numHeld, int callState, String number, + int type) { + if (mService != null && isEnabled()) { + try { + mService.phoneStateChanged(numActive, numHeld, callState, number, type); + } 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())); + } + } + + /** + * Notify Headset of phone roam state change. + * This is a backdoor for phone app to call BluetoothHeadset since + * there is currently not a good way to get roaming state change outside + * of phone app. + * + * @hide + */ + public void roamChanged(boolean roaming) { + if (mService != null && isEnabled()) { + try { + mService.roamChanged(roaming); + } 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())); + } + } + + /** + * Send Headset of CLCC response + * + * @hide + */ + public void clccResponse(int index, int direction, int status, int mode, boolean mpty, + String number, int type) { + if (mService != null && isEnabled()) { + try { + mService.clccResponse(index, direction, status, mode, mpty, number, type); + } 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())); + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 444dd1e95bc..1f1099868e6 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -33,12 +33,4 @@ interface IBluetoothA2dp { boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); boolean isA2dpPlaying(in BluetoothDevice device); - - // Internal APIs - boolean suspendSink(in BluetoothDevice device); - boolean resumeSink(in BluetoothDevice device); - boolean connectSinkInternal(in BluetoothDevice device); - boolean disconnectSinkInternal(in BluetoothDevice device); - boolean allowIncomingConnect(in BluetoothDevice device, boolean value); - } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index ec005275bbc..fc7627ae5c8 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -40,15 +40,17 @@ interface IBluetoothHeadset { int getBatteryUsageHint(in BluetoothDevice device); // Internal functions, not be made public - boolean createIncomingConnect(in BluetoothDevice device); boolean acceptIncomingConnect(in BluetoothDevice device); boolean rejectIncomingConnect(in BluetoothDevice device); - boolean cancelConnectThread(); - boolean connectHeadsetInternal(in BluetoothDevice device); - boolean disconnectHeadsetInternal(in BluetoothDevice device); - boolean setAudioState(in BluetoothDevice device, int state); int getAudioState(in BluetoothDevice device); + boolean isAudioOn(); + boolean connectAudio(); + boolean disconnectAudio(); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); + void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); + void roamChanged(boolean roam); + void clccResponse(int index, int direction, int status, int mode, boolean mpty, + String number, int type); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl new file mode 100644 index 00000000000..ab9dafe2025 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 Google Inc. + */ + +package android.bluetooth; + +/** + * API for Bluetooth Headset Phone Service in phone app + * + * {@hide} + */ +interface IBluetoothHeadsetPhone { + // Internal functions, not be made public + boolean answerCall(); + boolean hangupCall(); + boolean sendDtmf(int dtmf); + boolean processChld(int chld); + String getNetworkOperator(); + String getSubscriberNumber(); + boolean listCurrentCalls(); + + // Internal for phone app to call + void updateBtHandsfreeAfterRadioTechnologyChange(); + void cdmaSwapSecondCallState(); + void cdmaSetSecondCallState(boolean state); +} -- GitLab From 63d480914af08ed26e29451adfd00a75182627de Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 8 Mar 2012 15:20:04 -0800 Subject: [PATCH 0219/1408] add queryPhoneState in IBluetoothHeadsetPhone Change-Id: I10a9237518f04c1453d09f9a10b6ce0e6107056a --- framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl index ab9dafe2025..163e4e237f3 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl @@ -18,6 +18,7 @@ interface IBluetoothHeadsetPhone { String getNetworkOperator(); String getSubscriberNumber(); boolean listCurrentCalls(); + boolean queryPhoneState(); // Internal for phone app to call void updateBtHandsfreeAfterRadioTechnologyChange(); -- GitLab From 7e8de88cfb8b48d9e80d6e0c765bc42f8732fd5d Mon Sep 17 00:00:00 2001 From: Priti Aghera Date: Tue, 13 Mar 2012 10:41:41 -0700 Subject: [PATCH 0220/1408] Added support to cancel pairing from UI while doing SSP. The new code sends SSPReply with one of the arguments as false. Signed-off-by: Priti Aghera --- framework/java/android/bluetooth/BluetoothDevice.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothDevice.java diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java old mode 100644 new mode 100755 index bd089ff6dac..91984adf3ad --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -885,11 +885,9 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean cancelPairingUserInput() { - // TODO(BT) - /* try { - return sService.cancelPairingUserInput(this); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ + return sService.setPairingConfirmation(this, false); + } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } -- GitLab From 5e9fb033b987e9a115299b5af20d2094e306b97f Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 21 Mar 2012 23:15:06 -0700 Subject: [PATCH 0221/1408] Initial implementation of HID, add IBluetoothInputDevice idl Change-Id: Iadc79a425b4b6e12329d86dd2ac0782adcb0174d --- .../android/bluetooth/BluetoothHeadset.java | 2 +- .../bluetooth/BluetoothInputDevice.java | 105 +++++++++--------- .../bluetooth/IBluetoothInputDevice.aidl | 22 ++++ 3 files changed, 75 insertions(+), 54 deletions(-) create mode 100644 framework/java/android/bluetooth/IBluetoothInputDevice.aidl diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index adf07b4ffb5..7797d94793c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -219,7 +219,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private IBluetoothHeadset mService; - BluetoothAdapter mAdapter; + private BluetoothAdapter mAdapter; /** * Create a BluetoothHeadset proxy object. diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 62c7bdec972..199cc7a019d 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -18,10 +18,12 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; @@ -91,34 +93,32 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final int INPUT_OPERATION_SUCCESS = 5004; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetooth mService; + private IBluetoothInputDevice mService; /** * Create a BluetoothInputDevice proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile * */ - /*package*/ BluetoothInputDevice(Context mContext, ServiceListener l) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + /*package*/ BluetoothInputDevice(Context context, ServiceListener l) { + mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (b != null) { - mService = IBluetooth.Stub.asInterface(b); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, this); - } - } else { - Log.w(TAG, "Bluetooth Service not available!"); - - // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later when it is actually used. - mService = null; + if (!context.bindService(new Intent(IBluetoothInputDevice.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth HID Service"); } } /*package*/ void close() { + if (DBG) log("close()"); + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } mServiceListener = null; } @@ -144,16 +144,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - //TODO(BT) - /* + if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.connectInputDevice(device); + return mService.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -187,16 +184,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - //TODO(BT) - /* + if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnectInputDevice(device); + return mService.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -208,14 +202,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { - //TODO(BT) - /* try { - return mService.getConnectedInputDevices(); + return mService.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -227,14 +219,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { - //TODO(BT) - /* try { - return mService.getInputDevicesMatchingConnectionStates(states); + return mService.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -245,16 +235,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - //TODO(BT) - /* + if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.getInputDeviceConnectionState(device); + return mService.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -277,20 +264,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - //TODO(BT) - /* try { - return mService.setInputDevicePriority(device, priority); + return mService.setPriority(device, priority); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -311,21 +295,36 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (DBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - //TODO(BT) - /* + if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.getInputDevicePriority(device); + return mService.getPriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothInputDevice.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE); + } + } + }; + private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl new file mode 100644 index 00000000000..df061dbbf45 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Google Inc. + */ +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth HID service + * + * {@hide} + */ +interface IBluetoothInputDevice { + // Public API + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); +} -- GitLab From 789124988d485453d3c185f9eda35be81d6d5f7b Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 22 Mar 2012 17:18:37 -0700 Subject: [PATCH 0222/1408] Initial Health Profile implementation Change-Id: I22f35073ceb131d84df6b233d1b63d20fa1b4451 --- .../android/bluetooth/BluetoothHeadset.java | 2 +- .../android/bluetooth/BluetoothHealth.java | 82 +++++++++---------- .../java/android/bluetooth/IBluetooth.aidl | 2 - .../android/bluetooth/IBluetoothHealth.aidl | 30 +++++++ 4 files changed, 72 insertions(+), 44 deletions(-) create mode 100644 framework/java/android/bluetooth/IBluetoothHealth.aidl diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 7797d94793c..2df33a63894 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -239,7 +239,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * results once close() has been called. Multiple invocations of close() * are ok. */ - /*package*/ synchronized void close() { + /*package*/ void close() { if (DBG) log("close()"); if (mConnection != null) { mContext.unbindService(mConnection); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index f4aee98af25..5dad291c1a5 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -16,7 +16,10 @@ package android.bluetooth; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -146,13 +149,11 @@ public final class BluetoothHealth implements BluetoothProfile { new BluetoothHealthAppConfiguration(name, dataType, role, channelType); if (mService != null) { - //TODO(BT - /* try { result = mService.registerAppConfiguration(config, wrapper); } 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())); @@ -172,13 +173,11 @@ public final class BluetoothHealth implements BluetoothProfile { public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; if (mService != null && isEnabled() && config != null) { - //TODO(BT - /* try { result = mService.unregisterAppConfiguration(config); } 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())); @@ -203,13 +202,11 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { - //TODO(BT - /* try { return mService.connectChannelToSource(device, config); } 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())); @@ -234,13 +231,11 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config, int channelType) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { - //TODO(BT - /* try { return mService.connectChannelToSink(device, config, channelType); } 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())); @@ -265,13 +260,11 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config, int channelId) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { - //TODO(BT - /* try { return mService.disconnectChannel(device, config, channelId); } 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())); @@ -296,13 +289,11 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { - //TODO(BT - /* try { return mService.getMainChannelFd(device, config); } 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())); @@ -328,13 +319,11 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (mService != null && isEnabled() && isValidDevice(device)) { - //TODO(BT - /* try { return mService.getHealthDeviceConnectionState(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())); @@ -358,14 +347,12 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public List getConnectedDevices() { if (mService != null && isEnabled()) { - //TODO(BT - /* try { return mService.getConnectedHealthDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -392,14 +379,12 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (mService != null && isEnabled()) { - //TODO(BT - /* try { return mService.getHealthDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -445,35 +430,50 @@ public final class BluetoothHealth implements BluetoothProfile { /** Health App Configuration un-registration failure */ public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; + private Context mContext; private ServiceListener mServiceListener; - private IBluetooth mService; + private IBluetoothHealth mService; BluetoothAdapter mAdapter; /** * Create a BluetoothHealth proxy object. */ - /*package*/ BluetoothHealth(Context mContext, ServiceListener l) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + /*package*/ BluetoothHealth(Context context, ServiceListener l) { + mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (b != null) { - mService = IBluetooth.Stub.asInterface(b); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, this); - } - } else { - Log.w(TAG, "Bluetooth Service not available!"); - - // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later when it is actually used. - mService = null; + if (!context.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Health Service"); } } /*package*/ void close() { + if (DBG) log("close()"); + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } mServiceListener = null; } + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothHealth.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH); + } + } + }; + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index c59c62caf63..80a3d61d155 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -18,9 +18,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothStateChangeCallback; -import android.bluetooth.IBluetoothHealthCallback; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; diff --git a/framework/java/android/bluetooth/IBluetoothHealth.aidl b/framework/java/android/bluetooth/IBluetoothHealth.aidl new file mode 100644 index 00000000000..e741da475df --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHealth.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Google Inc. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHealthAppConfiguration; +import android.bluetooth.IBluetoothHealthCallback; +import android.os.ParcelFileDescriptor; + +/** + * API for Bluetooth Health service + * + * {@hide} + */ +interface IBluetoothHealth +{ + boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, + in IBluetoothHealthCallback callback); + boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); + boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); + boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, + int channelType); + boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); + ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); + List getConnectedHealthDevices(); + List getHealthDevicesMatchingConnectionStates(in int[] states); + int getHealthDeviceConnectionState(in BluetoothDevice device); +} -- GitLab From fab62db7cf9962e90bfe30b85df57b6c621b7310 Mon Sep 17 00:00:00 2001 From: zzy Date: Tue, 3 Apr 2012 19:48:32 -0700 Subject: [PATCH 0223/1408] Added new rfcomm multi accept code --- .../android/bluetooth/BluetoothAdapter.java | 149 +----- .../bluetooth/BluetoothServerSocket.java | 22 +- .../android/bluetooth/BluetoothSocket.java | 501 ++++++++++-------- .../java/android/bluetooth/IBluetooth.aidl | 4 + 4 files changed, 333 insertions(+), 343 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8d4c3afe63c..c4595a16c45 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -847,51 +847,6 @@ public final class BluetoothAdapter { return BluetoothProfile.STATE_DISCONNECTED; } - /** - /** - * Picks RFCOMM channels until none are left. - * Avoids reserved channels. - */ - private static class RfcommChannelPicker { - private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { - 10, // HFAG - 11, // HSAG - 12, // OPUSH - 19, // PBAP - }; - private static LinkedList sChannels; // master list of non-reserved channels - private static Random sRandom; - - private final LinkedList mChannels; // local list of channels left to try - - private final UUID mUuid; - - public RfcommChannelPicker(UUID uuid) { - synchronized (RfcommChannelPicker.class) { - if (sChannels == null) { - // lazy initialization of non-reserved rfcomm channels - sChannels = new LinkedList(); - for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) { - sChannels.addLast(new Integer(i)); - } - for (int reserved : RESERVED_RFCOMM_CHANNELS) { - sChannels.remove(new Integer(reserved)); - } - sRandom = new Random(); - } - mChannels = (LinkedList)sChannels.clone(); - } - mUuid = uuid; - } - /* Returns next random channel, or -1 if we're out */ - public int nextChannel() { - if (mChannels.size() == 0) { - return -1; - } - return mChannels.remove(sRandom.nextInt(mChannels.size())); - } - } - /** * Create a listening, secure RFCOMM Bluetooth socket. *

    A remote device connecting to this socket will be authenticated and @@ -911,10 +866,10 @@ public final class BluetoothAdapter { BluetoothSocket.TYPE_RFCOMM, true, true, channel); int errno = socket.mSocket.bindListen(); if (errno != 0) { - try { - socket.close(); - } catch (IOException e) {} - socket.mSocket.throwErrnoNative(errno); + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); } return socket; } @@ -1015,72 +970,23 @@ public final class BluetoothAdapter { return createNewRfcommSocketAndRecord(name, uuid, false, true); } + private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { - RfcommChannelPicker picker = new RfcommChannelPicker(uuid); BluetoothServerSocket socket; - int channel; - int errno; - while (true) { - channel = picker.nextChannel(); - - if (channel == -1) { - throw new IOException("No available channels"); - } - - socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, auth, encrypt, channel); - errno = socket.mSocket.bindListen(); - if (errno == 0) { - if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); - break; // success - } else if (errno == BluetoothSocket.EADDRINUSE) { - if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use"); - try { - socket.close(); - } catch (IOException e) {} - continue; // try another channel - } else { - try { - socket.close(); - } catch (IOException e) {} - socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() - } - } - - int handle = -1; - //TODO(BT): - /*try { - handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel, - new Binder()); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - if (handle == -1) { - try { - socket.close(); - } catch (IOException e) {} - throw new IOException("Not able to register SDP record for " + name); - } - - if (mServiceRecordHandler == null) { - mServiceRecordHandler = new Handler(Looper.getMainLooper()) { - public void handleMessage(Message msg) { - /* handle socket closing */ - int handle = msg.what; - // TODO(BT): - /* - try { - if (DBG) Log.d(TAG, "Removing service record " + - Integer.toHexString(handle)); - } catch (RemoteException e) {Log.e(TAG, "", e);} - */ - } - }; + socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, + encrypt, new ParcelUuid(uuid)); + socket.setServiceName(name); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); } - socket.setCloseHandler(mServiceRecordHandler, handle); return socket; } - /** * Construct an unencrypted, unauthenticated, RFCOMM server socket. * Call #accept to retrieve connections to this socket. @@ -1094,10 +1000,10 @@ public final class BluetoothAdapter { BluetoothSocket.TYPE_RFCOMM, false, false, port); int errno = socket.mSocket.bindListen(); if (errno != 0) { - try { - socket.close(); - } catch (IOException e) {} - socket.mSocket.throwErrnoNative(errno); + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); } return socket; } @@ -1115,11 +1021,11 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, true, port); int errno = socket.mSocket.bindListen(); - if (errno != 0) { - try { - socket.close(); - } catch (IOException e) {} - socket.mSocket.throwErrnoNative(errno); + if (errno < 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); } return socket; } @@ -1136,11 +1042,10 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_SCO, false, false, -1); int errno = socket.mSocket.bindListen(); - if (errno != 0) { - try { - socket.close(); - } catch (IOException e) {} - socket.mSocket.throwErrnoNative(errno); + if (errno < 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); } return socket; } diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 4021f7b61af..96be8a2fb67 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -17,6 +17,8 @@ package android.bluetooth; import android.os.Handler; +import android.os.Message; +import android.os.ParcelUuid; import java.io.Closeable; import java.io.IOException; @@ -85,6 +87,22 @@ public final class BluetoothServerSocket implements Closeable { mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); } + /** + * Construct a socket for incoming connections. + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param uuid uuid + * @throws IOException On error, for example Bluetooth not available, or + * insufficient privileges + */ + /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid) + throws IOException { + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid); + mChannel = mSocket.getPort(); + } + + /** * Block until a connection is established. *

    Returns a connected {@link BluetoothSocket} on successful connection. @@ -133,7 +151,9 @@ public final class BluetoothServerSocket implements Closeable { mHandler = handler; mMessage = message; } - + /*package*/ void setServiceName(String ServiceName) { + mSocket.setServiceName(ServiceName); + } /** * Returns the channel on which this socket is bound. * @hide diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 2662f391694..9b0e1cb2c13 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -1,32 +1,27 @@ /* - * Copyright (C) 2009 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. + * Copyright (C) 2012 Google Inc. */ package android.bluetooth; -import android.bluetooth.IBluetoothCallback; +import android.os.IBinder; import android.os.ParcelUuid; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.locks.ReentrantReadWriteLock; - +import java.util.List; +import android.net.LocalSocket; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; /** * A connected or connecting Bluetooth socket. * @@ -89,31 +84,41 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int EBADFD = 77; /*package*/ static final int EADDRINUSE = 98; + /*package*/ static final int SEC_FLAG_ENCRYPT = 1; + /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; + private final int mType; /* one of TYPE_RFCOMM etc */ - private final BluetoothDevice mDevice; /* remote device */ - private final String mAddress; /* remote address */ + private BluetoothDevice mDevice; /* remote device */ + private String mAddress; /* remote address */ private final boolean mAuth; private final boolean mEncrypt; private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; - private final SdpHelper mSdp; - + private final ParcelUuid mUuid; + private ParcelFileDescriptor mPfd; + private LocalSocket mSocket; + private InputStream mSocketIS; + private OutputStream mSocketOS; private int mPort; /* RFCOMM channel or L2CAP psm */ + private int mFd; + private String mServiceName; + private static IBluetooth sBluetoothProxy; + private static int PROXY_CONNECTION_TIMEOUT = 5000; + + private static int SOCK_SIGNAL_SIZE = 16; private enum SocketState { INIT, CONNECTED, - CLOSED + LISTENING, + CLOSED, } /** prevents all native calls after destroyNative() */ - private SocketState mSocketState; + private volatile SocketState mSocketState; /** protects mSocketState */ - private final ReentrantReadWriteLock mLock; - - /** used by native code only */ - private int mSocketData; + //private final ReentrantReadWriteLock mLock; /** * Construct a BluetoothSocket. @@ -134,36 +139,63 @@ public final class BluetoothSocket implements Closeable { throw new IOException("Invalid RFCOMM channel: " + port); } } - if (uuid == null) { - mPort = port; - mSdp = null; - } else { - mSdp = new SdpHelper(device, uuid); - mPort = -1; - } + mUuid = uuid; mType = type; mAuth = auth; mEncrypt = encrypt; mDevice = device; + mPort = port; + mFd = fd; + + mSocketState = SocketState.INIT; + if (device == null) { - mAddress = null; + // Server socket + mAddress = BluetoothAdapter.getDefaultAdapter().getAddress(); } else { + // Remote socket mAddress = device.getAddress(); } + mInputStream = new BluetoothInputStream(this); + mOutputStream = new BluetoothOutputStream(this); - //TODO(BT) - /* - if (fd == -1) { - initSocketNative(); - } else { - initSocketFromFdNative(fd); - }*/ + if (sBluetoothProxy == null) { + synchronized (BluetoothSocket.class) { + if (sBluetoothProxy == null) { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + if (b == null) + throw new RuntimeException("Bluetooth service not available"); + sBluetoothProxy = IBluetooth.Stub.asInterface(b); + } + } + } + } + private BluetoothSocket(BluetoothSocket s) { + mUuid = s.mUuid; + mType = s.mType; + mAuth = s.mAuth; + mEncrypt = s.mEncrypt; + mPort = s.mPort; mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); - mSocketState = SocketState.INIT; - mLock = new ReentrantReadWriteLock(); + mServiceName = s.mServiceName; + } + private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { + BluetoothSocket as = new BluetoothSocket(this); + as.mSocketState = SocketState.CONNECTED; + FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); + Log.d(TAG, "socket fd passed by stack fds: " + fds); + if(fds == null || fds.length != 1) { + Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); + throw new IOException("bt socket acept failed"); + } + as.mSocket = new LocalSocket(fds[0]); + as.mSocketIS = as.mSocket.getInputStream(); + as.mSocketOS = as.mSocket.getOutputStream(); + as.mAddress = RemoteAddr; + as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); + return as; } - /** * Construct a BluetoothSocket from address. Used by native code. * @param type type of socket @@ -189,71 +221,13 @@ public final class BluetoothSocket implements Closeable { super.finalize(); } } - - /** - * Attempt to connect to a remote device. - *

    This method will block until a connection is made or the connection - * fails. If this method returns without an exception then this socket - * is now connected. - *

    Creating new connections to - * remote Bluetooth devices should not be attempted while device discovery - * is in progress. Device discovery is a heavyweight procedure on the - * Bluetooth adapter and will significantly slow a device connection. - * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing - * discovery. Discovery is not managed by the Activity, - * but is run as a system service, so an application should always call - * {@link BluetoothAdapter#cancelDiscovery()} even if it - * did not directly request a discovery, just to be sure. - *

    {@link #close} can be used to abort this call from another thread. - * @throws IOException on error, for example connection failure - */ - public void connect() throws IOException { - mLock.readLock().lock(); - try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - - if (mSdp != null) { - mPort = mSdp.doSdp(); // blocks - } - - connectNative(); // blocks - mSocketState = SocketState.CONNECTED; - } finally { - mLock.readLock().unlock(); - } - } - - /** - * Immediately close this socket, and release all associated resources. - *

    Causes blocked calls on this socket in other threads to immediately - * throw an IOException. - */ - public void close() throws IOException { - // abort blocking operations on the socket - //TODO(BT) - /* - mLock.readLock().lock(); - try { - if (mSocketState == SocketState.CLOSED) return; - if (mSdp != null) { - mSdp.cancel(); - } - abortNative(); - } finally { - mLock.readLock().unlock(); - } - - // all native calls are guaranteed to immediately return after - // abortNative(), so this lock should immediately acquire - mLock.writeLock().lock(); - try { - if (mSocketState != SocketState.CLOSED) { - mSocketState = SocketState.CLOSED; - destroyNative(); - } - } finally { - mLock.writeLock().unlock(); - }*/ + private int getSecurityFlags() { + int flags = 0; + if(mAuth) + flags |= SEC_FLAG_AUTH; + if(mEncrypt) + flags |= SEC_FLAG_ENCRYPT; + return flags; } /** @@ -293,7 +267,63 @@ public final class BluetoothSocket implements Closeable { * false if not connected */ public boolean isConnected() { - return (mSocketState == SocketState.CONNECTED); + return mSocketState == SocketState.CONNECTED; + } + + /*package*/ void setServiceName(String name) { + mServiceName = name; + } + + /** + * Attempt to connect to a remote device. + *

    This method will block until a connection is made or the connection + * fails. If this method returns without an exception then this socket + * is now connected. + *

    Creating new connections to + * remote Bluetooth devices should not be attempted while device discovery + * is in progress. Device discovery is a heavyweight procedure on the + * Bluetooth adapter and will significantly slow a device connection. + * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing + * discovery. Discovery is not managed by the Activity, + * but is run as a system service, so an application should always call + * {@link BluetoothAdapter#cancelDiscovery()} even if it + * did not directly request a discovery, just to be sure. + *

    {@link #close} can be used to abort this call from another thread. + * @throws IOException on error, for example connection failure + */ + public void connect() throws IOException { + if (mDevice == null) throw new IOException("Connect is called on null device"); + + try { + // TODO(BT) derive flag from auth and encrypt + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + + mPfd = sBluetoothProxy.connectSocket(mDevice, mType, + mUuid, mPort, getSecurityFlags()); + synchronized(this) + { + Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + if (mPfd == null) throw new IOException("bt socket connect failed"); + FileDescriptor fd = mPfd.getFileDescriptor(); + mSocket = new LocalSocket(fd); + mSocketIS = mSocket.getInputStream(); + mSocketOS = mSocket.getOutputStream(); + } + int channel = readInt(mSocketIS); + if (channel <= 0) + throw new IOException("bt socket connect failed"); + mPort = channel; + waitSocketSignal(mSocketIS); + synchronized(this) + { + if (mSocketState == SocketState.CLOSED) + throw new IOException("bt socket closed"); + mSocketState = SocketState.CONNECTED; + } + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } } /** @@ -301,134 +331,165 @@ public final class BluetoothSocket implements Closeable { * so that BluetoothAdapter can check the error code for EADDRINUSE */ /*package*/ int bindListen() { - return -1; - //TODO(BT) - /* - mLock.readLock().lock(); + int ret; + if (mSocketState == SocketState.CLOSED) return EBADFD; try { - if (mSocketState == SocketState.CLOSED) return EBADFD; - return bindListenNative(); - } finally { - mLock.readLock().unlock(); - }*/ - } + mPfd = sBluetoothProxy.createSocketChannel(mType, mServiceName, + mUuid, mPort, getSecurityFlags()); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + // TODO(BT) right error code? + return -1; + } - /*package*/ BluetoothSocket accept(int timeout) throws IOException { - mLock.readLock().lock(); + // read out port number try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + synchronized(this) { + Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if(mSocketState != SocketState.INIT) return EBADFD; + if(mPfd == null) return -1; + FileDescriptor fd = mPfd.getFileDescriptor(); + Log.d(TAG, "bindListen(), new LocalSocket "); + mSocket = new LocalSocket(fd); + Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + mSocketIS = mSocket.getInputStream(); + mSocketOS = mSocket.getOutputStream(); + } + Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); + int channel = readInt(mSocketIS); + synchronized(this) { + if(mSocketState == SocketState.INIT) + mSocketState = SocketState.LISTENING; + } + Log.d(TAG, "channel: " + channel); + if (mPort == -1) { + mPort = channel; + } // else ASSERT(mPort == channel) + ret = 0; + } catch (IOException e) { + Log.e(TAG, "bindListen, fail to get port number, exception: " + e); + return -1; + } + return ret; + } - BluetoothSocket acceptedSocket = acceptNative(timeout); - mSocketState = SocketState.CONNECTED; - return acceptedSocket; - } finally { - mLock.readLock().unlock(); + /*package*/ BluetoothSocket accept(int timeout) throws IOException { + BluetoothSocket acceptedSocket; + if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state"); + // TODO(BT) wait on an incoming connection + String RemoteAddr = waitSocketSignal(mSocketIS); + synchronized(this) + { + if (mSocketState != SocketState.LISTENING) + throw new IOException("bt socket is not in listen state"); + acceptedSocket = acceptSocket(RemoteAddr); + //quick drop the reference of the file handle } + // TODO(BT) rfcomm socket only supports one connection, return this? + // return this; + return acceptedSocket; } /*package*/ int available() throws IOException { - mLock.readLock().lock(); - try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - return availableNative(); - } finally { - mLock.readLock().unlock(); - } + Log.d(TAG, "available: " + mSocketIS); + return mSocketIS.available(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - mLock.readLock().lock(); - try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - return readNative(b, offset, length); - } finally { - mLock.readLock().unlock(); - } + + Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + int ret = mSocketIS.read(b, offset, length); + if(ret < 0) + throw new IOException("bt socket closed, read return: " + ret); + Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - mLock.readLock().lock(); - try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - return writeNative(b, offset, length); - } finally { - mLock.readLock().unlock(); + + Log.d(TAG, "write: " + mSocketOS + " length: " + length); + mSocketOS.write(b, offset, length); + // There is no good way to confirm since the entire process is asynchronous anyway + Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; + } + + @Override + public void close() throws IOException { + Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if(mSocketState == SocketState.CLOSED) + return; + else + { + synchronized(this) + { + if(mSocketState == SocketState.CLOSED) + return; + mSocketState = SocketState.CLOSED; + Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); + if(mSocket != null) { + Log.d(TAG, "Closing mSocket: " + mSocket); + mSocket.shutdownInput(); + mSocket.shutdownOutput(); + mSocket.close(); + mSocket = null; + } + if(mPfd != null) + mPfd.detachFd(); + } } + // TODO(BT) unbind proxy, } - private native void initSocketNative() throws IOException; - private native void initSocketFromFdNative(int fd) throws IOException; - private native void connectNative() throws IOException; - private native int bindListenNative(); - private native BluetoothSocket acceptNative(int timeout) throws IOException; - private native int availableNative() throws IOException; - private native int readNative(byte[] b, int offset, int length) throws IOException; - private native int writeNative(byte[] b, int offset, int length) throws IOException; - private native void abortNative() throws IOException; - private native void destroyNative() throws IOException; - /** - * Throws an IOException for given posix errno. Done natively so we can - * use strerr to convert to string error. - */ - /*package*/ native void throwErrnoNative(int errno) throws IOException; + /*package */ void removeChannel() { + } - /** - * Helper to perform blocking SDP lookup. - */ - private static class SdpHelper extends IBluetoothCallback.Stub { - private final IBluetooth service; - private final ParcelUuid uuid; - private final BluetoothDevice device; - private int channel; - private boolean canceled; - public SdpHelper(BluetoothDevice device, ParcelUuid uuid) { - service = BluetoothDevice.getService(); - this.device = device; - this.uuid = uuid; - canceled = false; - } - /** - * Returns the RFCOMM channel for the UUID, or throws IOException - * on failure. - */ - public synchronized int doSdp() throws IOException { - if (canceled) throw new IOException("Service discovery canceled"); - channel = -1; - - boolean inProgress = false; - //TODO(BT) - /* - try { - inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - - if (!inProgress) throw new IOException("Unable to start Service Discovery"); - - try { - /* 12 second timeout as a precaution - onRfcommChannelFound - * should always occur before the timeout */ - wait(12000); // block - - } catch (InterruptedException e) {} - - if (canceled) throw new IOException("Service discovery canceled"); - if (channel < 1) throw new IOException("Service discovery failed"); - - return channel; - } - /** Object cannot be re-used after calling cancel() */ - public synchronized void cancel() { - if (!canceled) { - canceled = true; - channel = -1; - notifyAll(); // unblock - } - } - public synchronized void onRfcommChannelFound(int channel) { - if (!canceled) { - this.channel = channel; - notifyAll(); // unblock - } + /*package */ int getPort() { + return mPort; + } + private String convertAddr(final byte[] addr) { + return String.format("%02X:%02X:%02X:%02X:%02X:%02X", + addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); + } + private String waitSocketSignal(InputStream is) throws IOException { + byte [] sig = new byte[SOCK_SIGNAL_SIZE]; + int ret = readAll(is, sig); + Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); + ByteBuffer bb = ByteBuffer.wrap(sig); + bb.order(ByteOrder.nativeOrder()); + int size = bb.getShort(); + byte [] addr = new byte[6]; + bb.get(addr); + int channel = bb.getInt(); + int status = bb.getInt(); + String RemoteAddr = convertAddr(addr); + Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + + RemoteAddr + ", channel: " + channel + ", status: " + status); + if(status != 0) + throw new IOException("Connection failure, status: " + status); + return RemoteAddr; + } + private int readAll(InputStream is, byte[] b) throws IOException { + int left = b.length; + while(left > 0) { + int ret = is.read(b, b.length - left, left); + if(ret < 0) + throw new IOException("read failed, socket might closed, read ret: " + ret); + left -= ret; + if(left != 0) + Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + + ", expect size: " + b.length); } + return b.length; + } + + private int readInt(InputStream is) throws IOException { + byte[] ibytes = new byte[4]; + int ret = readAll(is, ibytes); + Log.d(TAG, "inputStream.read ret: " + ret); + ByteBuffer bb = ByteBuffer.wrap(ibytes); + bb.order(ByteOrder.nativeOrder()); + return bb.getInt(); } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 80a3d61d155..e98795440d9 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -73,4 +73,8 @@ interface IBluetooth boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); + + // For Socket + ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); + ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); } -- GitLab From b527e5f44a3a3663c6794eeae95bac1e4b94db9c Mon Sep 17 00:00:00 2001 From: Srikanth Uppala Date: Wed, 4 Apr 2012 03:33:26 -0700 Subject: [PATCH 0224/1408] Fix discoverability timeout issues. (a) implement timeout logic (b) persist 'never timeout' after reboot (c) code cleanup Change-Id: Iacaec4a48a45935a49576c140ea11ea3d0da6361 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothAdapter.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java old mode 100644 new mode 100755 index c4595a16c45..c8227543112 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -670,7 +670,8 @@ public final class BluetoothAdapter { /** @hide */ public boolean setScanMode(int mode) { if (getState() != STATE_ON) return false; - return setScanMode(mode, 120); + /* getDiscoverableTimeout() to use the latest from NV than use 0 */ + return setScanMode(mode, getDiscoverableTimeout()); } /** @hide */ -- GitLab From edc02569dc818ff7982a2dd10b834705fad90c11 Mon Sep 17 00:00:00 2001 From: fredc Date: Fri, 6 Apr 2012 02:08:46 -0700 Subject: [PATCH 0225/1408] Added support to get a remote device's service UUIDs Change-Id: I9aa47ac2bb7377aca268e95fec31c518358b3065 --- framework/java/android/bluetooth/BluetoothDevice.java | 8 +++----- framework/java/android/bluetooth/IBluetooth.aidl | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 91984adf3ad..dc59a7ea0a9 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -829,12 +829,10 @@ public final class BluetoothDevice implements Parcelable { * was started. */ public boolean fetchUuidsWithSdp() { - //TODO(BT) - /* try { - return sService.fetchRemoteUuids(this, null, null); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return false; + return sService.fetchRemoteUuids(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; } /** @hide */ diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index e98795440d9..9653de2fdd3 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -63,9 +63,7 @@ interface IBluetooth boolean setRemoteAlias(in BluetoothDevice device, in String name); int getRemoteClass(in BluetoothDevice device); ParcelUuid[] getRemoteUuids(in BluetoothDevice device); - //TODO(BT) - //boolean fetchRemoteUuids(in BluetoothDevice device, in ParcelUuid uuid, - // in IBluetoothCallback callback); + boolean fetchRemoteUuids(in BluetoothDevice device); boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] -- GitLab From 1bc06c2aa74f07c2f5a5b4a67f58ca751ab93756 Mon Sep 17 00:00:00 2001 From: Priti Aghera Date: Mon, 9 Apr 2012 12:13:17 -0700 Subject: [PATCH 0226/1408] HID-PTS: Included apis' for pts tests Change-Id: I7dc0a893ee86a2560c351a565bd170517f6c6079 --- .../bluetooth/BluetoothInputDevice.java | 240 ++++++++++++++++++ .../bluetooth/IBluetoothInputDevice.aidl | 24 ++ 2 files changed, 264 insertions(+) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothInputDevice.java mode change 100644 => 100755 framework/java/android/bluetooth/IBluetoothInputDevice.aidl diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java old mode 100644 new mode 100755 index 199cc7a019d..478a9d3b692 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; @@ -67,6 +68,22 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PROTOCOL_MODE_CHANGED = + "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; + + + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_VIRTUAL_UNPLUG_STATUS = + "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + + /** * Return codes for the connect and disconnect Bluez / Dbus calls. * @hide @@ -93,6 +110,77 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final int INPUT_OPERATION_SUCCESS = 5004; + /** + * @hide + */ + public static final int PROTOCOL_REPORT_MODE = 0; + + /** + * @hide + */ + public static final int PROTOCOL_BOOT_MODE = 1; + + /** + * @hide + */ + public static final int PROTOCOL_UNSUPPORTED_MODE = 255; + + /* int reportType, int reportType, int bufferSize */ + /** + * @hide + */ + public static final byte REPORT_TYPE_INPUT = 0; + + /** + * @hide + */ + public static final byte REPORT_TYPE_OUTPUT = 1; + + /** + * @hide + */ + public static final byte REPORT_TYPE_FEATURE = 2; + + /** + * @hide + */ + public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0; + + /** + * @hide + */ + public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1; + + /** + * @hide + */ + public static final String EXTRA_PROTOCOL_MODE = "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; + + /** + * @hide + */ + public static final String EXTRA_REPORT_TYPE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; + + /** + * @hide + */ + public static final String EXTRA_REPORT_ID = "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; + + /** + * @hide + */ + public static final String EXTRA_REPORT_BUFFER_SIZE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; + + /** + * @hide + */ + public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT"; + + /** + * @hide + */ + public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -337,6 +425,158 @@ public final class BluetoothInputDevice implements BluetoothProfile { return false; } + + /** + * Initiate virtual unplug for a HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean virtualUnplug(BluetoothDevice device) { + if (DBG) log("virtualUnplug(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.virtualUnplug(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + + } + + /** + * Send Get_Protocol_Mode command to the connected HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + *true otherwise + * @hide + */ + public boolean getProtocolMode(BluetoothDevice device) { + if (DBG) log("getProtocolMode(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getProtocolMode(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Protocol_Mode command to the connected HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { + if (DBG) log("setProtocolMode(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setProtocolMode(device, protocolMode); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Get_Report command to the connected HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param reportType Report type + * @param reportId Report ID + * @param bufferSize Report receiving buffer size + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { + if (DBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getReport(device, reportType, reportId, bufferSize); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Report command to the connected HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param reportType Report type + * @param report Report receiving buffer size + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setReport(BluetoothDevice device, byte reportType, String report) { + if (DBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setReport(device, reportType, report); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Send_Data command to the connected HID input device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param data Data to send + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean sendData(BluetoothDevice device, String report) { + if (DBG) log("sendData(" + device + "), report=" + report); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.sendData(device, report); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl old mode 100644 new mode 100755 index df061dbbf45..23e6d504ccc --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -19,4 +19,28 @@ interface IBluetoothInputDevice { int getConnectionState(in BluetoothDevice device); boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); + /** + * @hide + */ + boolean getProtocolMode(in BluetoothDevice device); + /** + * @hide + */ + boolean virtualUnplug(in BluetoothDevice device); + /** + * @hide + */ + boolean setProtocolMode(in BluetoothDevice device, int protocolMode); + /** + * @hide + */ + boolean getReport(in BluetoothDevice device, byte reportType, byte reportId, int bufferSize); + /** + * @hide + */ + boolean setReport(in BluetoothDevice device, byte reportType, String report); + /** + * @hide + */ + boolean sendData(in BluetoothDevice device, String report); } -- GitLab From a5d5a9c3c2dd28a5bd5a719af0a52f5e5a03f6e1 Mon Sep 17 00:00:00 2001 From: Ravi Nagarajan Date: Fri, 13 Apr 2012 21:18:16 +0530 Subject: [PATCH 0227/1408] Handle cancel bond for both legacy and ssp pairing Change-Id: I7a6db3cd7b5b8e72dca988e753dcba9a8df614c8 --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index dc59a7ea0a9..4d9dd82ee24 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -884,7 +884,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean cancelPairingUserInput() { try { - return sService.setPairingConfirmation(this, false); + return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } -- GitLab From 3c7196448a62414bf1bfcc94947bfcfa2d60278a Mon Sep 17 00:00:00 2001 From: fredc Date: Thu, 12 Apr 2012 00:02:00 -0700 Subject: [PATCH 0228/1408] Non persistent adapter service Change-Id: Ib13d5c77416e58161df0e04d7a15ec0dddbde8b5 Conflicts: core/java/android/bluetooth/BluetoothInputDevice.java Conflicts: core/java/com/android/internal/app/ShutdownThread.java services/java/com/android/server/SystemServer.java Conflicts: services/java/com/android/server/SystemServer.java services/java/com/android/server/pm/ShutdownThread.java --- .../java/android/bluetooth/BluetoothA2dp.java | 63 +++++++- .../android/bluetooth/BluetoothAdapter.java | 147 ++++++++++++++---- .../android/bluetooth/BluetoothDevice.java | 57 ++++++- .../android/bluetooth/BluetoothHeadset.java | 65 +++++++- .../android/bluetooth/BluetoothHealth.java | 63 +++++++- .../bluetooth/BluetoothInputDevice.java | 64 +++++++- .../java/android/bluetooth/BluetoothPan.java | 119 +++++++------- .../android/bluetooth/BluetoothSocket.java | 24 ++- .../BluetoothTetheringDataTracker.java | 100 +++++++++--- .../android/bluetooth/IBluetoothManager.aidl | 28 ++++ .../bluetooth/IBluetoothManagerCallback.aidl | 17 ++ .../java/android/bluetooth/IBluetoothPan.aidl | 22 +++ 12 files changed, 630 insertions(+), 139 deletions(-) create mode 100644 framework/java/android/bluetooth/IBluetoothManager.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothManagerCallback.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothPan.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c59a5aa85a7..bf5f1753dfa 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -108,6 +108,36 @@ public final class BluetoothA2dp implements BluetoothProfile { private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. @@ -117,6 +147,15 @@ public final class BluetoothA2dp implements BluetoothProfile { 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); + } + } + if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); } @@ -124,8 +163,30 @@ public final class BluetoothA2dp implements BluetoothProfile { /*package*/ void close() { mServiceListener = null; + 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); + } + } + } } + public void finalize() { + close(); + } /** * Initiate connection to a profile of the remote bluetooth device. * @@ -260,7 +321,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * Set priority of the profile * *

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

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c8227543112..75ef3dd3aa7 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -73,7 +73,7 @@ import java.util.UUID; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -343,7 +343,7 @@ public final class BluetoothAdapter { public static final int STATE_DISCONNECTING = 3; /** @hide */ - public static final String BLUETOOTH_SERVICE = "bluetooth"; + public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; private static final int ADDRESS_LENGTH = 17; @@ -353,7 +353,8 @@ public final class BluetoothAdapter { */ private static BluetoothAdapter sAdapter; - private final IBluetooth mService; + private final IBluetoothManager mManagerService; + private IBluetooth mService; private Handler mServiceRecordHandler; @@ -367,10 +368,10 @@ public final class BluetoothAdapter { */ public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - IBinder b = ServiceManager.getService("bluetooth"); + IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); if (b != null) { - IBluetooth service = IBluetooth.Stub.asInterface(b); - sAdapter = new BluetoothAdapter(service); + IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b); + sAdapter = new BluetoothAdapter(managerService); } else { Log.e(TAG, "Bluetooth binder is null"); } @@ -381,11 +382,15 @@ public final class BluetoothAdapter { /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ - BluetoothAdapter(IBluetooth service) { - if (service == null) { - throw new IllegalArgumentException("service is null"); + BluetoothAdapter(IBluetoothManager managerService) { + + if (managerService == null) { + throw new IllegalArgumentException("bluetooth manager service is null"); } - mService = service; + try { + mService = managerService.registerAdapter(mManagerCallback); + } catch (RemoteException e) {Log.e(TAG, "", e);} + mManagerService = managerService; mServiceRecordHandler = null; } @@ -402,6 +407,10 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(String address) { + if (mService == null) { + Log.e(TAG, "BT not enabled. Cannot create Remote Device"); + return null; + } return new BluetoothDevice(address); } @@ -433,8 +442,11 @@ public final class BluetoothAdapter { * @return true if the local adapter is turned on */ public boolean isEnabled() { + try { - return mService.isEnabled(); + synchronized(mManagerCallback) { + if (mService != null) return mService.isEnabled(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -451,9 +463,15 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter */ public int getState() { - if (mService == null) return STATE_OFF; try { - return mService.getState(); + synchronized(mManagerCallback) { + if (mService != null) + { + return mService.getState(); + } + // TODO(BT) there might be a small gap during STATE_TURNING_ON that + // mService is null, handle that case + } } catch (RemoteException e) {Log.e(TAG, "", e);} return STATE_OFF; } @@ -486,8 +504,13 @@ public final class BluetoothAdapter { * immediate error */ public boolean enable() { + + boolean enabled = false; try { - return mService.enable(); + enabled = mManagerService.enable(); + if (enabled) { + // TODO(BT) + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -518,7 +541,7 @@ public final class BluetoothAdapter { */ public boolean disable() { try { - return mService.disable(true); + return mManagerService.disable(true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -534,8 +557,9 @@ public final class BluetoothAdapter { * @hide */ public boolean disable(boolean persist) { + try { - return mService.disable(persist); + return mManagerService.disable(persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -549,7 +573,7 @@ public final class BluetoothAdapter { */ public String getAddress() { try { - return mService.getAddress(); + return mManagerService.getAddress(); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -563,7 +587,7 @@ public final class BluetoothAdapter { */ public String getName() { try { - return mService.getName(); + return mManagerService.getName(); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -579,7 +603,9 @@ public final class BluetoothAdapter { public ParcelUuid[] getUuids() { if (getState() != STATE_ON) return null; try { - return mService.getUuids(); + synchronized(mManagerCallback) { + if (mService != null) return mService.getUuids(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -602,7 +628,9 @@ public final class BluetoothAdapter { public boolean setName(String name) { if (getState() != STATE_ON) return false; try { - return mService.setName(name); + synchronized(mManagerCallback) { + if (mService != null) return mService.setName(name); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -626,7 +654,9 @@ public final class BluetoothAdapter { public int getScanMode() { if (getState() != STATE_ON) return SCAN_MODE_NONE; try { - return mService.getScanMode(); + synchronized(mManagerCallback) { + if (mService != null) return mService.getScanMode(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return SCAN_MODE_NONE; } @@ -662,7 +692,9 @@ public final class BluetoothAdapter { public boolean setScanMode(int mode, int duration) { if (getState() != STATE_ON) return false; try { - return mService.setScanMode(mode, duration); + synchronized(mManagerCallback) { + if (mService != null) return mService.setScanMode(mode, duration); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -678,7 +710,9 @@ public final class BluetoothAdapter { public int getDiscoverableTimeout() { if (getState() != STATE_ON) return -1; try { - return mService.getDiscoverableTimeout(); + synchronized(mManagerCallback) { + if (mService != null) return mService.getDiscoverableTimeout(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return -1; } @@ -687,7 +721,9 @@ public final class BluetoothAdapter { public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) return; try { - mService.setDiscoverableTimeout(timeout); + synchronized(mManagerCallback) { + if (mService != null) mService.setDiscoverableTimeout(timeout); + } } catch (RemoteException e) {Log.e(TAG, "", e);} } @@ -724,7 +760,9 @@ public final class BluetoothAdapter { public boolean startDiscovery() { if (getState() != STATE_ON) return false; try { - return mService.startDiscovery(); + synchronized(mManagerCallback) { + if (mService != null) return mService.startDiscovery(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -749,7 +787,9 @@ public final class BluetoothAdapter { public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { - return mService.cancelDiscovery(); + synchronized(mManagerCallback) { + if (mService != null) return mService.cancelDiscovery(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -776,7 +816,9 @@ public final class BluetoothAdapter { public boolean isDiscovering() { if (getState() != STATE_ON) return false; try { - return mService.isDiscovering(); + synchronized(mManagerCallback) { + if (mService != null ) return mService.isDiscovering(); + } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -797,7 +839,10 @@ public final class BluetoothAdapter { return toDeviceSet(new BluetoothDevice[0]); } try { - return toDeviceSet(mService.getBondedDevices()); + synchronized(mManagerCallback) { + if (mService != null) return toDeviceSet(mService.getBondedDevices()); + } + return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -818,7 +863,9 @@ public final class BluetoothAdapter { public int getConnectionState() { if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED; try { - return mService.getAdapterConnectionState(); + synchronized(mManagerCallback) { + if (mService != null) return mService.getAdapterConnectionState(); + } } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);} return BluetoothAdapter.STATE_DISCONNECTED; } @@ -841,7 +888,9 @@ public final class BluetoothAdapter { public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { - return mService.getProfileConnectionState(profile); + synchronized(mManagerCallback) { + if (mService != null) return mService.getProfileConnectionState(profile); + } } catch (RemoteException e) { Log.e(TAG, "getProfileConnectionState:", e); } @@ -1160,6 +1209,23 @@ public final class BluetoothAdapter { } } + final private IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + synchronized (mManagerCallback) { + mService = bluetoothService; + } + } + + public void onBluetoothServiceDown() { + if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + synchronized (mManagerCallback) { + mService = null; + } + } + }; + /** * Enable the Bluetooth Adapter, but don't auto-connect devices * and don't persist state. Only for use by system applications. @@ -1245,6 +1311,17 @@ public final class BluetoothAdapter { return Collections.unmodifiableSet(deviceSet); } + protected void finalize() throws Throwable { + try { + mManagerService.unregisterAdapter(mManagerCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + super.finalize(); + } + } + + /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" *

    Alphabetic characters must be uppercase to be valid. @@ -1275,4 +1352,14 @@ public final class BluetoothAdapter { } return true; } + + /*package*/ IBluetoothManager getBluetoothManager() { + return mManagerService; + } + + /*package*/ IBluetooth getBluetoothService() { + synchronized (mManagerCallback) { + return mService; + } + } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4d9dd82ee24..82bd702bac5 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -65,6 +65,7 @@ import java.util.UUID; */ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; + private static final boolean DBG = true; /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -483,11 +484,8 @@ public final class BluetoothDevice implements Parcelable { /*package*/ static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); - if (b == null) { - throw new RuntimeException("Bluetooth service not available"); - } - sService = IBluetooth.Stub.asInterface(b); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + sService = adapter.getBluetoothService(); } } return sService; @@ -561,6 +559,7 @@ public final class BluetoothDevice implements Parcelable { * @return Bluetooth hardware address as string */ public String getAddress() { + if (DBG) Log.d(TAG, "mAddress: " + mAddress); return mAddress; } @@ -575,6 +574,10 @@ public final class BluetoothDevice implements Parcelable { * @return the Bluetooth name, or null if there was a problem. */ public String getName() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); + return null; + } try { return sService.getRemoteName(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -589,6 +592,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public String getAlias() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias"); + return null; + } try { return sService.getRemoteAlias(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -606,6 +613,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setAlias(String alias) { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); + return false; + } try { return sService.setRemoteAlias(this, alias); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -642,6 +653,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBond() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); + return false; + } try { return sService.createBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -706,6 +721,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean cancelBondProcess() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond"); + return false; + } try { return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -723,6 +742,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean removeBond() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond"); + return false; + } try { return sService.removeBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -740,6 +763,10 @@ public final class BluetoothDevice implements Parcelable { * @return the bond state */ public int getBondState() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get bond state"); + return BOND_NONE; + } try { return sService.getBondState(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -753,6 +780,10 @@ public final class BluetoothDevice implements Parcelable { * @return Bluetooth class object, or null on error */ public BluetoothClass getBluetoothClass() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); + return null; + } try { int classInt = sService.getRemoteClass(this); if (classInt == BluetoothClass.ERROR) return null; @@ -807,6 +838,10 @@ public final class BluetoothDevice implements Parcelable { * or null on error */ public ParcelUuid[] getUuids() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); + return null; + } try { return sService.getRemoteUuids(this); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -847,6 +882,10 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean setPin(byte[] pin) { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); + return false; + } try { return sService.setPin(this, true, pin.length, pin); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -865,6 +904,10 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean setPairingConfirmation(boolean confirm) { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); + return false; + } try { return sService.setPairingConfirmation(this, confirm); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -883,6 +926,10 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean cancelPairingUserInput() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot create pairing user input"); + return false; + } try { return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 2df33a63894..541b69fad2b 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -45,7 +45,7 @@ import java.util.List; */ public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Intent used to broadcast the change in connection state of the Headset @@ -221,6 +221,37 @@ public final class BluetoothHeadset implements BluetoothProfile { private IBluetoothHeadset mService; private BluetoothAdapter mAdapter; + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + /** * Create a BluetoothHeadset proxy object. */ @@ -228,6 +259,16 @@ public final class BluetoothHeadset implements BluetoothProfile { 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); + } + } + if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Headset Service"); } @@ -241,9 +282,25 @@ public final class BluetoothHeadset implements BluetoothProfile { */ /*package*/ void close() { if (DBG) log("close()"); - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + + 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; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 5dad291c1a5..4a0bc7e37a2 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -57,7 +57,7 @@ import java.util.List; */ public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Health Profile Source Role - the health device. @@ -97,6 +97,37 @@ public final class BluetoothHealth implements BluetoothProfile { /** @hide */ public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005; + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Health Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + /** * Register an application configuration that acts as a Health SINK. @@ -442,6 +473,15 @@ public final class BluetoothHealth implements BluetoothProfile { 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); + } + } + if (!context.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Health Service"); } @@ -449,9 +489,24 @@ public final class BluetoothHealth implements BluetoothProfile { /*package*/ void close() { if (DBG) log("close()"); - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + 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; } diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 478a9d3b692..bff966d4d26 100755 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -44,7 +44,7 @@ import java.util.List; */ public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Intent used to broadcast the change in connection state of the Input @@ -186,6 +186,37 @@ public final class BluetoothInputDevice implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothInputDevice mService; + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth HID Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + /** * Create a BluetoothInputDevice proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile @@ -195,6 +226,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { 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); + } + } + if (!context.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth HID Service"); @@ -203,9 +244,24 @@ public final class BluetoothInputDevice implements BluetoothProfile { /*package*/ void close() { if (DBG) log("close()"); - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + 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; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 13526e81fa5..13d9078752a 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -18,7 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -27,7 +30,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; - /** * This class provides the APIs to control the Bluetooth Pan * Profile. @@ -41,7 +43,7 @@ import java.util.List; */ public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; - private static final boolean DBG = false; + private static final boolean DBG = true; /** * Intent used to broadcast the change in connection state of the Pan @@ -76,15 +78,18 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + public static final int PAN_ROLE_NONE = 0; /** * The local device is acting as a Network Access Point. */ public static final int LOCAL_NAP_ROLE = 1; + public static final int REMOTE_NAP_ROLE = 1; /** * The local device is acting as a PAN User. */ public static final int LOCAL_PANU_ROLE = 2; + public static final int REMOTE_PANU_ROLE = 2; /** * Return codes for the connect and disconnect Bluez / Dbus calls. @@ -112,34 +117,34 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetooth mService; + private IBluetoothPan mPanService; /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile * */ - /*package*/ BluetoothPan(Context mContext, ServiceListener l) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); + /*package*/ BluetoothPan(Context context, ServiceListener l) { + mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (b != null) { - mService = IBluetooth.Stub.asInterface(b); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PAN, this); - } - } else { - Log.w(TAG, "Bluetooth Service not available!"); - - // Instead of throwing an exception which prevents people from going - // into Wireless settings in the emulator. Let it crash later when it is actually used. - mService = null; + Log.d(TAG, "BluetoothPan() call bindService"); + if (!context.bindService(new Intent(IBluetoothPan.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth HID Service"); } + Log.d(TAG, "BluetoothPan(), bindService called"); } /*package*/ void close() { + if (DBG) log("close()"); + if (mConnection != null) { + mContext.unbindService(mConnection); + mConnection = null; + } mServiceListener = null; } @@ -163,18 +168,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && + if (mPanService != null && isEnabled() && isValidDevice(device)) { - //TODO(BT - /* try { - return mService.connectPanDevice(device); + return mPanService.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - }*/ + } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -206,18 +209,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && + if (mPanService != null && isEnabled() && isValidDevice(device)) { - //TODO(BT - /* try { - return mService.disconnectPanDevice(device); + return mPanService.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - }*/ + } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -226,17 +227,15 @@ public final class BluetoothPan implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { - //TODO(BT - /* + if (mPanService != null && isEnabled()) { try { - return mService.getConnectedPanDevices(); + return mPanService.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -245,17 +244,15 @@ public final class BluetoothPan implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { - //TODO(BT - /* + if (mPanService != null && isEnabled()) { try { - return mService.getPanDevicesMatchingConnectionStates(states); + return mPanService.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - }*/ + } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -264,45 +261,57 @@ public final class BluetoothPan implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() + if (mPanService != null && isEnabled() && isValidDevice(device)) { - //TODO(BT - /* try { - return mService.getPanDeviceConnectionState(device); + return mPanService.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - }*/ + } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - //TODO(BT - /* try { - mService.setBluetoothTethering(value); + mPanService.setBluetoothTethering(value); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - }*/ + } } public boolean isTetheringOn() { if (DBG) log("isTetheringOn()"); - //TODO(BT - /* try { - return mService.isTetheringOn(); + return mPanService.isTetheringOn(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; - }*/ + } return false; } + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); + mPanService = IBluetoothPan.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.PAN, + BluetoothPan.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); + mPanService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); + } + } + }; + private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 9b0e1cb2c13..d37f2d53840 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -102,7 +102,6 @@ public final class BluetoothSocket implements Closeable { private int mPort; /* RFCOMM channel or L2CAP psm */ private int mFd; private String mServiceName; - private static IBluetooth sBluetoothProxy; private static int PROXY_CONNECTION_TIMEOUT = 5000; private static int SOCK_SIGNAL_SIZE = 16; @@ -158,17 +157,6 @@ public final class BluetoothSocket implements Closeable { } mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); - - if (sBluetoothProxy == null) { - synchronized (BluetoothSocket.class) { - if (sBluetoothProxy == null) { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); - if (b == null) - throw new RuntimeException("Bluetooth service not available"); - sBluetoothProxy = IBluetooth.Stub.asInterface(b); - } - } - } } private BluetoothSocket(BluetoothSocket s) { mUuid = s.mUuid; @@ -297,8 +285,9 @@ public final class BluetoothSocket implements Closeable { try { // TODO(BT) derive flag from auth and encrypt if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - - mPfd = sBluetoothProxy.connectSocket(mDevice, mType, + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); + if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); + mPfd = bluetoothProxy.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); synchronized(this) { @@ -333,8 +322,13 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); + if (bluetoothProxy == null) { + Log.e(TAG, "bindListen fail, reason: bluetooth is off"); + return -1; + } try { - mPfd = sBluetoothProxy.createSocketChannel(mType, mServiceName, + mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName, mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 83d1bda8210..593b6999370 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.os.IBinder; +import android.os.ServiceManager; +import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpInfoInternal; @@ -28,6 +31,11 @@ import android.net.NetworkUtils; import android.os.Handler; import android.os.Message; import android.util.Log; +import java.net.InterfaceAddress; +import android.net.LinkAddress; +import android.net.RouteInfo; +import java.net.Inet4Address; +import android.os.SystemProperties; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -54,7 +62,6 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private NetworkInfo mNetworkInfo; private BluetoothPan mBluetoothPan; - private BluetoothDevice mDevice; private static String mIface; /* For sending events to connectivity service handler */ @@ -92,8 +99,10 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { * Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { + Log.d(TAG, "startMonitoring: target: " + target); mContext = context; mCsHandler = target; + Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); @@ -259,38 +268,87 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return "net.tcp.buffersize.wifi"; } + private static short countPrefixLength(byte [] mask) { + short count = 0; + for (byte b : mask) { + for (int i = 0; i < 8; ++i) { + if ((b & (1 << i)) != 0) { + ++count; + } + } + } + return count; + } + - public synchronized void startReverseTether(String iface, BluetoothDevice device) { + private boolean readLinkProperty(String iface) { + String DhcpPrefix = "dhcp." + iface + "."; + String ip = SystemProperties.get(DhcpPrefix + "ipaddress"); + String dns1 = SystemProperties.get(DhcpPrefix + "dns1"); + String dns2 = SystemProperties.get(DhcpPrefix + "dns2"); + String gateway = SystemProperties.get(DhcpPrefix + "gateway"); + String mask = SystemProperties.get(DhcpPrefix + "mask"); + if(ip.isEmpty() || gateway.isEmpty()) { + Log.e(TAG, "readLinkProperty, ip: " + ip + ", gateway: " + gateway + ", can not be empty"); + return false; + } + int PrefixLen = countPrefixLength(NetworkUtils.numericToInetAddress(mask).getAddress()); + mLinkProperties.addLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(ip), PrefixLen)); + RouteInfo ri = new RouteInfo(NetworkUtils.numericToInetAddress(gateway)); + mLinkProperties.addRoute(ri); + if(!dns1.isEmpty()) + mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns1)); + if(!dns2.isEmpty()) + mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns2)); + mLinkProperties.setInterfaceName(iface); + return true; + } + public synchronized void startReverseTether(String iface) { mIface = iface; - mDevice = device; + Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); Thread dhcpThread = new Thread(new Runnable() { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); - if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) { - Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); - return; + Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + String DhcpResultName = "dhcp." + mIface + ".result";; + String result = ""; + Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); + for(int i = 0; i < 30*5; i++) { + try { Thread.sleep(200); } catch (InterruptedException ie) { } + result = SystemProperties.get(DhcpResultName); + Log.d(TAG, "read " + DhcpResultName + ": " + result); + if(result.equals("failed")) { + Log.e(TAG, "startReverseTether, failed to start dhcp service"); + return; + } + if(result.equals("ok")) { + Log.d(TAG, "startReverseTether, dhcp resut: " + result); + if(readLinkProperty(mIface)) { + + mNetworkInfo.setIsAvailable(true); + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + + Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if(mCsHandler != null) { + Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); + msg.sendToTarget(); + + msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } + } + return; + } } - mLinkProperties = dhcpInfoInternal.makeLinkProperties(); - mLinkProperties.setInterfaceName(mIface); - - mNetworkInfo.setIsAvailable(true); - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - - msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); + Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); dhcpThread.start(); } - public synchronized void stopReverseTether(String iface) { - NetworkUtils.stopDhcp(iface); - + public synchronized void stopReverseTether() { + //NetworkUtils.stopDhcp(iface); mLinkProperties.clear(); mNetworkInfo.setIsAvailable(false); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl new file mode 100644 index 00000000000..f82da82fff1 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Google Inc. + */ + +package android.bluetooth; + +import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothManagerCallback; +import android.bluetooth.IBluetoothStateChangeCallback; + +/** + * System private API for talking with the Bluetooth service. + * + * {@hide} + */ +interface IBluetoothManager +{ + IBluetooth registerAdapter(in IBluetoothManagerCallback callback); + void unregisterAdapter(in IBluetoothManagerCallback callback); + void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); + void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); + boolean isEnabled(); + boolean enable(); + boolean disable(boolean persist); + + String getAddress(); + String getName(); +} diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl new file mode 100644 index 00000000000..3e795eab69e --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012 Google Inc. + */ + +package android.bluetooth; + +import android.bluetooth.IBluetooth; + +/** + * API for Communication between BluetoothAdapter and BluetoothManager + * + * {@hide} + */ +interface IBluetoothManagerCallback { + void onBluetoothServiceUp(in IBluetooth bluetoothService); + void onBluetoothServiceDown(); +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/IBluetoothPan.aidl b/framework/java/android/bluetooth/IBluetoothPan.aidl new file mode 100644 index 00000000000..b91bd7d9c52 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothPan.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Google Inc. + */ +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth Pan service + * + * {@hide} + */ +interface IBluetoothPan { + // Public API + boolean isTetheringOn(); + void setBluetoothTethering(boolean value); + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); +} -- GitLab From 08e6ff77c4f82e97df5e8a8ab060c51b51a561db Mon Sep 17 00:00:00 2001 From: Kausik Sinnaswamy Date: Mon, 16 Apr 2012 16:38:27 +0530 Subject: [PATCH 0229/1408] BT enable adapter API should return TRUE or FALSE. Change-Id: Iaef14c51d3407b69505c55a0bbdaf902213402cf --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 75ef3dd3aa7..bde52eedbc1 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -507,10 +507,7 @@ public final class BluetoothAdapter { boolean enabled = false; try { - enabled = mManagerService.enable(); - if (enabled) { - // TODO(BT) - } + return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } -- GitLab From 95a8d72c03118a11736212380b3497285b7bcf53 Mon Sep 17 00:00:00 2001 From: Sreenidhi T Date: Wed, 18 Apr 2012 04:24:33 -0700 Subject: [PATCH 0230/1408] Change done in BluetoothAdapter to use the same API for interacting with the Bluetooth Service. setName was being done using mService while getName was using mManagerService. As a result, the device name was not being updated in Localadapter getname call. Modified to use mService in both cases. Change-Id: I2e900a782df11fd511319fbb42d94324e50ae89b --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bde52eedbc1..867ab643317 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -584,7 +584,7 @@ public final class BluetoothAdapter { */ public String getName() { try { - return mManagerService.getName(); + return mService.getName(); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } -- GitLab From 50ef8e028d458ce29664d3ec3b3d9a320bade6ff Mon Sep 17 00:00:00 2001 From: fredc Date: Fri, 20 Apr 2012 14:47:08 -0700 Subject: [PATCH 0231/1408] Fixed issue with getting Bluetooth adapter's name and airplane mode Change-Id: I18b632223574aa41b09ba30de8e35417fad86cbe --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 867ab643317..bde52eedbc1 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -584,7 +584,7 @@ public final class BluetoothAdapter { */ public String getName() { try { - return mService.getName(); + return mManagerService.getName(); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } -- GitLab From 797a7ba64e979ce8dff1f87f39521b6e1308e715 Mon Sep 17 00:00:00 2001 From: Ravi Nagarajan Date: Mon, 23 Apr 2012 04:06:49 -0700 Subject: [PATCH 0232/1408] Disable debug logs in BluetoothDevice, that was turned on accidentally Change-Id: I8a0a3f5775c2b9f5a928e4840851e687f25b57ab --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 82bd702bac5..2f7204deea3 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -65,7 +65,7 @@ import java.util.UUID; */ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; - private static final boolean DBG = true; + private static final boolean DBG = false; /** * Sentinel error value for this class. Guaranteed to not equal any other -- GitLab From f1f5dde555c09019c0cedb05e844adf288677bc5 Mon Sep 17 00:00:00 2001 From: fredc Date: Tue, 24 Apr 2012 03:59:57 -0700 Subject: [PATCH 0233/1408] Fixed issue with Settings app crashing after during on/off and unpair. Fixed issue with BluetoothAdapter.getRemoteDevice() returning null. Change-Id: Ie86813532530a6b57bde1c430c7b4875ecc7354c --- .../android/bluetooth/BluetoothAdapter.java | 31 ++++++++++---- .../android/bluetooth/BluetoothDevice.java | 18 ++++++++- .../java/android/bluetooth/BluetoothPan.java | 40 +++++++++++++++++++ .../android/bluetooth/BluetoothSocket.java | 4 +- 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bde52eedbc1..9f50c542761 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -31,6 +31,7 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -407,10 +408,6 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(String address) { - if (mService == null) { - Log.e(TAG, "BT not enabled. Cannot create Remote Device"); - return null; - } return new BluetoothDevice(address); } @@ -504,7 +501,6 @@ public final class BluetoothAdapter { * immediate error */ public boolean enable() { - boolean enabled = false; try { return mManagerService.enable(); @@ -1212,6 +1208,11 @@ public final class BluetoothAdapter { if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); synchronized (mManagerCallback) { mService = bluetoothService; + for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ + try { + cb.onBluetoothServiceUp(bluetoothService); + } catch (Exception e) { Log.e(TAG,"",e);} + } } } @@ -1219,6 +1220,11 @@ public final class BluetoothAdapter { if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ + try { + cb.onBluetoothServiceDown(); + } catch (Exception e) { Log.e(TAG,"",e);} + } } } }; @@ -1354,9 +1360,20 @@ public final class BluetoothAdapter { return mManagerService; } - /*package*/ IBluetooth getBluetoothService() { + private ArrayList mBluetoothManagerCallbackList = new ArrayList(); + //private IBluetoothStateChangeCallback mBluetoothStateChangeCallback; + /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { + synchronized (mManagerCallback) { + if (!mBluetoothManagerCallbackList.contains(cb)) { + mBluetoothManagerCallbackList.add(cb); + } + } + return mService; + } + + /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { synchronized (mManagerCallback) { - return mService; + mBluetoothManagerCallbackList.remove(cb); } } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 2f7204deea3..25047632ce3 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -485,12 +485,28 @@ public final class BluetoothDevice implements Parcelable { synchronized (BluetoothDevice.class) { if (sService == null) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - sService = adapter.getBluetoothService(); + sService = adapter.getBluetoothService(mStateChangeCallback); } } return sService; } + static IBluetoothManagerCallback mStateChangeCallback = new IBluetoothManagerCallback.Stub() { + + public void onBluetoothServiceUp(IBluetooth bluetoothService) + throws RemoteException { + synchronized (BluetoothDevice.class) { + sService = bluetoothService; + } + } + + public void onBluetoothServiceDown() + throws RemoteException { + synchronized (BluetoothDevice.class) { + sService = null; + } + } + }; /** * Create a new BluetoothDevice * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 13d9078752a..cae7a73bcf0 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -131,6 +131,11 @@ public final class BluetoothPan implements BluetoothProfile { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); + try { + mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + } Log.d(TAG, "BluetoothPan() call bindService"); if (!context.bindService(new Intent(IBluetoothPan.class.getName()), mConnection, 0)) { @@ -146,8 +151,43 @@ public final class BluetoothPan implements BluetoothProfile { mConnection = null; } mServiceListener = null; + try { + mAdapter.getBluetoothManager().unregisterStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + } + } + + protected void finalize() { + close(); } + private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { + + @Override + public void onBluetoothStateChange(boolean on) throws RemoteException { + //Handle enable request to bind again. + if (on) { + Log.d(TAG, "onBluetoothStateChange(on) call bindService"); + if (!mContext.bindService(new Intent(IBluetoothPan.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth HID Service"); + } + Log.d(TAG, "BluetoothPan(), bindService called"); + } else { + if (DBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + try { + mPanService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + /** * Initiate connection to a profile of the remote bluetooth device. * diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d37f2d53840..221b1a3c854 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -285,7 +285,7 @@ public final class BluetoothSocket implements Closeable { try { // TODO(BT) derive flag from auth and encrypt if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -322,7 +322,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) { Log.e(TAG, "bindListen fail, reason: bluetooth is off"); return -1; -- GitLab From e8d151968574b4ec435e5b133d07fe67888ca22f Mon Sep 17 00:00:00 2001 From: fredc Date: Wed, 25 Apr 2012 17:46:13 -0700 Subject: [PATCH 0234/1408] Fixed socket not closing on BT off. Used RemoteCallbackList to monitor binder deaths in BluetoothManagerService. Change-Id: I524964bd2836d8c5a4bae095b93ac9481337941d --- .../android/bluetooth/BluetoothAdapter.java | 18 ++++++++++++++---- .../android/bluetooth/BluetoothSocket.java | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9f50c542761..c08705cf7f1 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1210,7 +1210,11 @@ public final class BluetoothAdapter { mService = bluetoothService; for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ try { - cb.onBluetoothServiceUp(bluetoothService); + if (cb != null) { + cb.onBluetoothServiceUp(bluetoothService); + } else { + Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + } } catch (Exception e) { Log.e(TAG,"",e);} } } @@ -1222,7 +1226,11 @@ public final class BluetoothAdapter { mService = null; for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ try { - cb.onBluetoothServiceDown(); + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + } } catch (Exception e) { Log.e(TAG,"",e);} } } @@ -1361,10 +1369,12 @@ public final class BluetoothAdapter { } private ArrayList mBluetoothManagerCallbackList = new ArrayList(); - //private IBluetoothStateChangeCallback mBluetoothStateChangeCallback; + /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { synchronized (mManagerCallback) { - if (!mBluetoothManagerCallbackList.contains(cb)) { + if (cb == null) { + Log.w(TAG, "Unable to register null state change callback", new Exception()); + } else if (!mBluetoothManagerCallbackList.contains(cb)) { mBluetoothManagerCallbackList.add(cb); } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 221b1a3c854..1bc640ff598 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -468,7 +468,7 @@ public final class BluetoothSocket implements Closeable { int left = b.length; while(left > 0) { int ret = is.read(b, b.length - left, left); - if(ret < 0) + if(ret <= 0) throw new IOException("read failed, socket might closed, read ret: " + ret); left -= ret; if(left != 0) -- GitLab From 221c7d29c6b01d3b0ab371d9f1b7db446d16b0a0 Mon Sep 17 00:00:00 2001 From: zzy Date: Fri, 27 Apr 2012 12:15:57 -0700 Subject: [PATCH 0235/1408] Added code to tear down dhcp thread when tethering is off --- .../bluetooth/BluetoothTetheringDataTracker.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 593b6999370..b2b5d81fe3a 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -63,7 +63,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private BluetoothPan mBluetoothPan; private static String mIface; - + private Thread mDhcpThread; /* For sending events to connectivity service handler */ private Handler mCsHandler; private Context mContext; @@ -306,7 +306,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public synchronized void startReverseTether(String iface) { mIface = iface; Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - Thread dhcpThread = new Thread(new Runnable() { + mDhcpThread = new Thread(new Runnable() { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. @@ -315,7 +315,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { String result = ""; Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); for(int i = 0; i < 30*5; i++) { - try { Thread.sleep(200); } catch (InterruptedException ie) { } + try { Thread.sleep(200); } catch (InterruptedException ie) { return;} result = SystemProperties.get(DhcpResultName); Log.d(TAG, "read " + DhcpResultName + ": " + result); if(result.equals("failed")) { @@ -344,11 +344,15 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); - dhcpThread.start(); + mDhcpThread.start(); } public synchronized void stopReverseTether() { //NetworkUtils.stopDhcp(iface); + if(mDhcpThread != null && mDhcpThread.isAlive()) { + mDhcpThread.interrupt(); + try { mDhcpThread.join(); } catch (InterruptedException ie) { return; } + } mLinkProperties.clear(); mNetworkInfo.setIsAvailable(false); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); -- GitLab From 164574516933c8810792d1db48483dbcda74be00 Mon Sep 17 00:00:00 2001 From: Swaminatha Balaji Date: Fri, 27 Apr 2012 05:28:17 -0700 Subject: [PATCH 0236/1408] Auto connect hf/a2dp Change-Id: I7c1fb4e2614f65e6810f195814dcf0cad88aee21 --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- framework/java/android/bluetooth/BluetoothHeadset.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothA2dp.java mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothHeadset.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java old mode 100644 new mode 100755 index bf5f1753dfa..74f634b19a5 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -337,7 +337,8 @@ public final class BluetoothA2dp implements BluetoothProfile { if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + priority != BluetoothProfile.PRIORITY_ON && + priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { return false; } try { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java old mode 100644 new mode 100755 index 541b69fad2b..75dfe9dbb4c --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -455,7 +455,8 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + priority != BluetoothProfile.PRIORITY_ON && + priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { return false; } try { -- GitLab From d07368a662080b25ab357c6ffbe57ea030d00a56 Mon Sep 17 00:00:00 2001 From: fredc Date: Wed, 9 May 2012 16:52:50 -0700 Subject: [PATCH 0237/1408] Moved BluetoothAdapter.ACTION_STATE_CHANGED broadcast from AdapterService to BluetoothManagerService Change-Id: I88e5f3fe050cf11eae9c5cf1b7c393a178b8f9b1 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++-------- .../java/android/bluetooth/IBluetooth.aidl | 3 +++ .../android/bluetooth/IBluetoothCallback.aidl | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c08705cf7f1..dc1802929a8 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -464,12 +464,15 @@ public final class BluetoothAdapter { synchronized(mManagerCallback) { if (mService != null) { - return mService.getState(); + int state= mService.getState(); + if (DBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + return state; } // TODO(BT) there might be a small gap during STATE_TURNING_ON that // mService is null, handle that case } } catch (RemoteException e) {Log.e(TAG, "", e);} + if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF"); return STATE_OFF; } @@ -1208,7 +1211,7 @@ public final class BluetoothAdapter { if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); synchronized (mManagerCallback) { mService = bluetoothService; - for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -1224,7 +1227,7 @@ public final class BluetoothAdapter { if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; - for (IBluetoothManagerCallback cb : mBluetoothManagerCallbackList ){ + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { cb.onBluetoothServiceDown(); @@ -1368,14 +1371,14 @@ public final class BluetoothAdapter { return mManagerService; } - private ArrayList mBluetoothManagerCallbackList = new ArrayList(); + private ArrayList mProxyServiceStateCallbacks = new ArrayList(); /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { synchronized (mManagerCallback) { if (cb == null) { - Log.w(TAG, "Unable to register null state change callback", new Exception()); - } else if (!mBluetoothManagerCallbackList.contains(cb)) { - mBluetoothManagerCallbackList.add(cb); + Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); + } else if (!mProxyServiceStateCallbacks.contains(cb)) { + mProxyServiceStateCallbacks.add(cb); } } return mService; @@ -1383,7 +1386,7 @@ public final class BluetoothAdapter { /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { synchronized (mManagerCallback) { - mBluetoothManagerCallbackList.remove(cb); + mProxyServiceStateCallbacks.remove(cb); } } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 9653de2fdd3..eff780dabcf 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -72,6 +72,9 @@ interface IBluetooth void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); + void registerCallback(in IBluetoothCallback callback); + void unregisterCallback(in IBluetoothCallback callback); + // For Socket ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); diff --git a/framework/java/android/bluetooth/IBluetoothCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl index 8edb3f4c413..e2809788d67 100644 --- a/framework/java/android/bluetooth/IBluetoothCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothCallback.aidl @@ -23,5 +23,6 @@ package android.bluetooth; */ interface IBluetoothCallback { - void onRfcommChannelFound(int channel); + //void onRfcommChannelFound(int channel); + void onBluetoothStateChange(int prevState, int newState); } -- GitLab From 40781c12e649b2d579c17823b73465141593c956 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Fri, 29 Jun 2012 16:57:31 -0700 Subject: [PATCH 0238/1408] Fix merge error, put in tmp workaround for input manager. Fix merge error, missing enableNoAutoConnect api. Remove bluetoothF from inputManageF.systemReady call as tmp workaround. Recover shutdownRadios(MAX_RADIO_WAIT_TIME) call in ShutdownThread Change-Id: Ice2b5c54267755bdbf56c742feabbb8709b92570 --- framework/java/android/bluetooth/IBluetooth.aidl | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index eff780dabcf..bfad4bed370 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -32,6 +32,7 @@ interface IBluetooth boolean isEnabled(); int getState(); boolean enable(); + boolean enableNoAutoConnect(); boolean disable(boolean persist); String getAddress(); -- GitLab From 9e9cac3f99e97e7da95c9666d7a7c85a36824d3d Mon Sep 17 00:00:00 2001 From: Fred Date: Wed, 11 Jul 2012 10:25:23 -0700 Subject: [PATCH 0239/1408] Removed startService() call from BluetoothManagerService. BT enable()/disable() are handled by using the binder interface Change-Id: I0bb8d4984129706e424320241ca3ea7e12caf0d3 Conflicts: core/java/android/bluetooth/IBluetooth.aidl --- framework/java/android/bluetooth/IBluetooth.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index bfad4bed370..d016c2623be 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -33,7 +33,7 @@ interface IBluetooth int getState(); boolean enable(); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable(); String getAddress(); ParcelUuid[] getUuids(); -- GitLab From 4351cc535758b5a63866f708e7bd9c08a56ec090 Mon Sep 17 00:00:00 2001 From: Syed Ibrahim M Date: Tue, 19 Jun 2012 10:14:25 -0700 Subject: [PATCH 0240/1408] handle NullPointerException in cases where a proxy call to AdapterService is called after Adapter Service is stopped. Change-Id: I009787f03f9a013b9b06fcb7e3f6241c850f8cd2 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 25047632ce3..4cc22b4ae90 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -786,6 +786,12 @@ public final class BluetoothDevice implements Parcelable { try { return sService.getBondState(this); } catch (RemoteException e) {Log.e(TAG, "", e);} + catch (NullPointerException npe) { + // Handle case where bluetooth service proxy + // is already null. + Log.e(TAG, "NullPointerException for getBondState() of device ("+ + getAddress()+")", npe); + } return BOND_NONE; } -- GitLab From f24eedf202851f8068f05c175f02a48451646a65 Mon Sep 17 00:00:00 2001 From: Swaminatha Balaji Date: Mon, 25 Jun 2012 04:03:31 -0700 Subject: [PATCH 0241/1408] Fix for When Hf is rejected the device should initiate A2dp if a2dp is enabled Change-Id: I82d05eacb6c7fae6eb1ebf246d2fce7314ff0a75 --- framework/java/android/bluetooth/BluetoothProfile.java | 7 +++++++ 1 file changed, 7 insertions(+) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothProfile.java diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java old mode 100644 new mode 100755 index 1920efa52f8..eada27c7b0d --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -114,6 +114,13 @@ public interface BluetoothProfile { * */ public static final int PRIORITY_UNDEFINED = -1; + /** + * This Intent is sent to initiate the other profile connections which are enabled + * @hide + **/ + public static final String ACTION_CONNECT_OTHER_PROFILES = + "android.bluetooth.profile.CONNECT_OTHER_PROFILES"; + /** * Get connected devices for this specific profile. * -- GitLab From 8a018552ddc1507818d9781aa6ec1993cc5ad968 Mon Sep 17 00:00:00 2001 From: Ravi Nagarajan Date: Fri, 29 Jun 2012 19:18:07 +0530 Subject: [PATCH 0242/1408] Reset priority on unbond Change-Id: I8232c666bde26235ad527c96e5218fc1b3e7a1db --- framework/java/android/bluetooth/BluetoothA2dp.java | 1 + framework/java/android/bluetooth/BluetoothHeadset.java | 1 + 2 files changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 74f634b19a5..1b415e5eb9b 100755 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -338,6 +338,7 @@ public final class BluetoothA2dp implements BluetoothProfile { && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON && + priority != BluetoothProfile.PRIORITY_UNDEFINED && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { return false; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 75dfe9dbb4c..8e6ebafda50 100755 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -456,6 +456,7 @@ public final class BluetoothHeadset implements BluetoothProfile { isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON && + priority != BluetoothProfile.PRIORITY_UNDEFINED && priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { return false; } -- GitLab From 0d6d9f8765c3eb2a602a6bf1809eef4b2a66d1de Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 3 Aug 2012 08:36:02 -0700 Subject: [PATCH 0243/1408] Bluetooth: fix enableNoAutoConnect() call. For now, just do a regular connect. Change-Id: Ibc43098d45c82177298cb17d72a32c7904924021 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index dc1802929a8..ceaf17ee620 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1246,10 +1246,8 @@ public final class BluetoothAdapter { * @hide */ public boolean enableNoAutoConnect() { - try { - return mService.enableNoAutoConnect(); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + // TODO avoid auto-connect in the new stack. + return enable(); } /** -- GitLab From 20867a776cbcb14edd68096a08fe766c8db25057 Mon Sep 17 00:00:00 2001 From: Ganesh Ganapathi Batta Date: Tue, 31 Jul 2012 16:08:17 -0700 Subject: [PATCH 0244/1408] Auto connection/disconnection related changes Remove ACTION_CONNECT_OTHER_PROFILES as it is no longer used Allow only PRIORITY_ON and PRIORTY_OFF as priority params for SetPriority() API in BluetoothA2DP and BluetoothHeadset BluetoothPBAP: Pass on proxy object as part of Service Connected callback Change-Id: Ida00fc2b7d1dcb91b01f7ddd2cea482367a3b334 --- .../java/android/bluetooth/BluetoothA2dp.java | 4 +- .../android/bluetooth/BluetoothHeadset.java | 4 +- .../java/android/bluetooth/BluetoothPbap.java | 69 +++++++++++++++++-- .../android/bluetooth/BluetoothProfile.java | 7 -- 4 files changed, 66 insertions(+), 18 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothPbap.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1b415e5eb9b..6e2278d460a 100755 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -337,9 +337,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON && - priority != BluetoothProfile.PRIORITY_UNDEFINED && - priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { + priority != BluetoothProfile.PRIORITY_ON){ return false; } try { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8e6ebafda50..541b69fad2b 100755 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -455,9 +455,7 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON && - priority != BluetoothProfile.PRIORITY_UNDEFINED && - priority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { + priority != BluetoothProfile.PRIORITY_ON) { return false; } try { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java old mode 100644 new mode 100755 index 639ae1a96d5..7de2ef61a07 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -70,6 +70,7 @@ public class BluetoothPbap { private IBluetoothPbap 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; @@ -96,7 +97,7 @@ public class BluetoothPbap { * this callback before making IPC calls on the BluetoothPbap * service. */ - public void onServiceConnected(); + public void onServiceConnected(BluetoothPbap proxy); /** * Called to notify the client that this proxy object has been @@ -108,12 +109,54 @@ public class BluetoothPbap { public void onServiceDisconnected(); } + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService( + new Intent(IBluetoothPbap.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth PBAP Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + /** * Create a BluetoothPbap proxy object. */ public BluetoothPbap(Context context, ServiceListener l) { 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); + } + } if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Pbap Service"); } @@ -134,9 +177,25 @@ public class BluetoothPbap { * are ok. */ public synchronized void close() { - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + 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); + mConnection = null; + } catch (Exception re) { + Log.e(TAG,"",re); + } + } } mServiceListener = null; } @@ -240,7 +299,7 @@ public class BluetoothPbap { if (DBG) log("Proxy object connected"); mService = IBluetoothPbap.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(); + mServiceListener.onServiceConnected(BluetoothPbap.this); } } public void onServiceDisconnected(ComponentName className) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index eada27c7b0d..1920efa52f8 100755 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -114,13 +114,6 @@ public interface BluetoothProfile { * */ public static final int PRIORITY_UNDEFINED = -1; - /** - * This Intent is sent to initiate the other profile connections which are enabled - * @hide - **/ - public static final String ACTION_CONNECT_OTHER_PROFILES = - "android.bluetooth.profile.CONNECT_OTHER_PROFILES"; - /** * Get connected devices for this specific profile. * -- GitLab From fa1a5278a9412da532f397de49bad3db3ebf1965 Mon Sep 17 00:00:00 2001 From: Ganesh Ganapathi Batta Date: Wed, 8 Aug 2012 15:35:49 -0700 Subject: [PATCH 0245/1408] Implement enableNoAutoconnect() Adding enableNoAutoconnect() API support in Bluetooth service to let BT enable in quiet mode Change-Id: I546f3ceb298082a9c9a698f406379470e3cc0d4f --- .../java/android/bluetooth/BluetoothAdapter.java | 15 ++++++++++++--- .../java/android/bluetooth/IBluetoothManager.aidl | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/IBluetoothManager.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ceaf17ee620..17d404d2d7a 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -504,7 +504,10 @@ public final class BluetoothAdapter { * immediate error */ public boolean enable() { - boolean enabled = false; + if (isEnabled() == true){ + if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); + return true; + } try { return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -1246,8 +1249,14 @@ public final class BluetoothAdapter { * @hide */ public boolean enableNoAutoConnect() { - // TODO avoid auto-connect in the new stack. - return enable(); + if (isEnabled() == true){ + if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!"); + return true; + } + try { + return mManagerService.enableNoAutoConnect(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; } /** diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl old mode 100644 new mode 100755 index f82da82fff1..de8fe91dc00 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -21,6 +21,7 @@ interface IBluetoothManager void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); boolean enable(); + boolean enableNoAutoConnect(); boolean disable(boolean persist); String getAddress(); -- GitLab From 6a62db4f9bdb16e8188ec1cd58ac67c73eb3a830 Mon Sep 17 00:00:00 2001 From: Irfan Sheriff Date: Thu, 16 Aug 2012 12:49:23 -0700 Subject: [PATCH 0246/1408] Captive portal handling We now notify the user of a captive portal before switching to the network as default. This allows background applications to continue to work until the user confirms he wants to sign in to the captive portal. Also, moved out captive portal handling out of wifi as a seperate component. Change-Id: I7c7507481967e33a1afad0b4961688bd192f0d31 --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index b2b5d81fe3a..30406e98710 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -133,6 +133,11 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return true; } + @Override + public void captivePortalCheckComplete() { + // not implemented + } + /** * Re-enable connectivity to a network after a {@link #teardown()}. */ -- GitLab From 80b7fb44e7613838b2162df33b64fd0a7b7ac2e5 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 29 Aug 2012 00:12:29 -0700 Subject: [PATCH 0247/1408] Turn off verbose debug message in BluetoothAdapter Change-Id: I30245ab911b5428f7af38f195b941db02d36b18f --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 17d404d2d7a..f817fb48d2c 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -75,6 +75,7 @@ import java.util.UUID; public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -465,7 +466,7 @@ public final class BluetoothAdapter { if (mService != null) { int state= mService.getState(); - if (DBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); return state; } // TODO(BT) there might be a small gap during STATE_TURNING_ON that -- GitLab From f8035a79ebb19a8c96197ea7a4daf82c5ce4464f Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 9 Oct 2012 22:10:37 -0700 Subject: [PATCH 0248/1408] Reduce android.bluetooth package debug messages bug 7174712 Change-Id: I8d3fdc8edbe42068b5a550660d7b24854c3c4af4 --- .../java/android/bluetooth/BluetoothA2dp.java | 13 ++++--- .../android/bluetooth/BluetoothHeadset.java | 23 ++++++------ .../android/bluetooth/BluetoothHealth.java | 11 +++--- .../bluetooth/BluetoothInputDevice.java | 19 +++++----- .../java/android/bluetooth/BluetoothPan.java | 13 ++++--- .../java/android/bluetooth/BluetoothPbap.java | 13 ++++--- .../android/bluetooth/BluetoothSocket.java | 37 ++++++++++--------- .../BluetoothTetheringDataTracker.java | 20 +++++----- 8 files changed, 80 insertions(+), 69 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 6e2278d460a..6fdf3b47b85 100755 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -45,6 +45,7 @@ import java.util.List; public final class BluetoothA2dp implements BluetoothProfile { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the A2DP @@ -113,7 +114,7 @@ public final class BluetoothA2dp implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -126,7 +127,7 @@ public final class BluetoothA2dp implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); } @@ -269,7 +270,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public List getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -286,7 +287,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -303,7 +304,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -365,7 +366,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 541b69fad2b..793d79858c6 100755 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -46,6 +46,7 @@ import java.util.List; public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Headset @@ -226,7 +227,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -239,7 +240,7 @@ public final class BluetoothHeadset implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Headset Service"); } @@ -281,7 +282,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * are ok. */ /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -387,7 +388,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public List getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -404,7 +405,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -421,7 +422,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); + if (VDBG) log("getConnectionState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -483,7 +484,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -566,7 +567,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * false otherwise or on error */ public boolean isAudioConnected(BluetoothDevice device) { - if (DBG) log("isAudioConnected()"); + if (VDBG) log("isAudioConnected()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -594,7 +595,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getBatteryUsageHint(BluetoothDevice device) { - if (DBG) log("getBatteryUsageHint()"); + if (VDBG) log("getBatteryUsageHint()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -661,7 +662,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getAudioState(BluetoothDevice device) { - if (DBG) log("getAudioState"); + if (VDBG) log("getAudioState"); if (mService != null && !isDisabled()) { try { return mService.getAudioState(device); @@ -683,7 +684,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean isAudioOn() { - if (DBG) log("isAudioOn()"); + if (VDBG) log("isAudioOn()"); if (mService != null && isEnabled()) { try { return mService.isAudioOn(); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 4a0bc7e37a2..cb23662f6c1 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -58,6 +58,7 @@ import java.util.List; public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Health Profile Source Role - the health device. @@ -102,7 +103,7 @@ public final class BluetoothHealth implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -115,7 +116,7 @@ public final class BluetoothHealth implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Health Service"); } @@ -148,7 +149,7 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthCallback callback) { if (!isEnabled() || name == null) return false; - if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); + if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); return registerAppConfiguration(name, dataType, SINK_ROLE, CHANNEL_TYPE_ANY, callback); } @@ -174,7 +175,7 @@ public final class BluetoothHealth implements BluetoothProfile { boolean result = false; if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; - if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + if (VDBG) log("registerApplication(" + name + ":" + dataType + ")"); BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); BluetoothHealthAppConfiguration config = new BluetoothHealthAppConfiguration(name, dataType, role, channelType); @@ -488,7 +489,7 @@ public final class BluetoothHealth implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index bff966d4d26..db7e424d40e 100755 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -45,6 +45,7 @@ import java.util.List; public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Input @@ -191,7 +192,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -204,7 +205,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth HID Service"); } @@ -243,7 +244,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -344,7 +345,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public List getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -361,7 +362,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -378,7 +379,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); @@ -438,7 +439,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); @@ -519,7 +520,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean getProtocolMode(BluetoothDevice device) { - if (DBG) log("getProtocolMode(" + device + ")"); + if (VDBG) log("getProtocolMode(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getProtocolMode(device); @@ -570,7 +571,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { - if (DBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); + if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getReport(device, reportType, reportId, bufferSize); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index cae7a73bcf0..e25ec86a866 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -44,6 +44,7 @@ import java.util.List; public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Pan @@ -145,7 +146,7 @@ public final class BluetoothPan implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; @@ -175,7 +176,7 @@ public final class BluetoothPan implements BluetoothProfile { } Log.d(TAG, "BluetoothPan(), bindService called"); } else { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mPanService = null; @@ -266,7 +267,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public List getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mPanService != null && isEnabled()) { try { return mPanService.getConnectedDevices(); @@ -283,7 +284,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mPanService != null && isEnabled()) { try { return mPanService.getDevicesMatchingConnectionStates(states); @@ -300,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mPanService != null && isEnabled() && isValidDevice(device)) { try { @@ -324,7 +325,7 @@ public final class BluetoothPan implements BluetoothProfile { } public boolean isTetheringOn() { - if (DBG) log("isTetheringOn()"); + if (VDBG) log("isTetheringOn()"); try { return mPanService.isTetheringOn(); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 7de2ef61a07..b5280e53377 100755 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -51,7 +51,8 @@ import android.util.Log; public class BluetoothPbap { private static final String TAG = "BluetoothPbap"; - private static final boolean DBG = false; + private static final boolean DBG = true; + private static final boolean VDBG = false; /** int extra for PBAP_STATE_CHANGED_ACTION */ public static final String PBAP_STATE = @@ -114,7 +115,7 @@ public class BluetoothPbap { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -127,7 +128,7 @@ public class BluetoothPbap { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService( new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { @@ -206,7 +207,7 @@ public class BluetoothPbap { * object is currently not connected to the Pbap service. */ public int getState() { - if (DBG) log("getState()"); + if (VDBG) log("getState()"); if (mService != null) { try { return mService.getState(); @@ -225,7 +226,7 @@ public class BluetoothPbap { * the Pbap service. */ public BluetoothDevice getClient() { - if (DBG) log("getClient()"); + if (VDBG) log("getClient()"); if (mService != null) { try { return mService.getClient(); @@ -243,7 +244,7 @@ public class BluetoothPbap { * object is not currently connected to the Pbap service. */ public boolean isConnected(BluetoothDevice device) { - if (DBG) log("isConnected(" + device + ")"); + if (VDBG) log("isConnected(" + device + ")"); if (mService != null) { try { return mService.isConnected(device); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 1bc640ff598..aba87106d98 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -72,6 +72,8 @@ import java.nio.ByteBuffer; */ public final class BluetoothSocket implements Closeable { private static final String TAG = "BluetoothSocket"; + private static final boolean DBG = true; + private static final boolean VDBG = false; /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; @@ -172,7 +174,7 @@ public final class BluetoothSocket implements Closeable { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - Log.d(TAG, "socket fd passed by stack fds: " + fds); + if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); if(fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); throw new IOException("bt socket acept failed"); @@ -291,7 +293,7 @@ public final class BluetoothSocket implements Closeable { mUuid, mPort, getSecurityFlags()); synchronized(this) { - Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mPfd == null) throw new IOException("bt socket connect failed"); FileDescriptor fd = mPfd.getFileDescriptor(); @@ -339,23 +341,24 @@ public final class BluetoothSocket implements Closeable { // read out port number try { synchronized(this) { - Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + + mPfd); if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - Log.d(TAG, "bindListen(), new LocalSocket "); + if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket "); mSocket = new LocalSocket(fd); - Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } - Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); + if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); int channel = readInt(mSocketIS); synchronized(this) { if(mSocketState == SocketState.INIT) mSocketState = SocketState.LISTENING; } - Log.d(TAG, "channel: " + channel); + if (VDBG) Log.d(TAG, "channel: " + channel); if (mPort == -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -385,26 +388,26 @@ public final class BluetoothSocket implements Closeable { } /*package*/ int available() throws IOException { - Log.d(TAG, "available: " + mSocketIS); + if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); int ret = mSocketIS.read(b, offset, length); if(ret < 0) throw new IOException("bt socket closed, read return: " + ret); - Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - Log.d(TAG, "write: " + mSocketOS + " length: " + length); + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); mSocketOS.write(b, offset, length); // There is no good way to confirm since the entire process is asynchronous anyway - Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); return length; } @@ -420,10 +423,10 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); if(mSocket != null) { - Log.d(TAG, "Closing mSocket: " + mSocket); + if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); mSocket.shutdownOutput(); mSocket.close(); @@ -449,7 +452,7 @@ public final class BluetoothSocket implements Closeable { private String waitSocketSignal(InputStream is) throws IOException { byte [] sig = new byte[SOCK_SIGNAL_SIZE]; int ret = readAll(is, sig); - Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); + if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); ByteBuffer bb = ByteBuffer.wrap(sig); bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); @@ -458,7 +461,7 @@ public final class BluetoothSocket implements Closeable { int channel = bb.getInt(); int status = bb.getInt(); String RemoteAddr = convertAddr(addr); - Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + RemoteAddr + ", channel: " + channel + ", status: " + status); if(status != 0) throw new IOException("Connection failure, status: " + status); @@ -481,7 +484,7 @@ public final class BluetoothSocket implements Closeable { private int readInt(InputStream is) throws IOException { byte[] ibytes = new byte[4]; int ret = readAll(is, ibytes); - Log.d(TAG, "inputStream.read ret: " + ret); + if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret); ByteBuffer bb = ByteBuffer.wrap(ibytes); bb.order(ByteOrder.nativeOrder()); return bb.getInt(); diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 30406e98710..063e5a8a466 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -51,6 +51,8 @@ import java.util.concurrent.atomic.AtomicInteger; public class BluetoothTetheringDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; + private static final boolean DBG = true; + private static final boolean VDBG = false; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); @@ -99,10 +101,10 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { * Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { - Log.d(TAG, "startMonitoring: target: " + target); + if (DBG) Log.d(TAG, "startMonitoring: target: " + target); mContext = context; mCsHandler = target; - Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); + if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); @@ -310,31 +312,31 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { } public synchronized void startReverseTether(String iface) { mIface = iface; - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); mDhcpThread = new Thread(new Runnable() { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); String DhcpResultName = "dhcp." + mIface + ".result";; String result = ""; - Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); + if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); for(int i = 0; i < 30*5; i++) { try { Thread.sleep(200); } catch (InterruptedException ie) { return;} result = SystemProperties.get(DhcpResultName); - Log.d(TAG, "read " + DhcpResultName + ": " + result); + if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result); if(result.equals("failed")) { Log.e(TAG, "startReverseTether, failed to start dhcp service"); return; } if(result.equals("ok")) { - Log.d(TAG, "startReverseTether, dhcp resut: " + result); + if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result); if(readLinkProperty(mIface)) { mNetworkInfo.setIsAvailable(true); mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); if(mCsHandler != null) { Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); @@ -346,7 +348,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return; } } - Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result); + Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); mDhcpThread.start(); -- GitLab From 3049570708578ac0b5fd0ed2fd1793395028c7a0 Mon Sep 17 00:00:00 2001 From: zzy Date: Thu, 11 Oct 2012 14:52:43 -0700 Subject: [PATCH 0249/1408] Add serial port profile support and allow to connect to rfcomm channel without sdp discovery bug 7272974 Change-Id: Idc10edc056b48da2fd96bea84eba3fb73b97bab1 --- framework/java/android/bluetooth/BluetoothSocket.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index aba87106d98..8cbf5b1a5c8 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.UUID; import android.net.LocalSocket; import java.nio.ByteOrder; import java.nio.ByteBuffer; @@ -140,7 +141,9 @@ public final class BluetoothSocket implements Closeable { throw new IOException("Invalid RFCOMM channel: " + port); } } - mUuid = uuid; + if(uuid != null) + mUuid = uuid; + else mUuid = new ParcelUuid(new UUID(0, 0)); mType = type; mAuth = auth; mEncrypt = encrypt; -- GitLab From a9cc57d090b3610c416d5b879b9fd05b2fea4a29 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Tue, 23 Oct 2012 17:31:56 -0700 Subject: [PATCH 0250/1408] License of files Bluetooth package is not updated to ASL2 bug 7385618 Change-Id: I6232f537f4fda979d3aabe3a059c11d3299de9b8 --- .../java/android/bluetooth/BluetoothSocket.java | 14 +++++++++++++- .../android/bluetooth/IBluetoothHeadsetPhone.aidl | 14 +++++++++++++- .../java/android/bluetooth/IBluetoothHealth.aidl | 14 +++++++++++++- .../android/bluetooth/IBluetoothInputDevice.aidl | 15 ++++++++++++++- .../java/android/bluetooth/IBluetoothManager.aidl | 14 +++++++++++++- .../bluetooth/IBluetoothManagerCallback.aidl | 14 +++++++++++++- .../java/android/bluetooth/IBluetoothPan.aidl | 15 ++++++++++++++- 7 files changed, 93 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 8cbf5b1a5c8..26bde197acd 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -1,5 +1,17 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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; diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl index 163e4e237f3..d5e64f6faec 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl @@ -1,5 +1,17 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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; diff --git a/framework/java/android/bluetooth/IBluetoothHealth.aidl b/framework/java/android/bluetooth/IBluetoothHealth.aidl index e741da475df..a84a42cb9ce 100644 --- a/framework/java/android/bluetooth/IBluetoothHealth.aidl +++ b/framework/java/android/bluetooth/IBluetoothHealth.aidl @@ -1,5 +1,17 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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; diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl index 23e6d504ccc..1ebb9ca6eb1 100755 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -1,6 +1,19 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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.bluetooth.BluetoothDevice; diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index de8fe91dc00..ed8777c5152 100755 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -1,5 +1,17 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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; diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl index 3e795eab69e..9551086a1de 100644 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -1,5 +1,17 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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; diff --git a/framework/java/android/bluetooth/IBluetoothPan.aidl b/framework/java/android/bluetooth/IBluetoothPan.aidl index b91bd7d9c52..5a323477704 100644 --- a/framework/java/android/bluetooth/IBluetoothPan.aidl +++ b/framework/java/android/bluetooth/IBluetoothPan.aidl @@ -1,6 +1,19 @@ /* - * Copyright (C) 2012 Google Inc. + * Copyright (C) 2012 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.bluetooth.BluetoothDevice; -- GitLab From 7658659fc3c32a8575db4dd503a1720e6472a4d2 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Wed, 31 Oct 2012 14:32:53 -0700 Subject: [PATCH 0251/1408] Remove DhcpInfoInternal First step in accepting a set of patches. bug:6799630 Change-Id: I6c894c60aeb3022960c2aaa45451bb1dde2b493b --- .../java/android/bluetooth/BluetoothTetheringDataTracker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 063e5a8a466..3ba4f267990 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -21,7 +21,6 @@ import android.os.ServiceManager; import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; -import android.net.DhcpInfoInternal; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; -- GitLab From 2e650b349052bcfd93fde6eb1e88aed8f77066a2 Mon Sep 17 00:00:00 2001 From: zzy Date: Thu, 22 Nov 2012 11:52:44 -0800 Subject: [PATCH 0252/1408] timeout support for accept() and added check for signal size bug 7592240 Change-Id: Ide1868da669c190cdfed90f7af3f739ec9da690b --- .../java/android/bluetooth/BluetoothSocket.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 26bde197acd..d7a214d9770 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -300,7 +300,6 @@ public final class BluetoothSocket implements Closeable { if (mDevice == null) throw new IOException("Connect is called on null device"); try { - // TODO(BT) derive flag from auth and encrypt if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); @@ -349,7 +348,6 @@ public final class BluetoothSocket implements Closeable { mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); - // TODO(BT) right error code? return -1; } @@ -388,8 +386,13 @@ public final class BluetoothSocket implements Closeable { /*package*/ BluetoothSocket accept(int timeout) throws IOException { BluetoothSocket acceptedSocket; if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state"); - // TODO(BT) wait on an incoming connection + if(timeout > 0) { + Log.d(TAG, "accept() set timeout (ms):" + timeout); + mSocket.setSoTimeout(timeout); + } String RemoteAddr = waitSocketSignal(mSocketIS); + if(timeout > 0) + mSocket.setSoTimeout(0); synchronized(this) { if (mSocketState != SocketState.LISTENING) @@ -397,8 +400,6 @@ public final class BluetoothSocket implements Closeable { acceptedSocket = acceptSocket(RemoteAddr); //quick drop the reference of the file handle } - // TODO(BT) rfcomm socket only supports one connection, return this? - // return this; return acceptedSocket; } @@ -451,7 +452,6 @@ public final class BluetoothSocket implements Closeable { mPfd.detachFd(); } } - // TODO(BT) unbind proxy, } /*package */ void removeChannel() { @@ -471,6 +471,8 @@ public final class BluetoothSocket implements Closeable { ByteBuffer bb = ByteBuffer.wrap(sig); bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); + if(size != SOCK_SIGNAL_SIZE) + throw new IOException("Connection failure, wrong signal size: " + size); byte [] addr = new byte[6]; bb.get(addr); int channel = bb.getInt(); @@ -487,7 +489,7 @@ public final class BluetoothSocket implements Closeable { while(left > 0) { int ret = is.read(b, b.length - left, left); if(ret <= 0) - throw new IOException("read failed, socket might closed, read ret: " + ret); + throw new IOException("read failed, socket might closed or timeout, read ret: " + ret); left -= ret; if(left != 0) Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + -- GitLab From a466c6c72a7dee9c1abf0f95cb3e3412072ffa94 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 29 Nov 2012 20:26:19 -0800 Subject: [PATCH 0253/1408] Clean up debug messages bug 7626174 Change-Id: I65cdcaf2c48a78468b6cef0b8591289435068b24 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f817fb48d2c..6367e160490 100755 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1212,7 +1212,7 @@ public final class BluetoothAdapter { final private IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); synchronized (mManagerCallback) { mService = bluetoothService; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ @@ -1228,7 +1228,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d7a214d9770..8029a1a25ce 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -429,7 +429,7 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else -- GitLab From 40b98951361157933c2084e855f7cffef0ca7fb7 Mon Sep 17 00:00:00 2001 From: Ganesh Ganapathi Batta Date: Tue, 5 Feb 2013 15:28:33 -0800 Subject: [PATCH 0254/1408] Initial version of BLE support for Bluedroid The API classes are hidden for now. Will unhide after API console approval. Change-Id: I8283dd562fd6189fdd15c866ef2efb8bbdbc4109 --- .../android/bluetooth/BluetoothAdapter.java | 19 +- .../java/android/bluetooth/BluetoothGatt.java | 1309 +++++++++++++++++ .../bluetooth/BluetoothGattCallback.java | 157 ++ .../BluetoothGattCharacteristic.java | 670 +++++++++ .../bluetooth/BluetoothGattDescriptor.java | 185 +++ .../bluetooth/BluetoothGattServer.java | 900 ++++++++++++ .../BluetoothGattServerCallback.java | 158 ++ .../bluetooth/BluetoothGattService.java | 232 +++ .../android/bluetooth/BluetoothProfile.java | 12 + .../android/bluetooth/IBluetoothGatt.aidl | 90 ++ .../bluetooth/IBluetoothGattCallback.aidl | 65 + .../IBluetoothGattServerCallback.aidl | 61 + .../MutableBluetoothGattCharacteristic.java | 67 + .../MutableBluetoothGattDescriptor.java | 45 + .../MutableBluetoothGattService.java | 83 ++ 15 files changed, 4051 insertions(+), 2 deletions(-) mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothAdapter.java create mode 100644 framework/java/android/bluetooth/BluetoothGatt.java create mode 100644 framework/java/android/bluetooth/BluetoothGattCallback.java create mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.java create mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.java create mode 100644 framework/java/android/bluetooth/BluetoothGattServer.java create mode 100644 framework/java/android/bluetooth/BluetoothGattServerCallback.java create mode 100644 framework/java/android/bluetooth/BluetoothGattService.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothProfile.java create mode 100644 framework/java/android/bluetooth/IBluetoothGatt.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothGattCallback.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl create mode 100644 framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java create mode 100644 framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java create mode 100644 framework/java/android/bluetooth/MutableBluetoothGattService.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java old mode 100755 new mode 100644 index 6367e160490..1bbfb5dfb31 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1136,8 +1136,9 @@ public final class BluetoothAdapter { /** * Get the profile proxy object associated with the profile. * - *

    Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or - * {@link BluetoothProfile#A2DP}. Clients must implements + *

    Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, + * or {@link BluetoothProfile#GATT_SERVER}. Clients must implements * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * @@ -1166,6 +1167,12 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; + } else if (profile == BluetoothProfile.GATT) { + BluetoothGatt gatt = new BluetoothGatt(context, listener); + return true; + } else if (profile == BluetoothProfile.GATT_SERVER) { + BluetoothGattServer gattServer = new BluetoothGattServer(context, listener); + return true; } else { return false; } @@ -1206,6 +1213,14 @@ public final class BluetoothAdapter { BluetoothHealth health = (BluetoothHealth)proxy; health.close(); break; + case BluetoothProfile.GATT: + BluetoothGatt gatt = (BluetoothGatt)proxy; + gatt.close(); + break; + case BluetoothProfile.GATT_SERVER: + BluetoothGattServer gattServer = (BluetoothGattServer)proxy; + gattServer.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java new file mode 100644 index 00000000000..1e12025497f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -0,0 +1,1309 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothStateChangeCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Public API for the Bluetooth Gatt Profile. + * + *

    This class provides Bluetooth Gatt functionality to enable communication + * with Bluetooth Smart or Smart Ready devices. + * + *

    BluetoothGatt is a proxy object for controlling the Bluetooth Service + * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothGatt proxy object. + * + *

    To connect to a remote peripheral device, create a {@link BluetoothGattCallback} + * and call {@link #registerApp} to register your application. Gatt capable + * devices can be discovered using the {@link #startScan} function or the + * regular Bluetooth device discovery process. + * @hide + */ +public final class BluetoothGatt implements BluetoothProfile { + private static final String TAG = "BluetoothGatt"; + private static final boolean DBG = true; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothGatt mService; + private BluetoothGattCallback mCallback; + private int mClientIf; + private boolean mAuthRetry = false; + + private List mServices; + + /** A Gatt operation completed successfully */ + public static final int GATT_SUCCESS = 0; + + /** Gatt read operation is not permitted */ + public static final int GATT_READ_NOT_PERMITTED = 0x2; + + /** Gatt write operation is not permitted */ + public static final int GATT_WRITE_NOT_PERMITTED = 0x3; + + /** Insufficient authentication for a given operation */ + public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; + + /** The given request is not supported */ + public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; + + /** Insufficient encryption for a given operation */ + public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; + + /** A read or write operation was requested with an invalid offset */ + public static final int GATT_INVALID_OFFSET = 0x7; + + /** A write operation exceeds the maximum length of the attribute */ + public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + + /** + * No authentication required. + * @hide + */ + /*package*/ static final int AUTHENTICATION_NONE = 0; + + /** + * Authentication requested; no man-in-the-middle protection required. + * @hide + */ + /*package*/ static final int AUTHENTICATION_NO_MITM = 1; + + /** + * Authentication with man-in-the-middle protection requested. + * @hide + */ + /*package*/ static final int AUTHENTICATION_MITM = 2; + + /** + * Bluetooth state change handlers + */ + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + mService = null; + mContext.unbindService(mConnection); + } + } else { + synchronized (mConnection) { + if (mService == null) { + if (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth GATT Service"); + } + } + } + } + } + }; + + /** + * Service binder handling + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothGatt.Stub.asInterface(service); + ServiceListener serviceListener = mServiceListener; + if (serviceListener != null) { + serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + ServiceListener serviceListener = mServiceListener; + if (serviceListener != null) { + serviceListener.onServiceDisconnected(BluetoothProfile.GATT); + } + } + }; + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattCallback mBluetoothGattCallback = + new IBluetoothGattCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + mClientIf = clientIf; + try { + mCallback.onAppRegistered(status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Client connection state changed + * @hide + */ + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status + + " clientIf=" + clientIf + " device=" + address); + try { + mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, + connected ? BluetoothProfile.STATE_CONNECTED + : BluetoothProfile.STATE_DISCONNECTED); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * A new GATT service has been discovered. + * The service is added to the internal list and the search + * continues. + * @hide + */ + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(), + srvcInstId, srvcType)); + } + + /** + * An included service has been found durig GATT discovery. + * The included service is added to the respective parent. + * @hide + */ + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, + srvcUuid.getUuid(), srvcInstId, srvcType); + BluetoothGattService includedService = getService(device, + inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); + + if (service != null && includedService != null) { + service.addIncludedService(includedService); + } + } + + /** + * A new GATT characteristic has been discovered. + * Add the new characteristic to the relevant service and continue + * the remote device inspection. + * @hide + */ + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service != null) { + service.addCharacteristic(new BluetoothGattCharacteristic( + service, charUuid.getUuid(), charInstId, charProps, 0)); + } + } + + /** + * A new GATT descriptor has been discovered. + * Finally, add the descriptor to the related characteristic. + * This should conclude the remote device update. + * @hide + */ + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid()); + if (characteristic == null) return; + + characteristic.addDescriptor(new BluetoothGattDescriptor( + characteristic, descUuid.getUuid(), 0)); + } + + /** + * Remote search has been completed. + * The internal object structure should now reflect the state + * of the remote device database. Let the application know that + * we are done at this point. + * @hide + */ + public void onSearchComplete(String address, int status) { + if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onServicesDiscovered(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote characteristic has been read. + * Updates the internal value. + * @hide + */ + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + + " UUID=" + charUuid + " Status=" + status); + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.readCharacteristic(mClientIf, address, + srvcType, srvcInstId, srvcUuid, + charInstId, charUuid, AUTHENTICATION_MITM); + return; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + if (status == 0) characteristic.setValue(value); + + try { + mCallback.onCharacteristicRead(characteristic, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Characteristic has been written to the remote device. + * Let the app know how we did... + * @hide + */ + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + + " UUID=" + charUuid + " Status=" + status); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.writeCharacteristic(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + characteristic.getWriteType(), AUTHENTICATION_MITM, + characteristic.getValue()); + return; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + try { + mCallback.onCharacteristicWrite(characteristic, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote characteristic has been updated. + * Updates the internal value. + * @hide + */ + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + characteristic.setValue(value); + + try { + mCallback.onCharacteristicChanged(characteristic); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Descriptor has been read. + * @hide + */ + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + descrUuid.getUuid()); + if (descriptor == null) return; + + if (status == 0) descriptor.setValue(value); + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.readDescriptor(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + descrUuid, AUTHENTICATION_MITM); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = true; + + try { + mCallback.onDescriptorRead(descriptor, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Descriptor write operation complete. + * @hide + */ + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(device, srvcUuid.getUuid(), + srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid.getUuid(), charInstId); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + descrUuid.getUuid()); + if (descriptor == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && mAuthRetry == false) { + try { + mAuthRetry = true; + mService.writeDescriptor(mClientIf, address, + srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + descrUuid, characteristic.getWriteType(), + AUTHENTICATION_MITM, descriptor.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + mAuthRetry = false; + + try { + mCallback.onDescriptorWrite(descriptor, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Prepared write transaction completed (or aborted) + * @hide + */ + public void onExecuteWrite(String address, int status) { + if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + + " status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onReliableWriteCompleted(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote device RSSI has been read + * @hide + */ + public void onReadRemoteRssi(String address, int rssi, int status) { + if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + + " rssi=" + rssi + " status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + try { + mCallback.onReadRemoteRssi(device, rssi, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + }; + + /** + * Create a BluetoothGatt proxy object. + */ + /*package*/ BluetoothGatt(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServices = new ArrayList(); + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); + } + } else { + Log.e(TAG, "Unable to get BluetoothManager interface."); + throw new RuntimeException("BluetoothManager inactive"); + } + + //Bind to the service only if the Bluetooth is ON + if(mAdapter.isEnabled()){ + if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); + } + } + } + + /** + * Close the connection to the gatt service. + */ + /*package*/ void close() { + if (DBG) Log.d(TAG, "close()"); + + unregisterApp(); + mServiceListener = null; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); + } + } + + synchronized (mConnection) { + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } + } + } + + /** + * Returns a service by UUID, instance and type. + * @hide + */ + /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, + int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getDevice().equals(device) && + svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; + } + } + return null; + } + + + /** + * Register an application callback to start using Gatt. + * + *

    This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback Gatt callback handler that will receive asynchronous + * callbacks. + * @return true, if application was successfully registered. + */ + public boolean registerApp(BluetoothGattCallback callback) { + if (DBG) Log.d(TAG, "registerApp()"); + if (mService == null) return false; + + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); + + try { + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Unregister the current application and callbacks. + */ + public void unregisterApp() { + if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); + if (mService == null || mClientIf == 0) return; + + try { + mCallback = null; + mService.unregisterClient(mClientIf); + mClientIf = 0; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + *

    Results of the scan are reported using the + * {@link BluetoothGattCallback#onScanResult} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startScan() { + if (DBG) Log.d(TAG, "startScan()"); + if (mService == null || mClientIf == 0) return false; + + try { + mService.startScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + *

    Devices which advertise all specified services are reported using the + * {@link BluetoothGattCallback#onScanResult} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startScan() - with UUIDs"); + if (mService == null || mClientIf == 0) return false; + + try { + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + mService.startScanWithUuids(mClientIf, false, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mService == null || mClientIf == 0) return; + + try { + mService.stopScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Initiate a connection to a Bluetooth Gatt capable device. + * + *

    The connection may not be established right away, but will be + * completed when the remote device is available. A + * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * invoked when the connection state changes as a result of this function. + * + *

    The autoConnect paramter determines whether to actively connect to + * the remote device, or rather passively scan and finalize the connection + * when the remote device is in range/available. Generally, the first ever + * connection to a device should be direct (autoConnect set to false) and + * subsequent connections to known devices should be invoked with the + * autoConnect parameter set to false. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to connect to + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect(BluetoothDevice device, boolean autoConnect) { + if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); + if (mService == null || mClientIf == 0) return false; + + try { + mService.clientConnect(mClientIf, device.getAddress(), + autoConnect ? false : true); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Disconnects an established connection, or cancels a connection attempt + * currently in progress. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void cancelConnection(BluetoothDevice device) { + if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return; + + try { + mService.clientDisconnect(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Discovers services offered by a remote device as well as their + * characteristics and descriptors. + * + *

    This is an asynchronous operation. Once service discovery is completed, + * the {@link BluetoothGattCallback#onServicesDiscovered} callback is + * triggered. If the discovery was successful, the remote services can be + * retrieved using the {@link #getServices} function. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to explore + * @return true, if the remote service discovery has been started + */ + public boolean discoverServices(BluetoothDevice device) { + if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + mServices.clear(); + + try { + mService.discoverServices(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Returns a list of GATT services offered by the remote device. + * + *

    This function requires that service discovery has been completed + * for the given device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return List of services on the remote device. Returns an empty list + * if service discovery has not yet been performed. + */ + public List getServices(BluetoothDevice device) { + List result = + new ArrayList(); + + for (BluetoothGattService service : mServices) { + if (service.getDevice().equals(device)) { + result.add(service); + } + } + + return result; + } + + /** + * Returns a {@link BluetoothGattService}, if the requested UUID is + * supported by the remote device. + * + *

    This function requires that service discovery has been completed + * for the given device. + * + *

    If multiple instances of the same service (as identified by UUID) + * exist, the first instance of the service is returned. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @param uuid UUID of the requested service + * @return BluetoothGattService if supported, or null if the requested + * service is not offered by the remote device. + */ + public BluetoothGattService getService(BluetoothDevice device, UUID uuid) { + for (BluetoothGattService service : mServices) { + if (service.getDevice().equals(device) && + service.getUuid().equals(uuid)) { + return service; + } + } + + return null; + } + + /** + * Reads the requested characteristic from the associated remote device. + * + *

    This is an asynchronous operation. The result of the read operation + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic Characteristic to read from the remote device + * @return true, if the read operation was initiated successfully + */ + public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { + if ((characteristic.getProperties() & + BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; + + if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.readCharacteristic(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Writes a given characteristic and it's values to the associated remote + * device. + * + *

    Once the write operation has been completed, the + * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * reporting the result of the operation. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic Characteristic to write on the remote device + * @return true, if the write operation was initiated successfully + */ + public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { + if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 + && (characteristic.getProperties() & + BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; + + if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.writeCharacteristic(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + characteristic.getWriteType(), AUTHENTICATION_NONE, + characteristic.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Reads the value for a given descriptor from the associated remote device. + * + *

    Once the read operation has been completed, the + * {@link BluetoothGattCallback#onDescriptorRead} callback is + * triggered, signaling the result of the operation. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor value to read from the remote device + * @return true, if the read operation was initiated successfully + */ + public boolean readDescriptor(BluetoothGattDescriptor descriptor) { + if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); + if (characteristic == null) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.readDescriptor(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Write the value of a given descriptor to the associated remote device. + * + *

    A {@link BluetoothGattCallback#onDescriptorWrite} callback is + * triggered to report the result of the write operation. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to write to the associated remote device + * @return true, if the write operation was initiated successfully + */ + public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { + if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); + if (characteristic == null) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.writeDescriptor(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + new ParcelUuid(descriptor.getUuid()), + characteristic.getWriteType(), AUTHENTICATION_NONE, + descriptor.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Initiates a reliable write transaction for a given remote device. + * + *

    Once a reliable write transaction has been initiated, all calls + * to {@link #writeCharacteristic} are sent to the remote device for + * verification and queued up for atomic execution. The application will + * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback + * in response to every {@link #writeCharacteristic} call and is responsible + * for verifying if the value has been transmitted accurately. + * + *

    After all characteristics have been queued up and verified, + * {@link #executeReliableWrite} will execute all writes. If a characteristic + * was not written correctly, calling {@link #abortReliableWrite} will + * cancel the current transaction without commiting any values on the + * remote device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the reliable write transaction has been initiated + */ + public boolean beginReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.beginReliableWrite(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Executes a reliable write transaction for a given remote device. + * + *

    This function will commit all queued up characteristic write + * operations for a given remote device. + * + *

    A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is + * invoked to indicate whether the transaction has been executed correctly. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the request to execute the transaction has been sent + */ + public boolean executeReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.endReliableWrite(mClientIf, device.getAddress(), true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Cancels a reliable write transaction for a given device. + * + *

    Calling this function will discard all queued characteristic write + * operations for a given remote device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void abortReliableWrite(BluetoothDevice device) { + if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return; + + try { + mService.endReliableWrite(mClientIf, device.getAddress(), false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Enable or disable notifications/indications for a given characteristic. + * + *

    Once notifications are enabled for a characteristic, a + * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be + * triggered if the remote device indicates that the given characteristic + * has changed. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristic for which to enable notifications + * @param enable Set to true to enable notifications/indications + * @return true, if the requested notification status was set successfully + */ + public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, + boolean enable) { + if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() + + " enable: " + enable); + if (mService == null || mClientIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + BluetoothDevice device = service.getDevice(); + if (device == null) return false; + + try { + mService.registerForNotification(mClientIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), + enable); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Clears the internal cache and forces a refresh of the services from the + * remote device. + * @hide + */ + public boolean refresh(BluetoothDevice device) { + if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.refreshDevice(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Read the RSSI for a connected remote device. + * + *

    The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be + * invoked when the RSSI value has been read. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + * @return true, if the RSSI value has been requested successfully + */ + public boolean readRemoteRssi(BluetoothDevice device) { + if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress()); + if (mService == null || mClientIf == 0) return false; + + try { + mService.readRemoteRssi(mClientIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Get the current connection state of the profile. + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG,"getConnectionState()"); + if (mService == null) return STATE_DISCONNECTED; + + List connectedDevices = getConnectedDevices(); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return STATE_CONNECTED; + } + } + + return STATE_DISCONNECTED; + } + + /** + * Get connected devices for the Gatt profile. + * + *

    Return the set of devices which are in state {@link #STATE_CONNECTED} + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of devices. The list will be empty on error. + */ + @Override + public List getConnectedDevices() { + if (DBG) Log.d(TAG,"getConnectedDevices"); + + List connectedDevices = new ArrayList(); + if (mService == null) return connectedDevices; + + try { + connectedDevices = mService.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * Get a list of devices that match any of the given connection + * states. + * + *

    If none of the devices match any of the given states, + * an empty list will be returned. + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + List devices = new ArrayList(); + if (mService == null) return devices; + + try { + devices = mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java new file mode 100644 index 00000000000..afa4539cc61 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import android.util.Log; + +/** + * This abstract class is used to implement {@link BluetoothGatt} callbacks. + * @hide + */ +public abstract class BluetoothGattCallback { + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application + * was successfully registered. + */ + public void onAppRegistered(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothGatt#startScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { + } + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Callback invoked when the list of remote services, characteristics and + * descriptors for the remote device have been updated. + * + * @param device Remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device + * has been explored successfully. + */ + public void onServicesDiscovered(BluetoothDevice device, int status) { + } + + /** + * Callback reporting the result of a characteristic read operation. + * + * @param characteristic Characteristic that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully. + */ + public void onCharacteristicRead(BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback indicating the result of a characteristic write operation. + * + *

    If this callback is invoked while a reliable write transaction is + * in progress, the value of the characteristic represents the value + * reported by the remote device. An application should compare this + * value to the desired value to be written. If the values don't match, + * the application must abort the reliable write transaction. + * + * @param characteristic Characteristic that was written to the associated + * remote device. + * @param status The result of the write operation + */ + public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback triggered as a result of a remote characteristic notification. + * + * @param characteristic Characteristic that has been updated as a result + * of a remote notification event. + */ + public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) { + } + + /** + * Callback reporting the result of a descriptor read operation. + * + * @param descriptor Descriptor that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully + */ + public void onDescriptorRead(BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback indicating the result of a descriptor write operation. + * + * @param descriptor Descriptor that was writte to the associated + * remote device. + * @param status The result of the write operation + */ + public void onDescriptorWrite(BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback invoked when a reliable write transaction has been completed. + * + * @param device Remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write + * transaction was executed successfully + */ + public void onReliableWriteCompleted(BluetoothDevice device, int status) { + } + + /** + * Callback reporting the RSSI for a remote device connection. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#readRemoteRssi} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device + * @param status 0 if the RSSI was read successfully + */ + public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java new file mode 100644 index 00000000000..18492ab8ff6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Characteristic + * @hide + */ +public class BluetoothGattCharacteristic { + + /** + * Characteristic proprty: Characteristic is broadcastable. + */ + public static final int PROPERTY_BROADCAST = 0x01; + + /** + * Characteristic property: Characteristic is readable. + */ + public static final int PROPERTY_READ = 0x02; + + /** + * Characteristic property: Characteristic can be written without response. + */ + public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04; + + /** + * Characteristic property: Characteristic can be written. + */ + public static final int PROPERTY_WRITE = 0x08; + + /** + * Characteristic property: Characteristic supports notification + */ + public static final int PROPERTY_NOTIFY = 0x10; + + /** + * Characteristic property: Characteristic supports indication + */ + public static final int PROPERTY_INDICATE = 0x20; + + /** + * Characteristic property: Characteristic supports write with signature + */ + public static final int PROPERTY_SIGNED_WRITE = 0x40; + + /** + * Characteristic property: Characteristic has extended properties + */ + public static final int PROPERTY_EXTENDED_PROPS = 0x80; + + /** + * Characteristic read permission + */ + public static final int PERMISSION_READ = 0x01; + + /** + * Characteristic permission: Allow encrypted read operations + */ + public static final int PERMISSION_READ_ENCRYPTED = 0x02; + + /** + * Characteristic permission: Allow reading with man-in-the-middle protection + */ + public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; + + /** + * Characteristic write permission + */ + public static final int PERMISSION_WRITE = 0x10; + + /** + * Characteristic permission: Allow encrypted writes + */ + public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; + + /** + * Characteristic permission: Allow encrypted writes with man-in-the-middle + * protection + */ + public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; + + /** + * Characteristic permission: Allow signed write operations + */ + public static final int PERMISSION_WRITE_SIGNED = 0x80; + + /** + * Characteristic permission: Allow signed write operations with + * man-in-the-middle protection + */ + public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; + + /** + * Write characteristic, requesting acknoledgement by the remote device + */ + public static final int WRITE_TYPE_DEFAULT = 0x02; + + /** + * Wrtite characteristic without requiring a response by the remote device + */ + public static final int WRITE_TYPE_NO_RESPONSE = 0x01; + + /** + * Write characteristic including and authenticated signature + */ + public static final int WRITE_TYPE_SIGNED = 0x04; + + /** + * Characteristic value format type uint8 + */ + public static final int FORMAT_UINT8 = 0x11; + + /** + * Characteristic value format type uint16 + */ + public static final int FORMAT_UINT16 = 0x12; + + /** + * Characteristic value format type uint32 + */ + public static final int FORMAT_UINT32 = 0x14; + + /** + * Characteristic value format type sint8 + */ + public static final int FORMAT_SINT8 = 0x21; + + /** + * Characteristic value format type sint16 + */ + public static final int FORMAT_SINT16 = 0x22; + + /** + * Characteristic value format type sint32 + */ + public static final int FORMAT_SINT32 = 0x24; + + /** + * Characteristic value format type sfloat (16-bit float) + */ + public static final int FORMAT_SFLOAT = 0x32; + + /** + * Characteristic value format type float (32-bit float) + */ + public static final int FORMAT_FLOAT = 0x34; + + + /** + * The UUID of this characteristic. + * @hide + */ + protected UUID mUuid; + + /** + * Instance ID for this characteristic. + * @hide + */ + protected int mInstance; + + /** + * Characteristic properties. + * @hide + */ + protected int mProperties; + + /** + * Characteristic permissions. + * @hide + */ + protected int mPermissions; + + /** + * Key size (default = 16). + * @hide + */ + protected int mKeySize = 16; + + /** + * Write type for this characteristic. + * See WRITE_TYPE_* constants. + * @hide + */ + protected int mWriteType; + + /** + * Back-reference to the service this characteristic belongs to. + * @hide + */ + protected BluetoothGattService mService; + + /** + * The cached value of this characteristic. + * @hide + */ + protected byte[] mValue; + + /** + * List of descriptors included in this characteristic. + */ + protected List mDescriptors; + + /** + * Create a new BluetoothGattCharacteristic + * @hide + */ + /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, + UUID uuid, int instanceId, + int properties, int permissions) { + mUuid = uuid; + mInstance = instanceId; + mProperties = properties; + mPermissions = permissions; + mService = service; + mValue = null; + mDescriptors = new ArrayList(); + + if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) { + mWriteType = WRITE_TYPE_NO_RESPONSE; + } else { + mWriteType = WRITE_TYPE_DEFAULT; + } + } + + /** + * Returns the deisred key size. + * @hide + */ + /*package*/ int getKeySize() { + return mKeySize; + } + + /** + * Add a descriptor to this characteristic + * @hide + */ + /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) { + mDescriptors.add(descriptor); + } + + /** + * Returns the service this characteristic belongs to. + * @return The asscociated service + */ + public BluetoothGattService getService() { + return mService; + } + + /** + * Returns the UUID of this characteristic + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this characteristic + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this characteristic. + * + *

    If a remote device offers multiple characteristics with the same UUID, + * the instance ID is used to distuinguish between characteristics. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Instance ID of this characteristic + */ + public int getInstanceId() { + return mInstance; + } + + /** + * Returns the properties of this characteristic. + * + *

    The properties contain a bit mask of property flags indicating + * the features of this characteristic. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Properties of this characteristic + */ + public int getProperties() { + return mProperties; + } + + /** + * Returns the permissions for this characteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Permissions of this characteristic + */ + public int getPermissions() { + return mPermissions; + } + + /** + * Gets the write type for this characteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Write type for this characteristic + */ + public int getWriteType() { + return mWriteType; + } + + /** + * Set the write type for this characteristic + * + *

    Setting the write type of a characteristic determines how the + * {@link BluetoothGatt#writeCharacteristic} function write this + * characteristic. + * + *

    The default write type for a characteristic is + * {@link #WRITE_TYPE_DEFAULT}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param writeType The write type to for this characteristic. Can be one + * of: + * {@link #WRITE_TYPE_DEFAULT}, + * {@link #WRITE_TYPE_NO_RESPONSE} or + * {@link #WRITE_TYPE_SIGNED}. + */ + public void setWriteType(int writeType) { + mWriteType = writeType; + } + + /** + * Returns a list of descriptors for this characteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Descriptors for this characteristic + */ + public List getDescriptors() { + return mDescriptors; + } + + /** + * Returns a descriptor with a given UUID out of the list of + * descriptors for this characteristic. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Gatt descriptor object or null if no descriptor with the + * given UUID was found. + */ + public BluetoothGattDescriptor getDescriptor(UUID uuid) { + for(BluetoothGattDescriptor descriptor : mDescriptors) { + if (descriptor.getUuid().equals(uuid)) { + return descriptor; + } + } + return null; + } + + /** + * Get the stored value for this characteristic. + * + *

    This function returns the stored value for this characteristic as + * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached + * value of the characteristic is updated as a result of a read characteristic + * operation or if a characteristic update notification has been received. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Cached value of the characteristic + */ + public byte[] getValue() { + return mValue; + } + + /** + * Return the stored value of this characteristic. + * + *

    The formatType parameter determines how the characteristic value + * is to be interpreted. For example, settting formatType to + * {@link #FORMAT_UINT16} specifies that the first two bytes of the + * characteristic value at the given offset are interpreted to generate the + * return value. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param formatType The format type used to interpret the characteristic + * value. + * @param offset Offset at which the integer value can be found. + * @return Cached value of the characteristic or null of offset exceeds + * value size. + */ + public Integer getIntValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > mValue.length) return null; + + switch (formatType) { + case FORMAT_UINT8: + return unsignedByteToInt(mValue[offset]); + + case FORMAT_UINT16: + return unsignedBytesToInt(mValue[offset], mValue[offset+1]); + + case FORMAT_UINT32: + return unsignedBytesToInt(mValue[offset], mValue[offset+1], + mValue[offset+2], mValue[offset+3]); + case FORMAT_SINT8: + return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8); + + case FORMAT_SINT16: + return unsignedToSigned(unsignedBytesToInt(mValue[offset], + mValue[offset+1]), 16); + + case FORMAT_SINT32: + return unsignedToSigned(unsignedBytesToInt(mValue[offset], + mValue[offset+1], mValue[offset+2], mValue[offset+3]), 32); + } + + return null; + } + + /** + * Return the stored value of this characteristic. + *

    See {@link #getValue} for details. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param formatType The format type used to interpret the characteristic + * value. + * @param offset Offset at which the float value can be found. + * @return Cached value of the characteristic at a given offset or null + * if the requested offset exceeds the value size. + */ + public Float getFloatValue(int formatType, int offset) { + if ((offset + getTypeLen(formatType)) > mValue.length) return null; + + switch (formatType) { + case FORMAT_SFLOAT: + return bytesToFloat(mValue[offset], mValue[offset+1]); + + case FORMAT_FLOAT: + return bytesToFloat(mValue[offset], mValue[offset+1], + mValue[offset+2], mValue[offset+3]); + } + + return null; + } + + /** + * Return the stored value of this characteristic. + *

    See {@link #getValue} for details. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param offset Offset at which the string value can be found. + * @return Cached value of the characteristic + */ + public String getStringValue(int offset) { + if (offset > mValue.length) return null; + byte[] strBytes = new byte[mValue.length - offset]; + for (int i=0; i != (mValue.length-offset); ++i) strBytes[i] = mValue[offset+i]; + return new String(strBytes); + } + + /** + * Updates the locally stored value of this characteristic. + * + *

    This function modifies the locally stored cached value of this + * characteristic. To send the value to the remote device, call + * {@link BluetoothGatt#writeCharacteristic} to send the value to the + * remote device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this characteristic + * @return true if the locally stored value has been set, false if the + * requested value could not be stored locally. + */ + public boolean setValue(byte[] value) { + mValue = value; + return true; + } + + /** + * Set the locally stored value of this characteristic. + *

    See {@link #setValue(byte[])} for details. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this characteristic + * @param formatType Integer format type used to transform the value parameter + * @param offset Offset at which the value should be placed + * @return true if the locally stored value has been set + */ + public boolean setValue(int value, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (mValue == null) mValue = new byte[len]; + if (len > mValue.length) return false; + + switch (formatType) { + case FORMAT_SINT8: + value = intToSignedBits(value, 8); + // Fall-through intended + case FORMAT_UINT8: + mValue[offset] = (byte)(value & 0xFF); + break; + + case FORMAT_SINT16: + value = intToSignedBits(value, 16); + // Fall-through intended + case FORMAT_UINT16: + mValue[offset++] = (byte)(value & 0xFF); + mValue[offset] = (byte)((value >> 8) & 0xFF); + break; + + case FORMAT_SINT32: + value = intToSignedBits(value, 32); + // Fall-through intended + case FORMAT_UINT32: + mValue[offset++] = (byte)(value & 0xFF); + mValue[offset++] = (byte)((value >> 8) & 0xFF); + mValue[offset] = (byte)((value >> 16) & 0xFF); + break; + + default: + return false; + } + return true; + } + + /** + * Set the locally stored value of this characteristic. + *

    See {@link #setValue(byte[])} for details. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param mantissa Mantissa for this characteristic + * @param exponent exponent value for this characteristic + * @param formatType Float format type used to transform the value parameter + * @param offset Offset at which the value should be placed + * @return true if the locally stored value has been set + */ + public boolean setValue(int mantissa, int exponent, int formatType, int offset) { + int len = offset + getTypeLen(formatType); + if (mValue == null) mValue = new byte[len]; + if (len > mValue.length) return false; + + switch (formatType) { + case FORMAT_SFLOAT: + mantissa = intToSignedBits(mantissa, 12); + exponent = intToSignedBits(exponent, 4); + mValue[offset++] = (byte)(mantissa & 0xFF); + mValue[offset] = (byte)((mantissa >> 8) & 0x0F); + mValue[offset] += (byte)((exponent & 0x0F) << 4); + break; + + case FORMAT_FLOAT: + mantissa = intToSignedBits(mantissa, 24); + exponent = intToSignedBits(exponent, 8); + mValue[offset++] = (byte)(mantissa & 0xFF); + mValue[offset++] = (byte)((mantissa >> 8) & 0xFF); + mValue[offset++] = (byte)((mantissa >> 16) & 0xFF); + mValue[offset] += (byte)(exponent & 0xFF); + break; + + default: + return false; + } + + return true; + } + + /** + * Set the locally stored value of this characteristic. + *

    See {@link #setValue(byte[])} for details. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * @param value New value for this characteristic + * @return true if the locally stored value has been set + */ + public boolean setValue(String value) { + mValue = value.getBytes(); + return true; + } + + /** + * Returns the size of a give value type. + * @hide + */ + private int getTypeLen(int formatType) { + return formatType & 0xF; + } + + /** + * Convert a signed byte to an unsigned int. + * @hide + */ + private int unsignedByteToInt(byte b) { + return b & 0xFF; + } + + /** + * Convert signed bytes to a 16-bit unsigned int. + * @hide + */ + private int unsignedBytesToInt(byte b0, byte b1) { + return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)); + } + + /** + * Convert signed bytes to a 32-bit unsigned int. + * @hide + */ + private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { + return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) + + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24); + } + + /** + * Convert signed bytes to a 16-bit short float value. + * @hide + */ + private float bytesToFloat(byte b0, byte b1) { + int mantissa = unsignedToSigned(unsignedByteToInt(b0) + + ((unsignedByteToInt(b1) & 0x0F) << 8), 12); + int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4); + return (float)(mantissa * Math.pow(10, exponent)); + } + + /** + * Convert signed bytes to a 32-bit short float value. + * @hide + */ + private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { + int mantissa = unsignedToSigned(unsignedByteToInt(b0) + + (unsignedByteToInt(b1) << 8) + + (unsignedByteToInt(b2) << 16), 24); + return (float)(mantissa * Math.pow(10, b3)); + } + + /** + * Convert an unsigned integer value to a two's-complement encoded + * signed value. + * @hide + */ + private int unsignedToSigned(int unsigned, int size) { + if ((unsigned & (1 << size-1)) != 0) { + unsigned = -1 * ((1 << size-1) - (unsigned & ((1 << size-1) - 1))); + } + return unsigned; + } + + /** + * Convert an integer into the signed bits of a given length. + * @hide + */ + private int intToSignedBits(int i, int size) { + if (i < 0) { + i = (1 << size-1) + (i & ((1 << size-1) - 1)); + } + return i; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java new file mode 100644 index 00000000000..ba1f28afd79 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Descriptor + * @hide + */ +public class BluetoothGattDescriptor { + + /** + * Value used to enable notification for a client configuration descriptor + */ + public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00}; + + /** + * Value used to enable indication for a client configuration descriptor + */ + public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00}; + + /** + * Value used to disable notifications or indicatinos + */ + public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00}; + + /** + * Descriptor read permission + */ + public static final int PERMISSION_READ = 0x01; + + /** + * Descriptor permission: Allow encrypted read operations + */ + public static final int PERMISSION_READ_ENCRYPTED = 0x02; + + /** + * Descriptor permission: Allow reading with man-in-the-middle protection + */ + public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; + + /** + * Descriptor write permission + */ + public static final int PERMISSION_WRITE = 0x10; + + /** + * Descriptor permission: Allow encrypted writes + */ + public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; + + /** + * Descriptor permission: Allow encrypted writes with man-in-the-middle + * protection + */ + public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; + + /** + * Descriptor permission: Allow signed write operations + */ + public static final int PERMISSION_WRITE_SIGNED = 0x80; + + /** + * Descriptor permission: Allow signed write operations with + * man-in-the-middle protection + */ + public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; + + /** + * The UUID of this descriptor. + * @hide + */ + protected UUID mUuid; + + /** + * Permissions for this descriptor + * @hide + */ + protected int mPermissions; + + /** + * Back-reference to the characteristic this descriptor belongs to. + * @hide + */ + protected BluetoothGattCharacteristic mCharacteristic; + + /** + * The value for this descriptor. + * @hide + */ + protected byte[] mValue; + + /** + * Create a new BluetoothGattDescriptor. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristic this descriptor belongs to + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, + int permissions) { + mCharacteristic = characteristic; + mUuid = uuid; + mPermissions = permissions; + } + + /** + * Returns the characteristic this descriptor belongs to. + * @return The characteristic. + */ + public BluetoothGattCharacteristic getCharacteristic() { + return mCharacteristic; + } + + /** + * Returns the UUID of this descriptor. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this descriptor + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the permissions for this descriptor. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Permissions of this descriptor + */ + public int getPermissions() { + return mPermissions; + } + + /** + * Returns the stored value for this descriptor + * + *

    This function returns the stored value for this descriptor as + * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached + * value of the descriptor is updated as a result of a descriptor read + * operation. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Cached value of the descriptor + */ + public byte[] getValue() { + return mValue; + } + + /** + * Updates the locally stored value of this descriptor. + * + *

    This function modifies the locally stored cached value of this + * descriptor. To send the value to the remote device, call + * {@link BluetoothGatt#writeDescriptor} to send the value to the + * remote device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param value New value for this descriptor + * @return true if the locally stored value has been set, false if the + * requested value could not be stored locally. + */ + public boolean setValue(byte[] value) { + mValue = value; + return true; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java new file mode 100644 index 00000000000..91a1a9443f8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -0,0 +1,900 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothStateChangeCallback; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Public API for the Bluetooth Gatt Profile server role. + * + *

    This class provides Bluetooth Gatt server role functionality, + * allowing applications to create and advertise Bluetooth Smart services + * and characteristics. + * + *

    BluetoothGattServer is a proxy object for controlling the Bluetooth Service + * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothGatt proxy object. + * @hide + */ +public final class BluetoothGattServer implements BluetoothProfile { + private static final String TAG = "BluetoothGattServer"; + private static final boolean DBG = true; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothGatt mService; + private BluetoothGattServerCallback mCallback; + private int mServerIf; + + private List mServices; + + /** + * Bluetooth state change handlers + */ + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); + if (!mContext.bindService(new + Intent(IBluetoothGatt.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth GATT Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + + /** + * Service binder handling + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothGatt.Stub.asInterface(service); + ServiceListener serviceListner = mServiceListener; + if (serviceListner != null) { + serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER, + BluetoothGattServer.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + ServiceListener serviceListner = mServiceListener; + if (serviceListner != null) { + serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER); + } + } + }; + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattServerCallback mBluetoothGattServerCallback = + new IBluetoothGattServerCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + public void onServerRegistered(int status, int serverIf) { + if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status + + " serverIf=" + serverIf); + mServerIf = serverIf; + try { + mCallback.onAppRegistered(status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onScanResult(mAdapter.getRemoteDevice(address), + rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Server connection state changed + * @hide + */ + public void onServerConnectionState(int status, int serverIf, + boolean connected, String address) { + if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status + + " serverIf=" + serverIf + " device=" + address); + try { + mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, + connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Service has been added + * @hide + */ + public void onServiceAdded(int status, int srvcType, + int srvcInstId, ParcelUuid srvcId) { + UUID srvcUuid = srvcId.getUuid(); + if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid + + "status=" + status); + + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + try { + mCallback.onServiceAdded((int)status, service); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client characteristic read request. + * @hide + */ + public void onCharacteristicReadRequest(String address, int transId, + int offset, boolean isLong, int srvcType, int srvcInstId, + ParcelUuid srvcId, int charInstId, ParcelUuid charId) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic( + charUuid); + if (characteristic == null) return; + + try { + mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client descriptor read request. + * @hide + */ + public void onDescriptorReadRequest(String address, int transId, + int offset, boolean isLong, int srvcType, int srvcInstId, + ParcelUuid srvcId, int charInstId, ParcelUuid charId, + ParcelUuid descrId) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + UUID descrUuid = descrId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid + + "descriptor=" + descrUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); + if (descriptor == null) return; + + try { + mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Remote client characteristic write request. + * @hide + */ + public void onCharacteristicWriteRequest(String address, int transId, + int offset, int length, boolean isPrep, boolean needRsp, + int srvcType, int srvcInstId, ParcelUuid srvcId, + int charInstId, ParcelUuid charId, byte[] value) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + if (DBG) Log.d(TAG, "onCharacteristicWriteRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + try { + mCallback.onCharacteristicWriteRequest(device, transId, characteristic, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + + } + + /** + * Remote client descriptor write request. + * @hide + */ + public void onDescriptorWriteRequest(String address, int transId, + int offset, int length, boolean isPrep, boolean needRsp, + int srvcType, int srvcInstId, ParcelUuid srvcId, + int charInstId, ParcelUuid charId, ParcelUuid descrId, + byte[] value) { + UUID srvcUuid = srvcId.getUuid(); + UUID charUuid = charId.getUuid(); + UUID descrUuid = descrId.getUuid(); + if (DBG) Log.d(TAG, "onDescriptorWriteRequest() - " + + "service=" + srvcUuid + ", characteristic=" + charUuid + + "descriptor=" + descrUuid); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + + BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); + if (service == null) return; + + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); + if (characteristic == null) return; + + BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); + if (descriptor == null) return; + + try { + mCallback.onDescriptorWriteRequest(device, transId, descriptor, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Execute pending writes. + * @hide + */ + public void onExecuteWrite(String address, int transId, + boolean execWrite) { + if (DBG) Log.d(TAG, "onExecuteWrite() - " + + "device=" + address + ", transId=" + transId + + "execWrite=" + execWrite); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onExecuteWrite(device, transId, execWrite); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + }; + + /** + * Create a BluetoothGattServer proxy object. + */ + /*package*/ BluetoothGattServer(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServices = new ArrayList(); + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); + } + } else { + Log.e(TAG, "Unable to get BluetoothManager interface."); + throw new RuntimeException("BluetoothManager inactive"); + } + + //Bind to the service only if the Bluetooth is ON + if(mAdapter.isEnabled()){ + if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); + } + } + } + + /** + * Close the connection to the gatt service. + */ + /*package*/ void close() { + if (DBG) Log.d(TAG, "close()"); + + unregisterApp(); + mServiceListener = null; + + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); + } + } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + + /** + * Returns a service by UUID, instance and type. + * @hide + */ + /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; + } + } + return null; + } + + /** + * Register an application callback to start using Gatt. + * + *

    This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback Gatt callback handler that will receive asynchronous + * callbacks. + * @return true, if application was successfully registered. + */ + public boolean registerApp(BluetoothGattServerCallback callback) { + if (DBG) Log.d(TAG, "registerApp()"); + if (mService == null) return false; + + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); + + try { + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Unregister the current application and callbacks. + */ + public void unregisterApp() { + if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf); + if (mService == null || mServerIf == 0) return; + + try { + mCallback = null; + mService.unregisterServer(mServerIf); + mServerIf = 0; + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + *

    Results of the scan are reported using the + * {@link BluetoothGattServerCallback#onScanResult} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startScan() { + if (DBG) Log.d(TAG, "startScan()"); + if (mService == null || mServerIf == 0) return false; + + try { + mService.startScan(mServerIf, true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + *

    Devices which advertise all specified services are reported using the + * {@link BluetoothGattServerCallback#onScanResult} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startScan() - with UUIDs"); + if (mService == null || mServerIf == 0) return false; + + try { + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + mService.startScanWithUuids(mServerIf, true, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mService == null || mServerIf == 0) return; + + try { + mService.stopScan(mServerIf, true); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Initiate a connection to a Bluetooth Gatt capable device. + * + *

    The connection may not be established right away, but will be + * completed when the remote device is available. A + * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * invoked when the connection state changes as a result of this function. + * + *

    The autoConnect paramter determines whether to actively connect to + * the remote device, or rather passively scan and finalize the connection + * when the remote device is in range/available. Generally, the first ever + * connection to a device should be direct (autoConnect set to false) and + * subsequent connections to known devices should be invoked with the + * autoConnect parameter set to false. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device to connect to + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect(BluetoothDevice device, boolean autoConnect) { + if (DBG) Log.d(TAG, "connect: " + device.getAddress() + ", auto: " + autoConnect); + if (mService == null || mServerIf == 0) return false; + + try { + mService.serverConnect(mServerIf, device.getAddress(), + autoConnect ? false : true); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Disconnects an established connection, or cancels a connection attempt + * currently in progress. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote device + */ + public void cancelConnection(BluetoothDevice device) { + if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return; + + try { + mService.serverDisconnect(mServerIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Send a response to a read or write request to a remote device. + * + *

    This function must be invoked in when a remote read/write request + * is received by one of these callback methots: + * + *

      + *
    • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} + *
    • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} + *
    • {@link BluetoothGattServerCallback#onDescriptorReadRequest} + *
    • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} + *
    + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote device to send this response to + * @param requestId The ID of the request that was received with the callback + * @param status The status of the request to be sent to the remote devices + * @param offset Value offset for partial read/write response + * @param value The value of the attribute that was read/written (optional) + */ + public boolean sendResponse(BluetoothDevice device, int requestId, + int status, int offset, byte[] value) { + if (DBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return false; + + try { + mService.sendResponse(mServerIf, device.getAddress(), requestId, + status, offset, value); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + return true; + } + + /** + * Send a notification or indication that a local characteristic has been + * updated. + * + *

    A notification or indication is sent to the remote device to signal + * that the characteristic has been updated. This function should be invoked + * for every client that requests notifications/indications by writing + * to the "Client Configuration" descriptor for the given characteristic. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device The remote device to receive the notification/indication + * @param characteristic The local characteristic that has been updated + * @param confirm true to request confirmation from the client (indication), + * false to send a notification + * @return true, if the notification has been triggered successfully + */ + public boolean notifyCharacteristicChanged(BluetoothDevice device, + BluetoothGattCharacteristic characteristic, boolean confirm) { + if (DBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); + if (mService == null || mServerIf == 0) return false; + + BluetoothGattService service = characteristic.getService(); + if (service == null) return false; + + try { + mService.sendNotification(mServerIf, device.getAddress(), + service.getType(), service.getInstanceId(), + new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), + new ParcelUuid(characteristic.getUuid()), confirm, + characteristic.getValue()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Add a service to the list of services to be advertised. + * + *

    Once a service has been addded to the the list, the service and it's + * included characteristics will be advertised by the local device. + * + *

    If the local device is already advertising services when this function + * is called, a service update notification will be sent to all clients. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service Service to be added to the list of services advertised + * by this device. + * @return true, if the service has been added successfully + */ + public boolean addService(BluetoothGattService service) { + if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); + if (mService == null || mServerIf == 0) return false; + + mServices.add(service); + + try { + mService.beginServiceDeclaration(mServerIf, service.getType(), + service.getInstanceId(), service.getHandles(), + new ParcelUuid(service.getUuid())); + + List includedServices = service.getIncludedServices(); + for (BluetoothGattService includedService : includedServices) { + mService.addIncludedService(mServerIf, + includedService.getType(), + includedService.getInstanceId(), + new ParcelUuid(includedService.getUuid())); + } + + List characteristics = service.getCharacteristics(); + for (BluetoothGattCharacteristic characteristic : characteristics) { + int permission = ((characteristic.getKeySize() - 7) << 12) + + characteristic.getPermissions(); + mService.addCharacteristic(mServerIf, + new ParcelUuid(characteristic.getUuid()), + characteristic.getProperties(), permission); + + List descriptors = characteristic.getDescriptors(); + for (BluetoothGattDescriptor descriptor: descriptors) { + mService.addDescriptor(mServerIf, + new ParcelUuid(descriptor.getUuid()), + descriptor.getPermissions()); + } + } + + mService.endServiceDeclaration(mServerIf); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Removes a service from the list of services to be advertised. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service Service to beremoved. + * @return true, if the service has been removed + */ + public boolean removeService(BluetoothGattService service) { + if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); + if (mService == null || mServerIf == 0) return false; + + BluetoothGattService intService = getService(service.getUuid(), + service.getInstanceId(), service.getType()); + if (intService == null) return false; + + try { + mService.removeService(mServerIf, service.getType(), + service.getInstanceId(), new ParcelUuid(service.getUuid())); + mServices.remove(intService); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Remove all services from the list of advertised services. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void clearServices() { + if (DBG) Log.d(TAG, "clearServices()"); + if (mService == null || mServerIf == 0) return; + + try { + mService.clearServices(mServerIf); + mServices.clear(); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Returns a list of GATT services offered bu this device. + * + *

    An application must call {@link #addService} to add a serice to the + * list of services offered by this device. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of services. Returns an empty list + * if no services have been added yet. + */ + public List getServices() { + return mServices; + } + + /** + * Returns a {@link BluetoothGattService} from the list of services offered + * by this device. + * + *

    If multiple instances of the same service (as identified by UUID) + * exist, the first instance of the service is returned. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid UUID of the requested service + * @return BluetoothGattService if supported, or null if the requested + * service is not offered by this device. + */ + public BluetoothGattService getService(UUID uuid) { + for (BluetoothGattService service : mServices) { + if (service.getUuid().equals(uuid)) { + return service; + } + } + + return null; + } + + /** + * Get the current connection state of the profile. + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @return State of the profile connection. One of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG,"getConnectionState()"); + if (mService == null) return STATE_DISCONNECTED; + + List connectedDevices = getConnectedDevices(); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return STATE_CONNECTED; + } + } + + return STATE_DISCONNECTED; + } + + /** + * Get connected devices for the Gatt profile. + * + *

    Return the set of devices which are in state {@link #STATE_CONNECTED} + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of devices. The list will be empty on error. + */ + @Override + public List getConnectedDevices() { + if (DBG) Log.d(TAG,"getConnectedDevices"); + + List connectedDevices = new ArrayList(); + if (mService == null) return connectedDevices; + + try { + connectedDevices = mService.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * Get a list of devices that match any of the given connection + * states. + * + *

    If none of the devices match any of the given states, + * an empty list will be returned. + * + *

    This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param states Array of states. States can be one of + * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + List devices = new ArrayList(); + if (mService == null) return devices; + + try { + devices = mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java new file mode 100644 index 00000000000..4f608ff754c --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import android.util.Log; + +/** + * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + * @hide + */ +public abstract class BluetoothGattServerCallback { + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application + * was successfully registered. + */ + public void onAppRegistered(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothGattServer#startScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { + } + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Indicates whether a local service has been added successfully. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service + * was added successfully. + * @param service The service that has been added + */ + public void onServiceAdded(int status, BluetoothGattService service) { + } + + /** + * A remote client has requested to read a local characteristic. + * + *

    An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param characteristic Characteristic to be read + */ + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattCharacteristic characteristic) { + } + + /** + * A remote client has requested to write to a local characteristic. + * + *

    An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param characteristic Characteristic to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the characteristic + */ + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * A remote client has requested to read a local descriptor. + * + *

    An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param descriptor Descriptor to be read + */ + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattDescriptor descriptor) { + } + + /** + * A remote client has requested to write to a local descriptor. + * + *

    An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param descriptor Descriptor to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the descriptor + */ + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * Execute all pending write operations for this device. + * + *

    An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operations + * @param requestId The Id of the request + * @param execute Whether the pending writes should be executed (true) or + * cancelled (false) + */ + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java new file mode 100644 index 00000000000..6a3ce66e8f2 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Service + * @hide + */ +public class BluetoothGattService { + + /** + * Primary service + */ + public static final int SERVICE_TYPE_PRIMARY = 0; + + /** + * Secondary service (included by primary services) + */ + public static final int SERVICE_TYPE_SECONDARY = 1; + + + /** + * The remote device his service is associated with. + * This applies to client applications only. + * @hide + */ + protected BluetoothDevice mDevice; + + /** + * The UUID of this service. + * @hide + */ + protected UUID mUuid; + + /** + * Instance ID for this service. + * @hide + */ + protected int mInstanceId; + + /** + * Handle counter override (for conformance testing). + * @hide + */ + protected int mHandles = 0; + + /** + * Service type (Primary/Secondary). + * @hide + */ + protected int mServiceType; + + /** + * List of characteristics included in this service. + */ + protected List mCharacteristics; + + /** + * List of included services for this service. + */ + protected List mIncludedServices; + + /** + * Create a new BluetoothGattService. + * @hide + */ + /*package*/ BluetoothGattService(UUID uuid, int serviceType) { + mDevice = null; + mUuid = uuid; + mInstanceId = 0; + mServiceType = serviceType; + mCharacteristics = new ArrayList(); + mIncludedServices = new ArrayList(); + } + + /** + * Create a new BluetoothGattService + * @hide + */ + /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid, + int instanceId, int serviceType) { + mDevice = device; + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + mCharacteristics = new ArrayList(); + mIncludedServices = new ArrayList(); + } + + /** + * Returns the device associated with this service. + * @hide + */ + /*package*/ BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Add a characteristic to this service. + * @hide + */ + /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristics.add(characteristic); + } + + /** + * Get characteristic by UUID and instanceId. + * @hide + */ + /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) { + for(BluetoothGattCharacteristic characteristic : mCharacteristics) { + if (uuid.equals(characteristic.getUuid()) && + mInstanceId == instanceId) + return characteristic; + } + return null; + } + + /** + * Get the handle count override (conformance testing. + * @hide + */ + /*package*/ int getHandles() { + return mHandles; + } + + /** + * Add an included service to the internal map. + * @hide + */ + /*package*/ void addIncludedService(BluetoothGattService includedService) { + mIncludedServices.add(includedService); + } + + /** + * Returns the UUID of this service + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return UUID of this service + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this service + * + *

    If a remote device offers multiple services with the same UUID + * (ex. multiple battery services for different batteries), the instance + * ID is used to distuinguish services. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Instance ID of this service + */ + public int getInstanceId() { + return mInstanceId; + } + + /** + * Get the type of this service (primary/secondary) + * @hide + */ + public int getType() { + return mServiceType; + } + + /** + * Get the list of included Gatt services for this service. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return List of included services or empty list if no included services + * were discovered. + */ + public List getIncludedServices() { + return mIncludedServices; + } + + /** + * Returns a list of characteristics included in this service. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Characteristics included in this service + */ + public List getCharacteristics() { + return mCharacteristics; + } + + /** + * Returns a characteristic with a given UUID out of the list of + * characteristics offered by this service. + * + *

    This is a convenience function to allow access to a given characteristic + * without enumerating over the list returned by {@link #getCharacteristics} + * manually. + * + *

    If a remote service offers multiple characteristics with the same + * UUID, the first instance of a characteristic with the given UUID + * is returned. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Gatt characteristic object or null if no characteristic with the + * given UUID was found. + */ + public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { + for(BluetoothGattCharacteristic characteristic : mCharacteristics) { + if (uuid.equals(characteristic.getUuid())) + return characteristic; + } + return null; + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java old mode 100755 new mode 100644 index 1920efa52f8..9ee202a5896 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -87,6 +87,18 @@ public interface BluetoothProfile { */ public static final int PBAP = 6; + /** + * GATT + * @hide + */ + static public final int GATT = 7; + + /** + * GATT_SERVER + * @hide + */ + static public final int GATT_SERVER = 8; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl new file mode 100644 index 00000000000..c89d132df7d --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.os.ParcelUuid; + +import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothGattServerCallback; + +/** + * API for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGatt { + List getDevicesMatchingConnectionStates(in int[] states); + + void startScan(in int appIf, in boolean isServer); + void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); + void stopScan(in int appIf, in boolean isServer); + + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); + void unregisterClient(in int clientIf); + void clientConnect(in int clientIf, in String address, in boolean isDirect); + void clientDisconnect(in int clientIf, in String address); + void refreshDevice(in int clientIf, in String address); + void discoverServices(in int clientIf, in String address); + void readCharacteristic(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in int authReq); + void writeCharacteristic(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in int writeType, in int authReq, in byte[] value); + void readDescriptor(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in ParcelUuid descrUuid, in int authReq); + void writeDescriptor(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in ParcelUuid descrId, in int writeType, + in int authReq, in byte[] value); + void registerForNotification(in int clientIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in boolean enable); + void beginReliableWrite(in int clientIf, in String address); + void endReliableWrite(in int clientIf, in String address, in boolean execute); + void readRemoteRssi(in int clientIf, in String address); + + void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); + void unregisterServer(in int serverIf); + void serverConnect(in int servertIf, in String address, in boolean isDirect); + void serverDisconnect(in int serverIf, in String address); + void beginServiceDeclaration(in int serverIf, in int srvcType, + in int srvcInstanceId, in int minHandles, + in ParcelUuid srvcId); + void addIncludedService(in int serverIf, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId); + void addCharacteristic(in int serverIf, in ParcelUuid charId, + in int properties, in int permissions); + void addDescriptor(in int serverIf, in ParcelUuid descId, + in int permissions); + void endServiceDeclaration(in int serverIf); + void removeService(in int serverIf, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId); + void clearServices(in int serverIf); + void sendResponse(in int serverIf, in String address, in int requestId, + in int status, in int offset, in byte[] value); + void sendNotification(in int serverIf, in String address, in int srvcType, + in int srvcInstanceId, in ParcelUuid srvcId, + in int charInstanceId, in ParcelUuid charId, + in boolean confirm, in byte[] value); +} diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl new file mode 100644 index 00000000000..fc521726c6a --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import android.os.ParcelUuid; + + +/** + * Callback definitions for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGattCallback { + void onClientRegistered(in int status, in int clientIf); + void onClientConnectionState(in int status, in int clientIf, + in boolean connected, in String address); + void onScanResult(in String address, in int rssi, in byte[] advData); + void onGetService(in String address, in int srvcType, in int srvcInstId, + in ParcelUuid srvcUuid); + void onGetIncludedService(in String address, in int srvcType, in int srvcInstId, + in ParcelUuid srvcUuid, in int inclSrvcType, + in int inclSrvcInstId, in ParcelUuid inclSrvcUuid); + void onGetCharacteristic(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in int charProps); + void onGetDescriptor(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid); + void onSearchComplete(in String address, in int status); + void onCharacteristicRead(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in byte[] value); + void onCharacteristicWrite(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid); + void onExecuteWrite(in String address, in int status); + void onDescriptorRead(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid, in byte[] value); + void onDescriptorWrite(in String address, in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in ParcelUuid descrUuid); + void onNotify(in String address, in int srvcType, + in int srvcInstId, in ParcelUuid srvcUuid, + in int charInstId, in ParcelUuid charUuid, + in byte[] value); + void onReadRemoteRssi(in String address, in int rssi, in int status); +} diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl new file mode 100644 index 00000000000..ae9bffcf4d3 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import android.os.ParcelUuid; + + +/** + * Callback definitions for interacting with BLE / GATT + * @hide + */ +interface IBluetoothGattServerCallback { + void onServerRegistered(in int status, in int serverIf); + void onScanResult(in String address, in int rssi, in byte[] advData); + void onServerConnectionState(in int status, in int serverIf, + in boolean connected, in String address); + void onServiceAdded(in int status, in int srvcType, + in int srvcInstId, in ParcelUuid srvcId); + void onCharacteristicReadRequest(in String address, in int transId, + in int offset, in boolean isLong, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId); + void onDescriptorReadRequest(in String address, in int transId, + in int offset, in boolean isLong, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in ParcelUuid descrId); + void onCharacteristicWriteRequest(in String address, in int transId, + in int offset, in int length, + in boolean isPrep, + in boolean needRsp, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in byte[] value); + void onDescriptorWriteRequest(in String address, in int transId, + in int offset, in int length, + in boolean isPrep, + in boolean needRsp, + in int srvcType, + in int srvcInstId, in ParcelUuid srvcId, + in int charInstId, in ParcelUuid charId, + in ParcelUuid descrId, + in byte[] value); + void onExecuteWrite(in String address, in int transId, in boolean execWrite); +} diff --git a/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java new file mode 100644 index 00000000000..c05abb21500 --- /dev/null +++ b/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import java.util.ArrayList; +import java.util.IllegalFormatConversionException; +import java.util.List; +import java.util.UUID; + +/** + * Mutable variant of a Bluetooth Gatt Characteristic + * @hide + */ +public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic { + + /** + * Create a new MutableBluetoothGattCharacteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this characteristic + * @param properties Properties of this characteristic + * @param permissions Permissions for this characteristic + */ + public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { + super(null, uuid, 0, properties, permissions); + } + + /** + * Adds a descriptor to this characteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to be added to this characteristic. + */ + public void addDescriptor(MutableBluetoothGattDescriptor descriptor) { + mDescriptors.add(descriptor); + descriptor.setCharacteristic(this); + } + + /** + * Set the desired key size. + * @hide + */ + public void setKeySize(int keySize) { + mKeySize = keySize; + } + + /** + * Sets the service associated with this device. + * @hide + */ + /*package*/ void setService(BluetoothGattService service) { + mService = service; + } +} diff --git a/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java new file mode 100644 index 00000000000..e4553920879 --- /dev/null +++ b/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import java.util.UUID; + +/** + * Mutable variant of a Bluetooth Gatt Descriptor + * @hide + */ +public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor { + + /** + * Create a new BluetoothGattDescriptor. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + public MutableBluetoothGattDescriptor(UUID uuid, int permissions) { + super(null, uuid, permissions); + } + + /** + * Set the back-reference to the associated characteristic + * @hide + */ + /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristic = characteristic; + } +} diff --git a/framework/java/android/bluetooth/MutableBluetoothGattService.java b/framework/java/android/bluetooth/MutableBluetoothGattService.java new file mode 100644 index 00000000000..927f5ab20b4 --- /dev/null +++ b/framework/java/android/bluetooth/MutableBluetoothGattService.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth Gatt Service + * @hide + */ +public class MutableBluetoothGattService extends BluetoothGattService { + + /** + * Create a new MutableBluetoothGattService. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this service + * @param serviceType The type of this service (primary/secondary) + */ + public MutableBluetoothGattService(UUID uuid, int serviceType) { + super(uuid, serviceType); + } + + /** + * Add an included service to this service. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service The service to be added + * @return true, if the included service was added to the service + */ + public boolean addService(BluetoothGattService service) { + mIncludedServices.add(service); + return true; + } + + /** + * Add a characteristic to this service. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristics to be added + * @return true, if the characteristic was added to the service + */ + public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) { + mCharacteristics.add(characteristic); + characteristic.setService(this); + return true; + } + + /** + * Force the instance ID. + * This is needed for conformance testing only. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstanceId = instanceId; + } + + /** + * Force the number of handles to reserve for this service. + * This is needed for conformance testing only. + * @hide + */ + public void setHandles(int handles) { + mHandles = handles; + } +} -- GitLab From d6f9701119db423333a50bfa286968cdb880512f Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Thu, 28 Feb 2013 14:23:00 -0800 Subject: [PATCH 0255/1408] Fix docs bug. Change-Id: I8c69963b339d703757bf1cd8f43161175783566d --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1bbfb5dfb31..b00bf095981 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1137,8 +1137,7 @@ public final class BluetoothAdapter { * Get the profile proxy object associated with the profile. * *

    Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, - * or {@link BluetoothProfile#GATT_SERVER}. Clients must implements + * or {@link BluetoothProfile#A2DP}. Clients must implement * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * -- GitLab From 137ce5ad1b743fef120f41dd3a8641502d83f3fb Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 8 Mar 2013 18:42:24 -0800 Subject: [PATCH 0256/1408] Fixed 32 bit characteristic value assignment Also fixed link in comment. Change-Id: I27223b8aadae2fc7c8037abc6396760e23f6f161 --- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 3 ++- framework/java/android/bluetooth/BluetoothGattServer.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 18492ab8ff6..f44dc5c08bb 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -529,7 +529,8 @@ public class BluetoothGattCharacteristic { case FORMAT_UINT32: mValue[offset++] = (byte)(value & 0xFF); mValue[offset++] = (byte)((value >> 8) & 0xFF); - mValue[offset] = (byte)((value >> 16) & 0xFF); + mValue[offset++] = (byte)((value >> 16) & 0xFF); + mValue[offset] = (byte)((value >> 24) & 0xFF); break; default: diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 91a1a9443f8..6b693779153 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -534,7 +534,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

    The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

    The autoConnect paramter determines whether to actively connect to @@ -553,7 +553,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return true, if the connection attempt was initiated successfully */ public boolean connect(BluetoothDevice device, boolean autoConnect) { - if (DBG) Log.d(TAG, "connect: " + device.getAddress() + ", auto: " + autoConnect); + if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); if (mService == null || mServerIf == 0) return false; try { -- GitLab From 902c9c2230c18719cb9e8055513740b248effd0e Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 7 Mar 2013 11:01:12 -0800 Subject: [PATCH 0257/1408] Add stacking to the NetworkStateTrackers. Bug: 8276725 Change-Id: I55a107fea78f9cc84f15bd6c75292138fb35a1b6 --- .../bluetooth/BluetoothTetheringDataTracker.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 3ba4f267990..43c239252be 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -373,4 +373,14 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void setDependencyMet(boolean met) { // not supported on this network } + + @Override + public void addStackedLink(LinkProperties link) { + mLinkProperties.addStackedLink(link); + } + + @Override + public void removeStackedLink(LinkProperties link) { + mLinkProperties.removeStackedLink(link); + } } -- GitLab From 444c57eb7972244540e64538fd3474b5a5d6f3a2 Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Wed, 27 Mar 2013 16:52:35 +0530 Subject: [PATCH 0258/1408] Correct executable bit for source files [Take 2] Change Ieb51bafb46c895a21d2e83696f5a901ba752b2c5 left out some files, this fixes them. Change-Id: Ia949a8581668836ea0251602e048f09c852f5169 --- framework/java/android/bluetooth/BluetoothA2dp.java | 0 framework/java/android/bluetooth/BluetoothAdapter.java | 0 framework/java/android/bluetooth/BluetoothDevice.java | 0 framework/java/android/bluetooth/BluetoothHeadset.java | 0 framework/java/android/bluetooth/BluetoothInputDevice.java | 0 framework/java/android/bluetooth/BluetoothPbap.java | 0 framework/java/android/bluetooth/BluetoothProfile.java | 0 framework/java/android/bluetooth/IBluetoothInputDevice.aidl | 0 framework/java/android/bluetooth/IBluetoothManager.aidl | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothA2dp.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothAdapter.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothDevice.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothHeadset.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothInputDevice.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothPbap.java mode change 100755 => 100644 framework/java/android/bluetooth/BluetoothProfile.java mode change 100755 => 100644 framework/java/android/bluetooth/IBluetoothInputDevice.aidl mode change 100755 => 100644 framework/java/android/bluetooth/IBluetoothManager.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl old mode 100755 new mode 100644 diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl old mode 100755 new mode 100644 -- GitLab From daef329568bccaa8265a1af4ac62f64adc862d76 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Tue, 21 Aug 2012 19:27:00 -0700 Subject: [PATCH 0259/1408] Add BT - DataTracker connection Allows the external BT stack the means to communicate with ConnectivityService during reverse tethering. bug:8445208 Change-Id: Ice7dfb0b50c9481d359aed14a51372878185171c --- .../BluetoothTetheringDataTracker.java | 203 +++++++++++------- 1 file changed, 125 insertions(+), 78 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 43c239252be..81c0a6a87ea 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -21,6 +21,7 @@ import android.os.ServiceManager; import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; +import android.net.DhcpResults; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -28,7 +29,10 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkStateTracker; import android.net.NetworkUtils; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.Messenger; +import android.text.TextUtils; import android.util.Log; import java.net.InterfaceAddress; import android.net.LinkAddress; @@ -36,8 +40,11 @@ import android.net.RouteInfo; import java.net.Inet4Address; import android.os.SystemProperties; +import com.android.internal.util.AsyncChannel; + import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; /** * This class tracks the data connection associated with Bluetooth @@ -51,24 +58,29 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean VDBG = true; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); + private final Object mLinkPropertiesLock = new Object(); private LinkProperties mLinkProperties; + private LinkCapabilities mLinkCapabilities; + + private final Object mNetworkInfoLock = new Object(); private NetworkInfo mNetworkInfo; private BluetoothPan mBluetoothPan; - private static String mIface; - private Thread mDhcpThread; + private static String mRevTetheredIface; /* For sending events to connectivity service handler */ private Handler mCsHandler; - private Context mContext; - public static BluetoothTetheringDataTracker sInstance; + protected Context mContext; + private static BluetoothTetheringDataTracker sInstance; + private BtdtHandler mBtdtHandler; + private AtomicReference mAsyncChannel = new AtomicReference(null); private BluetoothTetheringDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); @@ -108,6 +120,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); } + mBtdtHandler = new BtdtHandler(target.getLooper(), this); } private BluetoothProfile.ServiceListener mProfileServiceListener = @@ -224,15 +237,19 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { /** * Fetch NetworkInfo for the network */ - public synchronized NetworkInfo getNetworkInfo() { - return mNetworkInfo; + public NetworkInfo getNetworkInfo() { + synchronized (mNetworkInfoLock) { + return new NetworkInfo(mNetworkInfo); + } } /** * Fetch LinkProperties for the network */ - public synchronized LinkProperties getLinkProperties() { - return new LinkProperties(mLinkProperties); + public LinkProperties getLinkProperties() { + synchronized (mLinkPropertiesLock) { + return new LinkProperties(mLinkProperties); + } } /** @@ -286,88 +303,68 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return count; } - - private boolean readLinkProperty(String iface) { - String DhcpPrefix = "dhcp." + iface + "."; - String ip = SystemProperties.get(DhcpPrefix + "ipaddress"); - String dns1 = SystemProperties.get(DhcpPrefix + "dns1"); - String dns2 = SystemProperties.get(DhcpPrefix + "dns2"); - String gateway = SystemProperties.get(DhcpPrefix + "gateway"); - String mask = SystemProperties.get(DhcpPrefix + "mask"); - if(ip.isEmpty() || gateway.isEmpty()) { - Log.e(TAG, "readLinkProperty, ip: " + ip + ", gateway: " + gateway + ", can not be empty"); - return false; + void startReverseTether(final LinkProperties linkProperties) { + if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) { + Log.e(TAG, "attempted to reverse tether with empty interface"); + return; } - int PrefixLen = countPrefixLength(NetworkUtils.numericToInetAddress(mask).getAddress()); - mLinkProperties.addLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(ip), PrefixLen)); - RouteInfo ri = new RouteInfo(NetworkUtils.numericToInetAddress(gateway)); - mLinkProperties.addRoute(ri); - if(!dns1.isEmpty()) - mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns1)); - if(!dns2.isEmpty()) - mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns2)); - mLinkProperties.setInterfaceName(iface); - return true; - } - public synchronized void startReverseTether(String iface) { - mIface = iface; - if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - mDhcpThread = new Thread(new Runnable() { + synchronized (mLinkPropertiesLock) { + if (mLinkProperties.getInterfaceName() != null) { + Log.e(TAG, "attempted to reverse tether while already in process"); + return; + } + mLinkProperties = linkProperties; + } + Thread dhcpThread = new Thread(new Runnable() { public void run() { - //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - String DhcpResultName = "dhcp." + mIface + ".result";; - String result = ""; - if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); - for(int i = 0; i < 30*5; i++) { - try { Thread.sleep(200); } catch (InterruptedException ie) { return;} - result = SystemProperties.get(DhcpResultName); - if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result); - if(result.equals("failed")) { - Log.e(TAG, "startReverseTether, failed to start dhcp service"); + DhcpResults dhcpResults = new DhcpResults(); + boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(), + dhcpResults); + synchronized (mLinkPropertiesLock) { + if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) { + Log.e(TAG, "obsolete DHCP run aborted"); return; } - if(result.equals("ok")) { - if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result); - if(readLinkProperty(mIface)) { - - mNetworkInfo.setIsAvailable(true); - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - - if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - if(mCsHandler != null) { - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - - msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); - } - } + if (!success) { + Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } + mLinkProperties = dhcpResults.linkProperties; + synchronized (mNetworkInfoLock) { + mNetworkInfo.setIsAvailable(true); + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + if (mCsHandler != null) { + Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, + new NetworkInfo(mNetworkInfo)); + msg.sendToTarget(); + } + } + return; } - Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); - mDhcpThread.start(); + dhcpThread.start(); } - public synchronized void stopReverseTether() { - //NetworkUtils.stopDhcp(iface); - if(mDhcpThread != null && mDhcpThread.isAlive()) { - mDhcpThread.interrupt(); - try { mDhcpThread.join(); } catch (InterruptedException ie) { return; } + void stopReverseTether() { + synchronized (mLinkPropertiesLock) { + if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) { + Log.e(TAG, "attempted to stop reverse tether with nothing tethered"); + return; + } + NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName()); + mLinkProperties.clear(); + synchronized (mNetworkInfoLock) { + mNetworkInfo.setIsAvailable(false); + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); + + if (mCsHandler != null) { + mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)). + sendToTarget(); + } + } } - mLinkProperties.clear(); - mNetworkInfo.setIsAvailable(false); - mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - - msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); } public void setDependencyMet(boolean met) { @@ -383,4 +380,54 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } + + static class BtdtHandler extends Handler { + private AsyncChannel mStackChannel; + private final BluetoothTetheringDataTracker mBtdt; + + BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) { + super(looper); + mBtdt = parent; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED"); + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + AsyncChannel ac = (AsyncChannel)msg.obj; + if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) { + Log.e(TAG, "Trying to set mAsyncChannel twice!"); + } else { + ac.sendMessage( + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } + } + break; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED"); + mBtdt.stopReverseTether(); + mBtdt.mAsyncChannel.set(null); + break; + case NetworkStateTracker.EVENT_NETWORK_CONNECTED: + LinkProperties linkProperties = (LinkProperties)(msg.obj); + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties); + mBtdt.startReverseTether(linkProperties); + break; + case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED: + linkProperties = (LinkProperties)(msg.obj); + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); + mBtdt.stopReverseTether(); + break; + } + } + } + + @Override + public void supplyMessenger(Messenger messenger) { + if (messenger != null) { + new AsyncChannel().connect(mContext, mBtdtHandler, messenger); + } + } } -- GitLab From 9afa274491ca272dc30f6f6211ef2719b7a2226e Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Fri, 1 Mar 2013 18:41:02 -0800 Subject: [PATCH 0260/1408] Unhide Bluetooth Low Energy public APIs Updated API headers. Add BluetoothManager to be retrieved by context.getSystemService(Context.BLUETOOTH_SERVICE). LE scan functions are placed in BluetoothAdapter The GATT API are device driven instead of a profile-driver. bug 8450158 Change-Id: I424a4cedaac3ef8120a05996500008dd210d2553 --- .../android/bluetooth/BluetoothAdapter.java | 238 +++++++- .../bluetooth/BluetoothAdapterCallback.java | 57 ++ .../android/bluetooth/BluetoothDevice.java | 27 + .../java/android/bluetooth/BluetoothGatt.java | 525 ++++++------------ .../bluetooth/BluetoothGattCallback.java | 42 +- .../BluetoothGattCharacteristic.java | 91 +-- .../bluetooth/BluetoothGattDescriptor.java | 35 +- .../bluetooth/BluetoothGattServer.java | 408 ++++---------- .../BluetoothGattServerCallback.java | 36 +- .../bluetooth/BluetoothGattService.java | 63 ++- .../android/bluetooth/BluetoothManager.java | 219 ++++++++ .../android/bluetooth/BluetoothProfile.java | 2 - .../android/bluetooth/IBluetoothManager.aidl | 2 + .../MutableBluetoothGattCharacteristic.java | 67 --- .../MutableBluetoothGattDescriptor.java | 45 -- .../MutableBluetoothGattService.java | 83 --- 16 files changed, 947 insertions(+), 993 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdapterCallback.java create mode 100644 framework/java/android/bluetooth/BluetoothManager.java delete mode 100644 framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java delete mode 100644 framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java delete mode 100644 framework/java/android/bluetooth/MutableBluetoothGattService.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b00bf095981..2e9c9e334d6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -22,7 +22,6 @@ import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; @@ -359,6 +358,8 @@ public final class BluetoothAdapter { private IBluetooth mService; private Handler mServiceRecordHandler; + private BluetoothAdapterCallback mCallback; + private int mClientIf; /** * Get a handle to the default local Bluetooth adapter. @@ -1137,7 +1138,8 @@ public final class BluetoothAdapter { * Get the profile proxy object associated with the profile. * *

    Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * or {@link BluetoothProfile#A2DP}. Clients must implement + * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or + * {@link BluetoothProfile#GATT_SERVER}. Clients must implement * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * @@ -1166,12 +1168,6 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; - } else if (profile == BluetoothProfile.GATT) { - BluetoothGatt gatt = new BluetoothGatt(context, listener); - return true; - } else if (profile == BluetoothProfile.GATT_SERVER) { - BluetoothGattServer gattServer = new BluetoothGattServer(context, listener); - return true; } else { return false; } @@ -1411,4 +1407,230 @@ public final class BluetoothAdapter { mProxyServiceStateCallbacks.remove(cb); } } + + /** + * Register an callback to receive async results, such as LE scan result. + * + *

    This is an asynchronous call. The callback + * {@link BluetoothAdapterCallback#onCallbackRegistration} + * is used to notify success or failure if the function returns true. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks. + * @return If true, the callback will be called to notify success or failure, + * false on immediate error + */ + public boolean registerCallback(BluetoothAdapterCallback callback) { + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); + + iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Unregister the registered callback. + */ + public boolean unRegisterCallback(BluetoothAdapterCallback callback) { + if (callback != mCallback) return false; + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + + iGatt.unregisterClient(mClientIf); + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + *

    Results of the scan are reported using the + * {@link BluetoothAdapterCallback#onLeScan} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startLeScan() { + if (DBG) Log.d(TAG, "startLeScan()"); + if (mClientIf == 0) return false; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + iGatt.startScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + *

    Devices which advertise all specified services are reported using the + * {@link BluetoothAdapterCallback#onLeScan} callback. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startLeScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startLeScan() - with UUIDs"); + if (mClientIf == 0) return false; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + iGatt.startScanWithUuids(mClientIf, false, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopLeScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mClientIf == 0) return; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + iGatt.stopScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattCallback mBluetoothGattCallback = + new IBluetoothGattCallback.Stub() { + /** + * Application interface registered - app is ready to go + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + mClientIf = clientIf; + mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ? + BluetoothAdapterCallback.CALLBACK_REGISTERED : + BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE); + } + + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onLeScan(getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + // no op + } + + public void onSearchComplete(String address, int status) { + // no op + } + + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + // no op + } + + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + // no op + } + + public void onExecuteWrite(String address, int status) { + // no op + } + + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + }; + } diff --git a/framework/java/android/bluetooth/BluetoothAdapterCallback.java b/framework/java/android/bluetooth/BluetoothAdapterCallback.java new file mode 100644 index 00000000000..a726bc91895 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdapterCallback.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * This abstract class is used to implement {@link BluetoothAdapter} callbacks. + */ +public abstract class BluetoothAdapterCallback { + + /** + * Indicates the callback has been registered successfully + */ + public static final int CALLBACK_REGISTERED = 0; + + /** + * Indicates the callback registration has failed + */ + public static final int CALLBACK_REGISTRATION_FAILURE = 1; + + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link #CALLBACK_REGISTERED} if the application + * was successfully registered. + */ + public void onCallbackRegistration(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothAdapter#startLeScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { + } +} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4cc22b4ae90..83e95ca9b94 100755 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -1126,4 +1127,30 @@ public final class BluetoothDevice implements Parcelable { return pinBytes; } + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGattServer(Context context, boolean autoConnect, + BluetoothGattCallback callback) { + // TODO(Bluetooth) check whether platform support BLE + // Do the check here or in GattServer? + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager managerService = adapter.getBluetoothManager(); + try { + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); + BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); + gatt.connect(autoConnect, callback); + return gatt; + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1e12025497f..f9ce6eacace 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,8 +16,6 @@ package android.bluetooth; - -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; @@ -39,42 +37,48 @@ import java.util.List; import java.util.UUID; /** - * Public API for the Bluetooth Gatt Profile. + * Public API for the Bluetooth GATT Profile. * - *

    This class provides Bluetooth Gatt functionality to enable communication + *

    This class provides Bluetooth GATT functionality to enable communication * with Bluetooth Smart or Smart Ready devices. * - *

    BluetoothGatt is a proxy object for controlling the Bluetooth Service - * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the - * BluetoothGatt proxy object. - * *

    To connect to a remote peripheral device, create a {@link BluetoothGattCallback} - * and call {@link #registerApp} to register your application. Gatt capable - * devices can be discovered using the {@link #startScan} function or the - * regular Bluetooth device discovery process. - * @hide + * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class. + * GATT capable devices can be discovered using the Bluetooth device discovery or BLE + * scan process. */ public final class BluetoothGatt implements BluetoothProfile { private static final String TAG = "BluetoothGatt"; private static final boolean DBG = true; + private static final boolean VDBG = true; - private Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; + private final Context mContext; private IBluetoothGatt mService; private BluetoothGattCallback mCallback; private int mClientIf; private boolean mAuthRetry = false; + private BluetoothDevice mDevice; + private boolean mAutoConnect; + private int mConnState; + private final Object mStateLock = new Object(); + + private static final int CONN_STATE_IDLE = 0; + private static final int CONN_STATE_CONNECTING = 1; + private static final int CONN_STATE_CONNECTED = 2; + private static final int CONN_STATE_DISCONNECTING = 3; private List mServices; - /** A Gatt operation completed successfully */ + /** A GATT operation failed */ + public static final int GATT_FAILURE = 0; + + /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; - /** Gatt read operation is not permitted */ + /** GATT read operation is not permitted */ public static final int GATT_READ_NOT_PERMITTED = 0x2; - /** Gatt write operation is not permitted */ + /** GATT write operation is not permitted */ public static final int GATT_WRITE_NOT_PERMITTED = 0x3; /** Insufficient authentication for a given operation */ @@ -110,55 +114,6 @@ public final class BluetoothGatt implements BluetoothProfile { */ /*package*/ static final int AUTHENTICATION_MITM = 2; - /** - * Bluetooth state change handlers - */ - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); - synchronized (mConnection) { - mService = null; - mContext.unbindService(mConnection); - } - } else { - synchronized (mConnection) { - if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth GATT Service"); - } - } - } - } - } - }; - - /** - * Service binder handling - */ - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothGatt.Stub.asInterface(service); - ServiceListener serviceListener = mServiceListener; - if (serviceListener != null) { - serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this); - } - } - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - ServiceListener serviceListener = mServiceListener; - if (serviceListener != null) { - serviceListener.onServiceDisconnected(BluetoothProfile.GATT); - } - } - }; - /** * Bluetooth GATT interface callbacks */ @@ -171,11 +126,27 @@ public final class BluetoothGatt implements BluetoothProfile { public void onClientRegistered(int status, int clientIf) { if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + if (VDBG) { + synchronized(mStateLock) { + if (mConnState != CONN_STATE_CONNECTING) { + Log.e(TAG, "Bad connection state: " + mConnState); + } + } + } mClientIf = clientIf; + if (status != GATT_SUCCESS) { + mCallback.onConnectionStateChange(mDevice, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + synchronized(mStateLock) { + mConnState = CONN_STATE_IDLE; + } + return; + } try { - mCallback.onAppRegistered(status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + mService.clientConnect(mClientIf, mDevice.getAddress(), + !mAutoConnect); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); } } @@ -187,13 +158,24 @@ public final class BluetoothGatt implements BluetoothProfile { boolean connected, String address) { if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status + " clientIf=" + clientIf + " device=" + address); + if (!address.equals(mDevice.getAddress())) { + return; + } + int profileState = connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED; try { - mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, - connected ? BluetoothProfile.STATE_CONNECTED - : BluetoothProfile.STATE_DISCONNECTED); + mCallback.onConnectionStateChange(mDevice, status, profileState); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } + + synchronized(mStateLock) { + if (connected) { + mConnState = CONN_STATE_CONNECTED; + } else { + mConnState = CONN_STATE_IDLE; + } + } } /** @@ -201,13 +183,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } + // no op } /** @@ -219,8 +195,10 @@ public final class BluetoothGatt implements BluetoothProfile { public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) { if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType)); } @@ -236,10 +214,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); - BluetoothGattService includedService = getService(device, + BluetoothGattService includedService = getService(mDevice, inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); if (service != null && includedService != null) { @@ -260,8 +240,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service != null) { service.addCharacteristic(new BluetoothGattCharacteristic( @@ -281,8 +263,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descUuid) { if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -303,9 +287,11 @@ public final class BluetoothGatt implements BluetoothProfile { */ public void onSearchComplete(String address, int status) { if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onServicesDiscovered(device, status); + mCallback.onServicesDiscovered(mDevice, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -322,6 +308,9 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + " UUID=" + charUuid + " Status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) && mAuthRetry == false) { @@ -338,8 +327,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -367,8 +355,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " UUID=" + charUuid + " Status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -411,8 +401,10 @@ public final class BluetoothGatt implements BluetoothProfile { byte[] value) { if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -439,8 +431,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descrUuid, byte[] value) { if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -486,8 +480,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descrUuid) { if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -529,9 +525,11 @@ public final class BluetoothGatt implements BluetoothProfile { public void onExecuteWrite(String address, int status) { if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onReliableWriteCompleted(device, status); + mCallback.onReliableWriteCompleted(mDevice, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -544,43 +542,24 @@ public final class BluetoothGatt implements BluetoothProfile { public void onReadRemoteRssi(String address, int rssi, int status) { if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + " rssi=" + rssi + " status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onReadRemoteRssi(device, rssi, status); + mCallback.onReadRemoteRssi(mDevice, rssi, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } } }; - /** - * Create a BluetoothGatt proxy object. - */ - /*package*/ BluetoothGatt(Context context, ServiceListener l) { + /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { mContext = context; - mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mService = iGatt; + mDevice = device; mServices = new ArrayList(); - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - } else { - Log.e(TAG, "Unable to get BluetoothManager interface."); - throw new RuntimeException("BluetoothManager inactive"); - } - - //Bind to the service only if the Bluetooth is ON - if(mAdapter.isEnabled()){ - if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); - } - } + mConnState = CONN_STATE_IDLE; } /** @@ -590,24 +569,6 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "close()"); unregisterApp(); - mServiceListener = null; - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } } /** @@ -629,18 +590,18 @@ public final class BluetoothGatt implements BluetoothProfile { /** - * Register an application callback to start using Gatt. + * Register an application callback to start using GATT. * - *

    This is an asynchronous call. The callback is used to notify - * success or failure if the function returns true. + *

    This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + * is used to notify success or failure if the function returns true. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param callback Gatt callback handler that will receive asynchronous - * callbacks. - * @return true, if application was successfully registered. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @return If true, the callback will be called to notify success or failure, + * false on immediate error */ - public boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallback callback) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -661,7 +622,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Unregister the current application and callbacks. */ - public void unregisterApp() { + private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); if (mService == null || mClientIf == 0) return; @@ -675,77 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Starts a scan for Bluetooth LE devices. - * - *

    Results of the scan are reported using the - * {@link BluetoothGattCallback#onScanResult} callback. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return true, if the scan was started successfully - */ - public boolean startScan() { - if (DBG) Log.d(TAG, "startScan()"); - if (mService == null || mClientIf == 0) return false; - - try { - mService.startScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Starts a scan for Bluetooth LE devices, looking for devices that - * advertise given services. - * - *

    Devices which advertise all specified services are reported using the - * {@link BluetoothGattCallback#onScanResult} callback. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param serviceUuids Array of services to look for - * @return true, if the scan was started successfully - */ - public boolean startScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startScan() - with UUIDs"); - if (mService == null || mClientIf == 0) return false; - - try { - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); - } - mService.startScanWithUuids(mClientIf, false, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Stops an ongoing Bluetooth LE device scan. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mService == null || mClientIf == 0) return; - - try { - mService.stopScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Initiate a connection to a Bluetooth Gatt capable device. + * Initiate a connection to a Bluetooth GATT capable device. * *

    The connection may not be established right away, but will be * completed when the remote device is available. A @@ -757,7 +648,7 @@ public final class BluetoothGatt implements BluetoothProfile { * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to false. + * autoConnect parameter set to true. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * @@ -767,18 +658,24 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - public boolean connect(BluetoothDevice device, boolean autoConnect) { - if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); - if (mService == null || mClientIf == 0) return false; - - try { - mService.clientConnect(mClientIf, device.getAddress(), - autoConnect ? false : true); // autoConnect is inverse of "isDirect" - } catch (RemoteException e) { - Log.e(TAG,"",e); + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); + synchronized(mStateLock) { + if (mConnState != CONN_STATE_IDLE) { + throw new IllegalStateException("Not idle"); + } + mConnState = CONN_STATE_CONNECTING; + } + if (!registerApp(callback)) { + synchronized(mStateLock) { + mConnState = CONN_STATE_IDLE; + } + Log.e(TAG, "Failed to register callback"); return false; } + // the connection will continue after successful callback registration + mAutoConnect = autoConnect; return true; } @@ -787,18 +684,17 @@ public final class BluetoothGatt implements BluetoothProfile { * currently in progress. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote device */ - public void cancelConnection(BluetoothDevice device) { - if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress()); + public void disconnect() { + if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; try { - mService.clientDisconnect(mClientIf, device.getAddress()); + mService.clientDisconnect(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); } + // TBD deregister after conneciton is torn down } /** @@ -812,17 +708,16 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device to explore * @return true, if the remote service discovery has been started */ - public boolean discoverServices(BluetoothDevice device) { - if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress()); + public boolean discoverServices() { + if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; mServices.clear(); try { - mService.discoverServices(mClientIf, device.getAddress()); + mService.discoverServices(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -839,16 +734,15 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return List of services on the remote device. Returns an empty list * if service discovery has not yet been performed. */ - public List getServices(BluetoothDevice device) { + public List getServices() { List result = new ArrayList(); for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(device)) { + if (service.getDevice().equals(mDevice)) { result.add(service); } } @@ -868,14 +762,13 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @param uuid UUID of the requested service * @return BluetoothGattService if supported, or null if the requested * service is not offered by the remote device. */ - public BluetoothGattService getService(BluetoothDevice device, UUID uuid) { + public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(device) && + if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { return service; } @@ -923,8 +816,7 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Writes a given characteristic and it's values to the associated remote - * device. + * Writes a given characteristic and its values to the associated remote device. * *

    Once the write operation has been completed, the * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, @@ -1061,15 +953,14 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the reliable write transaction has been initiated */ - public boolean beginReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress()); + public boolean beginReliableWrite() { + if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.beginReliableWrite(mClientIf, device.getAddress()); + mService.beginReliableWrite(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1089,15 +980,14 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the request to execute the transaction has been sent */ - public boolean executeReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress()); + public boolean executeReliableWrite() { + if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.endReliableWrite(mClientIf, device.getAddress(), true); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1113,15 +1003,13 @@ public final class BluetoothGatt implements BluetoothProfile { * operations for a given remote device. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote device */ - public void abortReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress()); + public void abortReliableWrite(BluetoothDevice mDevice) { + if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; try { - mService.endReliableWrite(mClientIf, device.getAddress(), false); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -1172,12 +1060,12 @@ public final class BluetoothGatt implements BluetoothProfile { * remote device. * @hide */ - public boolean refresh(BluetoothDevice device) { - if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress()); + public boolean refresh() { + if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.refreshDevice(mClientIf, device.getAddress()); + mService.refreshDevice(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1194,15 +1082,14 @@ public final class BluetoothGatt implements BluetoothProfile { * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the RSSI value has been requested successfully */ - public boolean readRemoteRssi(BluetoothDevice device) { - if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress()); + public boolean readRemoteRssi() { + if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.readRemoteRssi(mClientIf, device.getAddress()); + mService.readRemoteRssi(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1212,98 +1099,38 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Get the current connection state of the profile. - * - *

    This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @throws UnsupportedOperationException */ @Override public int getConnectionState(BluetoothDevice device) { - if (DBG) Log.d(TAG,"getConnectionState()"); - if (mService == null) return STATE_DISCONNECTED; - - List connectedDevices = getConnectedDevices(); - for(BluetoothDevice connectedDevice : connectedDevices) { - if (device.equals(connectedDevice)) { - return STATE_CONNECTED; - } - } - - return STATE_DISCONNECTED; + throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } /** - * Get connected devices for the Gatt profile. - * - *

    Return the set of devices which are in state {@link #STATE_CONNECTED} - * - *

    This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List getConnectedDevices() { - if (DBG) Log.d(TAG,"getConnectedDevices"); - - List connectedDevices = new ArrayList(); - if (mService == null) return connectedDevices; - - try { - connectedDevices = mService.getDevicesMatchingConnectionStates( - new int[] { BluetoothProfile.STATE_CONNECTED }); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return connectedDevices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getConnectedDevices instead."); } /** - * Get a list of devices that match any of the given connection - * states. - * - *

    If none of the devices match any of the given states, - * an empty list will be returned. - * - *

    This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. + * Not supported - please use + * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} + * with {@link BluetoothProfile#GATT} as first argument * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); - - List devices = new ArrayList(); - if (mService == null) return devices; - - try { - devices = mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return devices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index afa4539cc61..c9e5fea713d 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -18,34 +18,10 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.util.Log; - /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. - * @hide */ public abstract class BluetoothGattCallback { - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application - * was successfully registered. - */ - public void onAppRegistered(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothGatt#startScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { - } /** * Callback indicating when a remote device has been connected or disconnected. @@ -61,8 +37,8 @@ public abstract class BluetoothGattCallback { } /** - * Callback invoked when the list of remote services, characteristics and - * descriptors for the remote device have been updated. + * Callback invoked when the list of remote services, characteristics and descriptors + * for the remote device have been updated, ie new services have been discovered. * * @param device Remote device * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device @@ -97,7 +73,7 @@ public abstract class BluetoothGattCallback { * @param status The result of the write operation */ public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, - int status) { + int status) { } /** @@ -113,23 +89,21 @@ public abstract class BluetoothGattCallback { * Callback reporting the result of a descriptor read operation. * * @param descriptor Descriptor that was read from the associated - * remote device. + * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully */ - public void onDescriptorRead(BluetoothGattDescriptor descriptor, - int status) { + public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) { } /** * Callback indicating the result of a descriptor write operation. * * @param descriptor Descriptor that was writte to the associated - * remote device. + * remote device. * @param status The result of the write operation */ - public void onDescriptorWrite(BluetoothGattDescriptor descriptor, - int status) { + public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) { } /** @@ -150,7 +124,7 @@ public abstract class BluetoothGattCallback { * * @param device Identifies the remote device * @param rssi The RSSI value for the remote device - * @param status 0 if the RSSI was read successfully + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully */ public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { } diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index f44dc5c08bb..d63d97e22eb 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -21,8 +21,7 @@ import java.util.List; import java.util.UUID; /** - * Represents a Bluetooth Gatt Characteristic - * @hide + * Represents a Bluetooth GATT Characteristic */ public class BluetoothGattCharacteristic { @@ -119,7 +118,7 @@ public class BluetoothGattCharacteristic { public static final int WRITE_TYPE_NO_RESPONSE = 0x01; /** - * Write characteristic including and authenticated signature + * Write characteristic including authentication signature */ public static final int WRITE_TYPE_SIGNED = 0x04; @@ -218,6 +217,18 @@ public class BluetoothGattCharacteristic { */ protected List mDescriptors; + /** + * Create a new BluetoothGattCharacteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this characteristic + * @param properties Properties of this characteristic + * @param permissions Permissions for this characteristic + */ + public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { + initCharacteristic(null, uuid, 0, properties, permissions); + } + /** * Create a new BluetoothGattCharacteristic * @hide @@ -225,6 +236,12 @@ public class BluetoothGattCharacteristic { /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, UUID uuid, int instanceId, int properties, int permissions) { + initCharacteristic(service, uuid, instanceId, properties, permissions); + } + + private void initCharacteristic(BluetoothGattService service, + UUID uuid, int instanceId, + int properties, int permissions) { mUuid = uuid; mInstance = instanceId; mProperties = properties; @@ -249,11 +266,16 @@ public class BluetoothGattCharacteristic { } /** - * Add a descriptor to this characteristic - * @hide + * Adds a descriptor to this characteristic. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to be added to this characteristic. + * @return true, if the descriptor was added to the characteristic */ - /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) { + public boolean addDescriptor(BluetoothGattDescriptor descriptor) { mDescriptors.add(descriptor); + descriptor.setCharacteristic(this); + return true; } /** @@ -264,9 +286,16 @@ public class BluetoothGattCharacteristic { return mService; } + /** + * Sets the service associated with this device. + * @hide + */ + /*package*/ void setService(BluetoothGattService service) { + mService = service; + } + /** * Returns the UUID of this characteristic - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this characteristic */ @@ -280,8 +309,6 @@ public class BluetoothGattCharacteristic { *

    If a remote device offers multiple characteristics with the same UUID, * the instance ID is used to distuinguish between characteristics. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Instance ID of this characteristic */ public int getInstanceId() { @@ -294,8 +321,6 @@ public class BluetoothGattCharacteristic { *

    The properties contain a bit mask of property flags indicating * the features of this characteristic. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Properties of this characteristic */ public int getProperties() { @@ -304,7 +329,6 @@ public class BluetoothGattCharacteristic { /** * Returns the permissions for this characteristic. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Permissions of this characteristic */ @@ -314,7 +338,6 @@ public class BluetoothGattCharacteristic { /** * Gets the write type for this characteristic. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Write type for this characteristic */ @@ -329,11 +352,6 @@ public class BluetoothGattCharacteristic { * {@link BluetoothGatt#writeCharacteristic} function write this * characteristic. * - *

    The default write type for a characteristic is - * {@link #WRITE_TYPE_DEFAULT}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param writeType The write type to for this characteristic. Can be one * of: * {@link #WRITE_TYPE_DEFAULT}, @@ -344,9 +362,16 @@ public class BluetoothGattCharacteristic { mWriteType = writeType; } + /** + * Set the desired key size. + * @hide + */ + public void setKeySize(int keySize) { + mKeySize = keySize; + } + /** * Returns a list of descriptors for this characteristic. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Descriptors for this characteristic */ @@ -358,9 +383,7 @@ public class BluetoothGattCharacteristic { * Returns a descriptor with a given UUID out of the list of * descriptors for this characteristic. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return Gatt descriptor object or null if no descriptor with the + * @return GATT descriptor object or null if no descriptor with the * given UUID was found. */ public BluetoothGattDescriptor getDescriptor(UUID uuid) { @@ -376,12 +399,10 @@ public class BluetoothGattCharacteristic { * Get the stored value for this characteristic. * *

    This function returns the stored value for this characteristic as - * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached + * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached * value of the characteristic is updated as a result of a read characteristic * operation or if a characteristic update notification has been received. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Cached value of the characteristic */ public byte[] getValue() { @@ -397,8 +418,6 @@ public class BluetoothGattCharacteristic { * characteristic value at the given offset are interpreted to generate the * return value. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param formatType The format type used to interpret the characteristic * value. * @param offset Offset at which the integer value can be found. @@ -436,7 +455,6 @@ public class BluetoothGattCharacteristic { /** * Return the stored value of this characteristic. *

    See {@link #getValue} for details. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param formatType The format type used to interpret the characteristic * value. @@ -462,7 +480,7 @@ public class BluetoothGattCharacteristic { /** * Return the stored value of this characteristic. *

    See {@link #getValue} for details. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param offset Offset at which the string value can be found. * @return Cached value of the characteristic */ @@ -481,8 +499,6 @@ public class BluetoothGattCharacteristic { * {@link BluetoothGatt#writeCharacteristic} to send the value to the * remote device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param value New value for this characteristic * @return true if the locally stored value has been set, false if the * requested value could not be stored locally. @@ -495,7 +511,6 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. *

    See {@link #setValue(byte[])} for details. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param value New value for this characteristic * @param formatType Integer format type used to transform the value parameter @@ -542,7 +557,7 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. *

    See {@link #setValue(byte[])} for details. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param mantissa Mantissa for this characteristic * @param exponent exponent value for this characteristic * @param formatType Float format type used to transform the value parameter @@ -582,7 +597,7 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. *

    See {@link #setValue(byte[])} for details. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param value New value for this characteristic * @return true if the locally stored value has been set */ @@ -593,7 +608,6 @@ public class BluetoothGattCharacteristic { /** * Returns the size of a give value type. - * @hide */ private int getTypeLen(int formatType) { return formatType & 0xF; @@ -601,7 +615,6 @@ public class BluetoothGattCharacteristic { /** * Convert a signed byte to an unsigned int. - * @hide */ private int unsignedByteToInt(byte b) { return b & 0xFF; @@ -609,7 +622,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 16-bit unsigned int. - * @hide */ private int unsignedBytesToInt(byte b0, byte b1) { return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)); @@ -617,7 +629,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 32-bit unsigned int. - * @hide */ private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) @@ -626,7 +637,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 16-bit short float value. - * @hide */ private float bytesToFloat(byte b0, byte b1) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) @@ -637,7 +647,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 32-bit short float value. - * @hide */ private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) @@ -649,7 +658,6 @@ public class BluetoothGattCharacteristic { /** * Convert an unsigned integer value to a two's-complement encoded * signed value. - * @hide */ private int unsignedToSigned(int unsigned, int size) { if ((unsigned & (1 << size-1)) != 0) { @@ -660,7 +668,6 @@ public class BluetoothGattCharacteristic { /** * Convert an integer into the signed bits of a given length. - * @hide */ private int intToSignedBits(int i, int size) { if (i < 0) { diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index ba1f28afd79..6ba2db704f4 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -19,8 +19,7 @@ package android.bluetooth; import java.util.UUID; /** - * Represents a Bluetooth Gatt Descriptor - * @hide + * Represents a Bluetooth GATT Descriptor */ public class BluetoothGattDescriptor { @@ -105,6 +104,17 @@ public class BluetoothGattDescriptor { */ protected byte[] mValue; + /** + * Create a new BluetoothGattDescriptor. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + public BluetoothGattDescriptor(UUID uuid, int permissions) { + initDescriptor(null, uuid, permissions); + } + /** * Create a new BluetoothGattDescriptor. *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -115,6 +125,11 @@ public class BluetoothGattDescriptor { */ /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, int permissions) { + initDescriptor(characteristic, uuid, permissions); + } + + private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, + int permissions) { mCharacteristic = characteristic; mUuid = uuid; mPermissions = permissions; @@ -128,9 +143,16 @@ public class BluetoothGattDescriptor { return mCharacteristic; } + /** + * Set the back-reference to the associated characteristic + * @hide + */ + /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristic = characteristic; + } + /** * Returns the UUID of this descriptor. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this descriptor */ @@ -140,7 +162,6 @@ public class BluetoothGattDescriptor { /** * Returns the permissions for this descriptor. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Permissions of this descriptor */ @@ -152,12 +173,10 @@ public class BluetoothGattDescriptor { * Returns the stored value for this descriptor * *

    This function returns the stored value for this descriptor as - * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached + * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached * value of the descriptor is updated as a result of a descriptor read * operation. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Cached value of the descriptor */ public byte[] getValue() { @@ -172,8 +191,6 @@ public class BluetoothGattDescriptor { * {@link BluetoothGatt#writeDescriptor} to send the value to the * remote device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param value New value for this descriptor * @return true if the locally stored value has been set, false if the * requested value could not be stored locally. diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 6b693779153..d1f4b82f982 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -38,88 +38,30 @@ import java.util.List; import java.util.UUID; /** - * Public API for the Bluetooth Gatt Profile server role. + * Public API for the Bluetooth GATT Profile server role. * - *

    This class provides Bluetooth Gatt server role functionality, + *

    This class provides Bluetooth GATT server role functionality, * allowing applications to create and advertise Bluetooth Smart services * and characteristics. * *

    BluetoothGattServer is a proxy object for controlling the Bluetooth Service * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the * BluetoothGatt proxy object. - * @hide */ public final class BluetoothGattServer implements BluetoothProfile { private static final String TAG = "BluetoothGattServer"; private static final boolean DBG = true; - private Context mContext; - private ServiceListener mServiceListener; + private final Context mContext; private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; - private int mServerIf; + private Object mServerIfLock = new Object(); + private int mServerIf; private List mServices; - /** - * Bluetooth state change handlers - */ - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new - Intent(IBluetoothGatt.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth GATT Service"); - } - } - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - } - }; - - /** - * Service binder handling - */ - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothGatt.Stub.asInterface(service); - ServiceListener serviceListner = mServiceListener; - if (serviceListner != null) { - serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER, - BluetoothGattServer.this); - } - } - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - ServiceListener serviceListner = mServiceListener; - if (serviceListner != null) { - serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER); - } - } - }; + private static final int CALLBACK_REG_TIMEOUT = 10000; /** * Bluetooth GATT interface callbacks @@ -133,11 +75,14 @@ public final class BluetoothGattServer implements BluetoothProfile { public void onServerRegistered(int status, int serverIf) { if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status + " serverIf=" + serverIf); - mServerIf = serverIf; - try { - mCallback.onAppRegistered(status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + synchronized(mServerIfLock) { + if (mCallback != null) { + mServerIf = serverIf; + mServerIfLock.notify(); + } else { + // registration timeout + Log.e(TAG, "onServerRegistered: mCallback is null"); + } } } @@ -147,13 +92,7 @@ public final class BluetoothGattServer implements BluetoothProfile { */ public void onScanResult(String address, int rssi, byte[] advData) { if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onScanResult(mAdapter.getRemoteDevice(address), - rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } + // no op } /** @@ -209,8 +148,7 @@ public final class BluetoothGattServer implements BluetoothProfile { BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); if (service == null) return; - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid); + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); if (characteristic == null) return; try { @@ -340,31 +278,13 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, ServiceListener l) { + /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) { mContext = context; - mServiceListener = l; + mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); + mCallback = null; + mServerIf = 0; mServices = new ArrayList(); - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - } else { - Log.e(TAG, "Unable to get BluetoothManager interface."); - throw new RuntimeException("BluetoothManager inactive"); - } - - //Bind to the service only if the Bluetooth is ON - if(mAdapter.isEnabled()){ - if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); - } - } } /** @@ -372,82 +292,67 @@ public final class BluetoothGattServer implements BluetoothProfile { */ /*package*/ void close() { if (DBG) Log.d(TAG, "close()"); - - unregisterApp(); - mServiceListener = null; - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - } - - /** - * Returns a service by UUID, instance and type. - * @hide - */ - /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { - for(BluetoothGattService svc : mServices) { - if (svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { - return svc; - } - } - return null; + unregisterCallback(); } /** - * Register an application callback to start using Gatt. + * Register an application callback to start using GattServer. * *

    This is an asynchronous call. The callback is used to notify * success or failure if the function returns true. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param callback Gatt callback handler that will receive asynchronous - * callbacks. - * @return true, if application was successfully registered. + * @param callback GATT callback handler that will receive asynchronous + * callbacks. + * @return true, the callback will be called to notify success or failure, + * false on immediate error */ - public boolean registerApp(BluetoothGattServerCallback callback) { - if (DBG) Log.d(TAG, "registerApp()"); - if (mService == null) return false; - - mCallback = callback; - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); - - try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); - } catch (RemoteException e) { - Log.e(TAG,"",e); + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + if (DBG) Log.d(TAG, "registerCallback()"); + if (mService == null) { + Log.e(TAG, "GATT service not available"); return false; } + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - return true; + synchronized(mServerIfLock) { + if (mCallback != null) { + Log.e(TAG, "App can register callback only once"); + return false; + } + + mCallback = callback; + try { + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + mCallback = null; + return false; + } + + try { + mServerIfLock.wait(CALLBACK_REG_TIMEOUT); + } catch (InterruptedException e) { + Log.e(TAG, "" + e); + mCallback = null; + } + + if (mServerIf == 0) { + mCallback = null; + return false; + } else { + return true; + } + } } /** * Unregister the current application and callbacks. */ - public void unregisterApp() { - if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf); + private void unregisterCallback() { + if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); if (mService == null || mServerIf == 0) return; try { @@ -460,77 +365,22 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Starts a scan for Bluetooth LE devices. - * - *

    Results of the scan are reported using the - * {@link BluetoothGattServerCallback#onScanResult} callback. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return true, if the scan was started successfully - */ - public boolean startScan() { - if (DBG) Log.d(TAG, "startScan()"); - if (mService == null || mServerIf == 0) return false; - - try { - mService.startScan(mServerIf, true); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Starts a scan for Bluetooth LE devices, looking for devices that - * advertise given services. - * - *

    Devices which advertise all specified services are reported using the - * {@link BluetoothGattServerCallback#onScanResult} callback. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param serviceUuids Array of services to look for - * @return true, if the scan was started successfully + * Returns a service by UUID, instance and type. + * @hide */ - public boolean startScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startScan() - with UUIDs"); - if (mService == null || mServerIf == 0) return false; - - try { - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); + /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; } - mService.startScanWithUuids(mServerIf, true, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Stops an ongoing Bluetooth LE device scan. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mService == null || mServerIf == 0) return; - - try { - mService.stopScan(mServerIf, true); - } catch (RemoteException e) { - Log.e(TAG,"",e); } + return null; } /** - * Initiate a connection to a Bluetooth Gatt capable device. + * Initiate a connection to a Bluetooth GATT capable device. * *

    The connection may not be established right away, but will be * completed when the remote device is available. A @@ -542,11 +392,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to false. + * autoConnect parameter set to true. * *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device to connect to * @param autoConnect Whether to directly connect to the remote device (false) * or to automatically connect as soon as the remote * device becomes available (true). @@ -590,7 +439,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Send a response to a read or write request to a remote device. * *

    This function must be invoked in when a remote read/write request - * is received by one of these callback methots: + * is received by one of these callback methods: * *

      *
    • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} @@ -662,17 +511,17 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Add a service to the list of services to be advertised. + * Add a service to the list of services to be hosted. * *

      Once a service has been addded to the the list, the service and it's - * included characteristics will be advertised by the local device. + * included characteristics will be provided by the local device. * - *

      If the local device is already advertising services when this function + *

      If the local device has already exposed services when this function * is called, a service update notification will be sent to all clients. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param service Service to be added to the list of services advertised + * @param service Service to be added to the list of services provided * by this device. * @return true, if the service has been added successfully */ @@ -721,11 +570,11 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Removes a service from the list of services to be advertised. + * Removes a service from the list of services to be provided. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param service Service to beremoved. + * @param service Service to be removed. * @return true, if the service has been removed */ public boolean removeService(BluetoothGattService service) { @@ -749,7 +598,7 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Remove all services from the list of advertised services. + * Remove all services from the list of provided services. *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ public void clearServices() { @@ -765,7 +614,7 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Returns a list of GATT services offered bu this device. + * Returns a list of GATT services offered by this device. * *

      An application must call {@link #addService} to add a serice to the * list of services offered by this device. @@ -802,99 +651,40 @@ public final class BluetoothGattServer implements BluetoothProfile { return null; } + /** - * Get the current connection state of the profile. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - *

      This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @throws UnsupportedOperationException */ @Override public int getConnectionState(BluetoothDevice device) { - if (DBG) Log.d(TAG,"getConnectionState()"); - if (mService == null) return STATE_DISCONNECTED; - - List connectedDevices = getConnectedDevices(); - for(BluetoothDevice connectedDevice : connectedDevices) { - if (device.equals(connectedDevice)) { - return STATE_CONNECTED; - } - } - - return STATE_DISCONNECTED; + throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } /** - * Get connected devices for the Gatt profile. - * - *

      Return the set of devices which are in state {@link #STATE_CONNECTED} + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - *

      This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List getConnectedDevices() { - if (DBG) Log.d(TAG,"getConnectedDevices"); - - List connectedDevices = new ArrayList(); - if (mService == null) return connectedDevices; - - try { - connectedDevices = mService.getDevicesMatchingConnectionStates( - new int[] { BluetoothProfile.STATE_CONNECTED }); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return connectedDevices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getConnectedDevices instead."); } /** - * Get a list of devices that match any of the given connection - * states. - * - *

      If none of the devices match any of the given states, - * an empty list will be returned. - * - *

      This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. + * Not supported - please use + * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} + * with {@link BluetoothProfile#GATT} as first argument * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); - - List devices = new ArrayList(); - if (mService == null) return devices; - - try { - devices = mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return devices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 4f608ff754c..f9f1d975951 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -22,30 +22,8 @@ import android.util.Log; /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. - * @hide */ public abstract class BluetoothGattServerCallback { - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application - * was successfully registered. - */ - public void onAppRegistered(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothGattServer#startScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { - } /** * Callback indicating when a remote device has been connected or disconnected. @@ -101,9 +79,9 @@ public abstract class BluetoothGattServerCallback { * @param value The value the client wants to assign to the characteristic */ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** @@ -118,7 +96,7 @@ public abstract class BluetoothGattServerCallback { * @param descriptor Descriptor to be read */ public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { + int offset, BluetoothGattDescriptor descriptor) { } /** @@ -137,9 +115,9 @@ public abstract class BluetoothGattServerCallback { * @param value The value the client wants to assign to the descriptor */ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 6a3ce66e8f2..c3b3cfe2393 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -22,8 +22,7 @@ import java.util.List; import java.util.UUID; /** - * Represents a Bluetooth Gatt Service - * @hide + * Represents a Bluetooth GATT Service */ public class BluetoothGattService { @@ -81,9 +80,14 @@ public class BluetoothGattService { /** * Create a new BluetoothGattService. - * @hide + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this service + * @param serviceType The type of this service, + * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or + * {@link BluetoothGattService#SERVICE_TYPE_SECONDARY} */ - /*package*/ BluetoothGattService(UUID uuid, int serviceType) { + public BluetoothGattService(UUID uuid, int serviceType) { mDevice = null; mUuid = uuid; mInstanceId = 0; @@ -114,12 +118,29 @@ public class BluetoothGattService { return mDevice; } + /** + * Add an included service to this service. + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service The service to be added + * @return true, if the included service was added to the service + */ + public boolean addService(BluetoothGattService service) { + mIncludedServices.add(service); + return true; + } + /** * Add a characteristic to this service. - * @hide + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristics to be added + * @return true, if the characteristic was added to the service */ - /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) { + public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) { mCharacteristics.add(characteristic); + characteristic.setService(this); + return true; } /** @@ -135,6 +156,15 @@ public class BluetoothGattService { return null; } + /** + * Force the instance ID. + * This is needed for conformance testing only. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstanceId = instanceId; + } + /** * Get the handle count override (conformance testing. * @hide @@ -143,6 +173,15 @@ public class BluetoothGattService { return mHandles; } + /** + * Force the number of handles to reserve for this service. + * This is needed for conformance testing only. + * @hide + */ + public void setHandles(int handles) { + mHandles = handles; + } + /** * Add an included service to the internal map. * @hide @@ -153,7 +192,6 @@ public class BluetoothGattService { /** * Returns the UUID of this service - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this service */ @@ -168,8 +206,6 @@ public class BluetoothGattService { * (ex. multiple battery services for different batteries), the instance * ID is used to distuinguish services. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Instance ID of this service */ public int getInstanceId() { @@ -178,15 +214,13 @@ public class BluetoothGattService { /** * Get the type of this service (primary/secondary) - * @hide */ public int getType() { return mServiceType; } /** - * Get the list of included Gatt services for this service. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Get the list of included GATT services for this service. * * @return List of included services or empty list if no included services * were discovered. @@ -197,7 +231,6 @@ public class BluetoothGattService { /** * Returns a list of characteristics included in this service. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Characteristics included in this service */ @@ -217,9 +250,7 @@ public class BluetoothGattService { * UUID, the first instance of a characteristic with the given UUID * is returned. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return Gatt characteristic object or null if no characteristic with the + * @return GATT characteristic object or null if no characteristic with the * given UUID was found. */ public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java new file mode 100644 index 00000000000..19083b55dac --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * High level manager used to obtain an instance of an {@link BluetoothAdapter} + * and to conduct overall Bluetooth Management. + *

      + * Use {@link android.content.Context#getSystemService(java.lang.String)} + * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager}, + * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}. + *

      + * Alternately, you can just call the static helper + * {@link BluetoothAdapter#getDefaultAdapter()}. + * + *

      + *

      Developer Guides

      + *

      For more information about using BLUETOOTH, read the + * Bluetooth developer guide.

      + *
      + * + * @see Context#getSystemService + * @see BluetoothAdapter#getDefaultAdapter() + */ +public final class BluetoothManager { + private static final String TAG = "BluetoothManager"; + private static final boolean DBG = true; + private static final boolean VDBG = true; + + private final BluetoothAdapter mAdapter; + + /** + * @hide + */ + public BluetoothManager(Context context) { + context = context.getApplicationContext(); + if (context == null) { + throw new IllegalArgumentException( + "context not associated with any application (using a mock context?)"); + } + // Legacy api - getDefaultAdapter does not take in the context + mAdapter = BluetoothAdapter.getDefaultAdapter(); + } + + /** + * Get the default BLUETOOTH Adapter for this device. + * + * @return the default BLUETOOTH Adapter + */ + public BluetoothAdapter getAdapter() { + return mAdapter; + } + + /** + * Get the current connection state of the profile to the remote device. + * + *

      This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for certain profile. + * This can be used by applications like status bar which would just like + * to know the state of Bluetooth. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @param profile GATT or GATT_SERVER + * @return State of the profile connection. One of + * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, + * {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING} + */ + public int getConnectionState(BluetoothDevice device, int profile) { + if (DBG) Log.d(TAG,"getConnectionState()"); + + List connectedDevices = getConnectedDevices(profile); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return BluetoothProfile.STATE_CONNECTED; + } + } + + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Get connected devices for the specified profile. + * + *

      Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED} + * + *

      This is not specific to any application configuration but represents + * the connection state of Bluetooth for this profile. + * This can be used by applications like status bar which would just like + * to know the state of Bluetooth. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param profile GATT or GATT_SERVER + * @return List of devices. The list will be empty on error. + */ + public List getConnectedDevices(int profile) { + if (DBG) Log.d(TAG,"getConnectedDevices"); + if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { + throw new IllegalArgumentException("Profile not supported: " + profile); + } + + List connectedDevices = new ArrayList(); + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) return connectedDevices; + + connectedDevices = iGatt.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * + * Get a list of devices that match any of the given connection + * states. + * + *

      If none of the devices match any of the given states, + * an empty list will be returned. + * + *

      This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param profile GATT or GATT_SERVER + * @param states Array of states. States can be one of + * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, + * {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + public List getDevicesMatchingConnectionStates(int profile, int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { + throw new IllegalArgumentException("Profile not supported: " + profile); + } + + List devices = new ArrayList(); + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) return devices; + devices = iGatt.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @return BluetoothGattServer instance + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback) { + if (context == null || callback == null) { + throw new IllegalArgumentException("null parameter: " + context + " " + callback); + } + + // TODO(Bluetooth) check whether platform support BLE + // Do the check here or in GattServer? + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) { + Log.e(TAG, "Fail to get GATT Server connection"); + return null; + } + BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt); + Boolean regStatus = mGattServer.registerCallback(callback); + return regStatus? mGattServer : null; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return null; + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 9ee202a5896..43079f44f5b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -89,13 +89,11 @@ public interface BluetoothProfile { /** * GATT - * @hide */ static public final int GATT = 7; /** * GATT_SERVER - * @hide */ static public final int GATT_SERVER = 8; diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index ed8777c5152..493d2f8e774 100755 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothStateChangeCallback; @@ -35,6 +36,7 @@ interface IBluetoothManager boolean enable(); boolean enableNoAutoConnect(); boolean disable(boolean persist); + IBluetoothGatt getBluetoothGatt(); String getAddress(); String getName(); diff --git a/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java deleted file mode 100644 index c05abb21500..00000000000 --- a/framework/java/android/bluetooth/MutableBluetoothGattCharacteristic.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import java.util.ArrayList; -import java.util.IllegalFormatConversionException; -import java.util.List; -import java.util.UUID; - -/** - * Mutable variant of a Bluetooth Gatt Characteristic - * @hide - */ -public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic { - - /** - * Create a new MutableBluetoothGattCharacteristic. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this characteristic - * @param properties Properties of this characteristic - * @param permissions Permissions for this characteristic - */ - public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { - super(null, uuid, 0, properties, permissions); - } - - /** - * Adds a descriptor to this characteristic. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param descriptor Descriptor to be added to this characteristic. - */ - public void addDescriptor(MutableBluetoothGattDescriptor descriptor) { - mDescriptors.add(descriptor); - descriptor.setCharacteristic(this); - } - - /** - * Set the desired key size. - * @hide - */ - public void setKeySize(int keySize) { - mKeySize = keySize; - } - - /** - * Sets the service associated with this device. - * @hide - */ - /*package*/ void setService(BluetoothGattService service) { - mService = service; - } -} diff --git a/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java deleted file mode 100644 index e4553920879..00000000000 --- a/framework/java/android/bluetooth/MutableBluetoothGattDescriptor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import java.util.UUID; - -/** - * Mutable variant of a Bluetooth Gatt Descriptor - * @hide - */ -public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor { - - /** - * Create a new BluetoothGattDescriptor. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this descriptor - * @param permissions Permissions for this descriptor - */ - public MutableBluetoothGattDescriptor(UUID uuid, int permissions) { - super(null, uuid, permissions); - } - - /** - * Set the back-reference to the associated characteristic - * @hide - */ - /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { - mCharacteristic = characteristic; - } -} diff --git a/framework/java/android/bluetooth/MutableBluetoothGattService.java b/framework/java/android/bluetooth/MutableBluetoothGattService.java deleted file mode 100644 index 927f5ab20b4..00000000000 --- a/framework/java/android/bluetooth/MutableBluetoothGattService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Represents a Bluetooth Gatt Service - * @hide - */ -public class MutableBluetoothGattService extends BluetoothGattService { - - /** - * Create a new MutableBluetoothGattService. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this service - * @param serviceType The type of this service (primary/secondary) - */ - public MutableBluetoothGattService(UUID uuid, int serviceType) { - super(uuid, serviceType); - } - - /** - * Add an included service to this service. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param service The service to be added - * @return true, if the included service was added to the service - */ - public boolean addService(BluetoothGattService service) { - mIncludedServices.add(service); - return true; - } - - /** - * Add a characteristic to this service. - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param characteristic The characteristics to be added - * @return true, if the characteristic was added to the service - */ - public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) { - mCharacteristics.add(characteristic); - characteristic.setService(this); - return true; - } - - /** - * Force the instance ID. - * This is needed for conformance testing only. - * @hide - */ - public void setInstanceId(int instanceId) { - mInstanceId = instanceId; - } - - /** - * Force the number of handles to reserve for this service. - * This is needed for conformance testing only. - * @hide - */ - public void setHandles(int handles) { - mHandles = handles; - } -} -- GitLab From dca236ec3b92e3d5fad1e52dfb30567dc2be1e38 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 3 Apr 2013 14:26:43 -0700 Subject: [PATCH 0261/1408] Fix minor LE descriptor permissions issue Change-Id: Idded482a08f17a1373adf3e3a51709ff585d4f24 --- framework/java/android/bluetooth/BluetoothGattServer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index d1f4b82f982..644c619b9a6 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -554,9 +554,10 @@ public final class BluetoothGattServer implements BluetoothProfile { List descriptors = characteristic.getDescriptors(); for (BluetoothGattDescriptor descriptor: descriptors) { + permission = ((characteristic.getKeySize() - 7) << 12) + + descriptor.getPermissions(); mService.addDescriptor(mServerIf, - new ParcelUuid(descriptor.getUuid()), - descriptor.getPermissions()); + new ParcelUuid(descriptor.getUuid()), permission); } } -- GitLab From 18b2c9aa6d972220ebd0669280377b4e97ff3459 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 3 Apr 2013 00:29:27 -0700 Subject: [PATCH 0262/1408] Change BluetoothGattCallback methods argument from BluetoothDevice to BluetoothGatt Change name of BluetoothDevice#connectGattServer to BluetoothDevice#connectGatt Add BluetoothGatt#getDevice to retrieve device from BluetoothGatt Add BluetoothGatt#connect() to reconnect back to the server. Make BluetoothGatt#close() public to clean up/unregister callback Add BluetoothDevice.getType() to return int of bug 8529188 Change-Id: Iebd9ac68cc7a64c43972e617dd3068f66c8ea0b2 --- .../android/bluetooth/BluetoothDevice.java | 44 +++++++++++++- .../java/android/bluetooth/BluetoothGatt.java | 58 ++++++++++++++----- .../bluetooth/BluetoothGattCallback.java | 41 +++++++------ .../BluetoothGattCharacteristic.java | 4 ++ .../bluetooth/BluetoothGattDescriptor.java | 4 ++ .../bluetooth/BluetoothGattService.java | 3 + .../java/android/bluetooth/IBluetooth.aidl | 1 + 7 files changed, 122 insertions(+), 33 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 83e95ca9b94..3c1ec90f19c 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -261,6 +261,26 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; + /** + * Bluetooth device type, Unknown + */ + public static final int DEVICE_TYPE_UNKNOWN = 0; + + /** + * Bluetooth device type, Classic - BR/EDR devices + */ + public static final int DEVICE_TYPE_CLASSIC = 1; + + /** + * Bluetooth device type, Low Energy - LE-only + */ + public static final int DEVICE_TYPE_LE = 2; + + /** + * Bluetooth device type, Dual Mode - BR/EDR/LE + */ + public static final int DEVICE_TYPE_DUAL = 3; + /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it @@ -601,6 +621,26 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** + * Get the Bluetooth device type of the remote device. + * + *

      Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} + * {@link #DEVICE_TYPE_DUAL}. + * {@link #DEVICE_TYPE_UNKNOWN} if it's not available + */ + public int getType() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); + return DEVICE_TYPE_UNKNOWN; + } + try { + return sService.getRemoteType(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return DEVICE_TYPE_UNKNOWN; + } + /** * Get the Bluetooth alias of the remote device. *

      Alias is the locally modified name of a remote device. @@ -1139,8 +1179,8 @@ public final class BluetoothDevice implements Parcelable { * device becomes available (true). * @throws IllegalArgumentException if callback is null */ - public BluetoothGatt connectGattServer(Context context, boolean autoConnect, - BluetoothGattCallback callback) { + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index f9ce6eacace..bffe64b3570 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -43,7 +43,7 @@ import java.util.UUID; * with Bluetooth Smart or Smart Ready devices. * *

      To connect to a remote peripheral device, create a {@link BluetoothGattCallback} - * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class. + * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. * GATT capable devices can be discovered using the Bluetooth device discovery or BLE * scan process. */ @@ -66,6 +66,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final int CONN_STATE_CONNECTING = 1; private static final int CONN_STATE_CONNECTED = 2; private static final int CONN_STATE_DISCONNECTING = 3; + private static final int CONN_STATE_CLOSED = 4; private List mServices; @@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mCallback.onConnectionStateChange(mDevice, GATT_FAILURE, + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, BluetoothProfile.STATE_DISCONNECTED); synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; @@ -164,7 +165,7 @@ public final class BluetoothGatt implements BluetoothProfile { int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; try { - mCallback.onConnectionStateChange(mDevice, status, profileState); + mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -291,7 +292,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onServicesDiscovered(mDevice, status); + mCallback.onServicesDiscovered(BluetoothGatt.this, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -338,7 +339,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); try { - mCallback.onCharacteristicRead(characteristic, status); + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -384,7 +385,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; try { - mCallback.onCharacteristicWrite(characteristic, status); + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -415,7 +416,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); try { - mCallback.onCharacteristicChanged(characteristic); + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -464,7 +465,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = true; try { - mCallback.onDescriptorRead(descriptor, status); + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -512,7 +513,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; try { - mCallback.onDescriptorWrite(descriptor, status); + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -529,7 +530,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onReliableWriteCompleted(mDevice, status); + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -546,7 +547,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onReadRemoteRssi(mDevice, rssi, status); + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -563,12 +564,13 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Close the connection to the gatt service. + * Close this Bluetooth GATT client. */ - /*package*/ void close() { + public void close() { if (DBG) Log.d(TAG, "close()"); unregisterApp(); + mConnState = CONN_STATE_CLOSED; } /** @@ -694,7 +696,35 @@ public final class BluetoothGatt implements BluetoothProfile { } catch (RemoteException e) { Log.e(TAG,"",e); } - // TBD deregister after conneciton is torn down + } + + /** + * Connect back to remote device. + * + *

      This method is used to re-connect to a remote device after the + * connection has been dropped. If the device is not in range, the + * re-connection will be triggered once the device is back in range. + * + * @return true, if the connection attempt was initiated successfully + */ + public boolean connect() { + try { + mService.clientConnect(mClientIf, mDevice.getAddress(), + false); // autoConnect is inverse of "isDirect" + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Return the remote bluetooth device this GATT client targets to + * + * @return remote bluetooth device + */ + public BluetoothDevice getDevice() { + return mDevice; } /** diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index c9e5fea713d..2259c1eff5c 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -16,23 +16,22 @@ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. */ public abstract class BluetoothGattCallback { /** - * Callback indicating when a remote device has been connected or disconnected. + * Callback indicating when GATT client has connected/disconnected to/from a remote + * GATT server. * - * @param device Remote device that has been connected or disconnected. + * @param gatt GATT client * @param status Status of the connect or disconnect operation. * @param newState Returns the new connection state. Can be one of * {@link BluetoothProfile#STATE_DISCONNECTED} or * {@link BluetoothProfile#STATE_CONNECTED} */ - public void onConnectionStateChange(BluetoothDevice device, int status, + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { } @@ -40,22 +39,23 @@ public abstract class BluetoothGattCallback { * Callback invoked when the list of remote services, characteristics and descriptors * for the remote device have been updated, ie new services have been discovered. * - * @param device Remote device + * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device * has been explored successfully. */ - public void onServicesDiscovered(BluetoothDevice device, int status) { + public void onServicesDiscovered(BluetoothGatt gatt, int status) { } /** * Callback reporting the result of a characteristic read operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} * @param characteristic Characteristic that was read from the associated * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully. */ - public void onCharacteristicRead(BluetoothGattCharacteristic characteristic, + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } @@ -68,52 +68,59 @@ public abstract class BluetoothGattCallback { * value to the desired value to be written. If the values don't match, * the application must abort the reliable write transaction. * + * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} * @param characteristic Characteristic that was written to the associated * remote device. * @param status The result of the write operation */ - public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, - int status) { + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { } /** * Callback triggered as a result of a remote characteristic notification. * + * @param gatt GATT client the characteristic is associated with * @param characteristic Characteristic that has been updated as a result * of a remote notification event. */ - public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) { + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { } /** * Callback reporting the result of a descriptor read operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} * @param descriptor Descriptor that was read from the associated * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully */ - public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) { + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { } /** * Callback indicating the result of a descriptor write operation. * + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} * @param descriptor Descriptor that was writte to the associated * remote device. * @param status The result of the write operation */ - public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) { + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { } /** * Callback invoked when a reliable write transaction has been completed. * - * @param device Remote device + * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write * transaction was executed successfully */ - public void onReliableWriteCompleted(BluetoothDevice device, int status) { + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { } /** @@ -122,10 +129,10 @@ public abstract class BluetoothGattCallback { * This callback is triggered in response to the * {@link BluetoothGatt#readRemoteRssi} function. * - * @param device Identifies the remote device + * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} * @param rssi The RSSI value for the remote device * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully */ - public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index d63d97e22eb..033f0798165 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -22,6 +22,10 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Characteristic + * + *

      A GATT characteristic is a basic data element used to construct a GATT service, + * {@link BluetoothGattService}. The characteristic contains a value as well as + * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}. */ public class BluetoothGattCharacteristic { diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 6ba2db704f4..1cd68787ce0 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -20,6 +20,10 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Descriptor + * + *

      GATT Descriptors contain additional information and attributes of a GATT + * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe + * the characteristic's features or to control certain behaviours of the characteristic. */ public class BluetoothGattDescriptor { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index c3b3cfe2393..39a435becb5 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -23,6 +23,9 @@ import java.util.UUID; /** * Represents a Bluetooth GATT Service + * + *

      Gatt Service contains a collection of {@link BluetoothGattCharacteristic}, + * as well as referenced services. */ public class BluetoothGattService { diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index d016c2623be..80806f97d48 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -60,6 +60,7 @@ interface IBluetooth int getBondState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); + int getRemoteType(in BluetoothDevice device); String getRemoteAlias(in BluetoothDevice device); boolean setRemoteAlias(in BluetoothDevice device, in String name); int getRemoteClass(in BluetoothDevice device); -- GitLab From 6b79decb475fdfc95e7ea193016d015bb0e94bd5 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 5 Apr 2013 09:34:11 -0700 Subject: [PATCH 0263/1408] Make BluetoothGattServer.close() public This functino is needed for applications to un-register from the Bluetooth stack. bug 8591003 Change-Id: Id05f592245d1d90791d34c3617aadac67bc6502c --- framework/java/android/bluetooth/BluetoothGattServer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 644c619b9a6..d7f150b3191 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -288,9 +288,9 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Close the connection to the gatt service. + * Close this GATT server instance. */ - /*package*/ void close() { + public void close() { if (DBG) Log.d(TAG, "close()"); unregisterCallback(); } -- GitLab From 01e8e2ac8e6994921b811f741b2d4dbe99b50b19 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 11 Apr 2013 16:36:26 -0700 Subject: [PATCH 0264/1408] Remove BluetoothAdapterCallback. Simplify leScan Api App does not need to explicitly register/unregister callback bug 8599881 Change-Id: I18cfef14d7ddb344722945e657dcb959823b412b --- .../android/bluetooth/BluetoothAdapter.java | 422 +++++++++++------- .../bluetooth/BluetoothAdapterCallback.java | 57 --- .../android/bluetooth/BluetoothManager.java | 6 +- 3 files changed, 260 insertions(+), 225 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothAdapterCallback.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2e9c9e334d6..3498bb8e47b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,7 +20,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelUuid; @@ -30,11 +29,14 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.HashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -357,9 +359,7 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; - private Handler mServiceRecordHandler; - private BluetoothAdapterCallback mCallback; - private int mClientIf; + private final Map mLeScanClients; /** * Get a handle to the default local Bluetooth adapter. @@ -394,7 +394,7 @@ public final class BluetoothAdapter { mService = managerService.registerAdapter(mManagerCallback); } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; - mServiceRecordHandler = null; + mLeScanClients = new HashMap(); } /** @@ -1409,72 +1409,38 @@ public final class BluetoothAdapter { } /** - * Register an callback to receive async results, such as LE scan result. + * Callback interface used to deliver LE scan results. * - *

      This is an asynchronous call. The callback - * {@link BluetoothAdapterCallback#onCallbackRegistration} - * is used to notify success or failure if the function returns true. - * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks. - * @return If true, the callback will be called to notify success or failure, - * false on immediate error - */ - public boolean registerCallback(BluetoothAdapterCallback callback) { - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - mCallback = callback; - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - - iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); - return true; - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - } - - /** - * Unregister the registered callback. - */ - public boolean unRegisterCallback(BluetoothAdapterCallback callback) { - if (callback != mCallback) return false; - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - - iGatt.unregisterClient(mClientIf); - return true; - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } + * @see #startLeScan(LeScanCallback) + * @see #startLeScan(UUID[], LeScanCallback) + */ + public interface LeScanCallback { + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothAdapter#startLeScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); } /** * Starts a scan for Bluetooth LE devices. * *

      Results of the scan are reported using the - * {@link BluetoothAdapterCallback#onLeScan} callback. + * {@link LeScanCallback#onLeScan} callback. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully */ - public boolean startLeScan() { - if (DBG) Log.d(TAG, "startLeScan()"); - if (mClientIf == 0) return false; - - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - iGatt.startScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; + public boolean startLeScan(LeScanCallback callback) { + return startLeScan(null, callback); } /** @@ -1482,155 +1448,281 @@ public final class BluetoothAdapter { * advertise given services. * *

      Devices which advertise all specified services are reported using the - * {@link BluetoothAdapterCallback#onLeScan} callback. + * {@link LeScanCallback#onLeScan} callback. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param serviceUuids Array of services to look for + * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully */ - public boolean startLeScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startLeScan() - with UUIDs"); - if (mClientIf == 0) return false; + public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) { + if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); - } - iGatt.startScanWithUuids(mClientIf, false, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); + if (callback == null) { + if (DBG) Log.e(TAG, "startLeScan: null callback"); return false; } - return true; + synchronized(mLeScanClients) { + if (mLeScanClients.containsKey(callback)) { + if (DBG) Log.e(TAG, "LE Scan has already started"); + return false; + } + + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids); + + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.scanStarted()) { + mLeScanClients.put(callback, wrapper); + return true; + } + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + return false; } /** * Stops an ongoing Bluetooth LE device scan. * *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopLeScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mClientIf == 0) return; - - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - iGatt.stopScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); + * + * @param callback used to identify which scan to stop + * must be the same handle used to start the scan + */ + public void stopLeScan(LeScanCallback callback) { + if (DBG) Log.d(TAG, "stopLeScan()"); + GattCallbackWrapper wrapper; + synchronized(mLeScanClients) { + wrapper = mLeScanClients.remove(callback); + if (wrapper == null) return; } + wrapper.stopLeScan(); } /** * Bluetooth GATT interface callbacks */ - private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { - /** - * Application interface registered - app is ready to go - */ - public void onClientRegistered(int status, int clientIf) { - if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status - + " clientIf=" + clientIf); - mClientIf = clientIf; - mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ? - BluetoothAdapterCallback.CALLBACK_REGISTERED : - BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE); - } + private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int LE_CALLBACK_REG_TIMEOUT = 2000; + private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + + private final LeScanCallback mLeScanCb; + // mLeHandle 0: not registered + // -1: scan stopped + // >0: registered and scan started + private int mLeHandle; + private final UUID[] mScanFilter; + private WeakReference mBluetoothAdapter; + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, + LeScanCallback leScanCb, UUID[] uuid) { + mBluetoothAdapter = new WeakReference(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + } - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op + public boolean scanStarted() { + boolean started = false; + synchronized(this) { + if (mLeHandle == -1) return false; + + int count = 0; + // wait for callback registration and LE scan to start + while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + try { + wait(LE_CALLBACK_REG_TIMEOUT); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + count++; + } + started = (mLeHandle > 0); } + return started; + } - /** - * Callback reporting an LE scan result. - * @hide - */ - public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onLeScan(getRemoteDevice(address), rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + public void stopLeScan() { + synchronized(this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopScan(mLeHandle, false); + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop scan and unregister" + e); + } + } else { + Log.e(TAG, "stopLeScan, BluetoothAdapter is null"); } + mLeHandle = -1; + notifyAll(); } + } - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } + /** + * Application interface registered - app is ready to go + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + synchronized(this) { + if (mLeHandle == -1) { + if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); + } - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + IBluetoothGatt iGatt = null; + try { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } + } else { + Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); + mLeHandle = -1; + } + } catch (RemoteException e) { + Log.e(TAG, "fail to start le scan: " + e); + mLeHandle = -1; + } + if (mLeHandle == -1) { + // registration succeeded but start scan failed + if (iGatt != null) { + try { + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "fail to unregister callback: " + mLeHandle + + " error: " + e); + } + } + } + } else { + // registration failed + mLeHandle = -1; + } + notifyAll(); } + } - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descUuid) { - // no op - } + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - public void onSearchComplete(String address, int status) { - // no op + // Check null in case the scan has been stopped + synchronized(this) { + if (mLeHandle <= 0) return; } - - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op + try { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter == null) { + Log.d(TAG, "onScanResult, BluetoothAdapter null"); + return; + } + mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); } + } - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } - public void onNotify(String address, int srvcType, + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + // no op + } + + public void onSearchComplete(String address, int status) { + // no op + } + + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } + // no op + } - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid, byte[] value) { - // no op - } + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + // no op + } - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid) { - // no op - } + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + // no op + } - public void onExecuteWrite(String address, int status) { - // no op - } + public void onExecuteWrite(String address, int status) { + // no op + } - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - }; + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + } } diff --git a/framework/java/android/bluetooth/BluetoothAdapterCallback.java b/framework/java/android/bluetooth/BluetoothAdapterCallback.java deleted file mode 100644 index a726bc91895..00000000000 --- a/framework/java/android/bluetooth/BluetoothAdapterCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * This abstract class is used to implement {@link BluetoothAdapter} callbacks. - */ -public abstract class BluetoothAdapterCallback { - - /** - * Indicates the callback has been registered successfully - */ - public static final int CALLBACK_REGISTERED = 0; - - /** - * Indicates the callback registration has failed - */ - public static final int CALLBACK_REGISTRATION_FAILURE = 1; - - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link #CALLBACK_REGISTERED} if the application - * was successfully registered. - */ - public void onCallbackRegistration(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothAdapter#startLeScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { - } -} diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 19083b55dac..172f3bcdefe 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -127,7 +127,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return connectedDevices; connectedDevices = iGatt.getDevicesMatchingConnectionStates( @@ -172,7 +172,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; devices = iGatt.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { @@ -203,7 +203,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) { Log.e(TAG, "Fail to get GATT Server connection"); return null; -- GitLab From f6fcd9b26b8a1df94a02a0ff27f4f0fa3fcf7339 Mon Sep 17 00:00:00 2001 From: zzy Date: Tue, 16 Apr 2013 17:17:37 -0700 Subject: [PATCH 0265/1408] Added flush() for bluetooth output stream Bug 8498784 Zebra QL420 Plus Bluetooth printer fails on Android 4.2.2 --- .../android/bluetooth/BluetoothOutputStream.java | 11 +++++++++++ .../java/android/bluetooth/BluetoothSocket.java | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index 62242a2672f..117dd47c1ab 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -84,4 +84,15 @@ import java.io.OutputStream; } mSocket.write(b, offset, count); } + /** + * Wait until the data in sending queue is emptied. A polling version + * for flush implementation. Use it to ensure the writing data afterwards will + * be packed in the new RFCOMM frame. + * @throws IOException + * if an i/o error occurs. + * @since Android 4.2.3 + */ + public void flush() throws IOException { + mSocket.flush(); + } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 8029a1a25ce..a19341c07da 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -192,6 +192,7 @@ public final class BluetoothSocket implements Closeable { if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); if(fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); + as.close(); throw new IOException("bt socket acept failed"); } as.mSocket = new LocalSocket(fds[0]); @@ -407,6 +408,17 @@ public final class BluetoothSocket implements Closeable { if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); } + /** + * Wait until the data in sending queue is emptied. A polling version + * for flush implementation. Used to ensure the writing data afterwards will + * be packed in new RFCOMM frame. + * @throws IOException + * if an i/o error occurs. + */ + /*package*/ void flush() throws IOException { + if (VDBG) Log.d(TAG, "flush: " + mSocketOS); + mSocketOS.flush(); + } /*package*/ int read(byte[] b, int offset, int length) throws IOException { -- GitLab From 1438e9ec8b03589b2b9ec133f0bc298bcd49b72d Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 24 Apr 2013 17:51:37 -0700 Subject: [PATCH 0266/1408] Change permission of LE scan APIs from BLUETOOTH to BLUETOOTH_ADMIN bug 8667898 Change-Id: Iaff19fe72b16a96a6cf2f5b9140e369098567d2b --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3498bb8e47b..cfbfb48d4a1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1434,7 +1434,7 @@ public final class BluetoothAdapter { *

      Results of the scan are reported using the * {@link LeScanCallback#onLeScan} callback. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully @@ -1450,7 +1450,7 @@ public final class BluetoothAdapter { *

      Devices which advertise all specified services are reported using the * {@link LeScanCallback#onLeScan} callback. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param serviceUuids Array of services to look for * @param callback the callback LE scan results are delivered @@ -1490,7 +1490,7 @@ public final class BluetoothAdapter { /** * Stops an ongoing Bluetooth LE device scan. * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission. + *

      Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param callback used to identify which scan to stop * must be the same handle used to start the scan -- GitLab From c3ef4fc08a0381c13161d681fe7364d82876216a Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 8 May 2013 19:26:57 -0700 Subject: [PATCH 0267/1408] Donot bind to GATT service when BLE is not supported bug 8664724 Change-Id: I9b9222cd5877babcded73798a5d1ff13fd10e791 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 +++++- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cfbfb48d4a1..7ec73ef7413 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1472,9 +1472,13 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + UUID uuid = UUID.randomUUID(); GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids); - iGatt.registerClient(new ParcelUuid(uuid), wrapper); if (wrapper.scanStarted()) { mLeScanClients.put(callback, wrapper); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3c1ec90f19c..79a5ffea46a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1187,6 +1187,10 @@ public final class BluetoothDevice implements Parcelable { IBluetoothManager managerService = adapter.getBluetoothManager(); try { IBluetoothGatt iGatt = managerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return null; + } BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); gatt.connect(autoConnect, callback); return gatt; -- GitLab From 5b42df5d8fb13292be8d1406b9f4a7c1d4add7b9 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 8 May 2013 19:26:57 -0700 Subject: [PATCH 0268/1408] Donot bind to GATT service when BLE is not supported bug 8664724 Change-Id: I9b9222cd5877babcded73798a5d1ff13fd10e791 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 +++++- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cfbfb48d4a1..7ec73ef7413 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1472,9 +1472,13 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + UUID uuid = UUID.randomUUID(); GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids); - iGatt.registerClient(new ParcelUuid(uuid), wrapper); if (wrapper.scanStarted()) { mLeScanClients.put(callback, wrapper); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3c1ec90f19c..79a5ffea46a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1187,6 +1187,10 @@ public final class BluetoothDevice implements Parcelable { IBluetoothManager managerService = adapter.getBluetoothManager(); try { IBluetoothGatt iGatt = managerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return null; + } BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); gatt.connect(autoConnect, callback); return gatt; -- GitLab From 977f260e31f6db1889ab6928f4f87f0fff1d1f3d Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 13 May 2013 14:04:12 -0700 Subject: [PATCH 0269/1408] BluetoothGatt: Print stack trace if an exception occurs in a callback Change-Id: Iaaaaca8347197aae5c7fcecb2325ef4836969434 --- .../java/android/bluetooth/BluetoothGatt.java | 18 +++++++++--------- .../android/bluetooth/BluetoothGattServer.java | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index bffe64b3570..4df6e7cfd4a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -167,7 +167,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } synchronized(mStateLock) { @@ -294,7 +294,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onServicesDiscovered(BluetoothGatt.this, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -341,7 +341,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -387,7 +387,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -418,7 +418,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -467,7 +467,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -515,7 +515,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -532,7 +532,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -549,7 +549,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } }; diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index d7f150b3191..d3b85a9e3d9 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -108,7 +108,7 @@ public final class BluetoothGattServer implements BluetoothProfile { connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -128,7 +128,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback.onServiceAdded((int)status, service); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -154,7 +154,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -186,7 +186,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -214,7 +214,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback.onCharacteristicWriteRequest(device, transId, characteristic, isPrep, needRsp, offset, value); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -250,7 +250,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback.onDescriptorWriteRequest(device, transId, descriptor, isPrep, needRsp, offset, value); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } @@ -270,7 +270,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback.onExecuteWrite(device, transId, execWrite); } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + Log.w(TAG, "Unhandled exception in callback", ex); } } }; -- GitLab From 4cbd2cd0de44b4fdcbdcfbc4915e646a500a309f Mon Sep 17 00:00:00 2001 From: Ravi Nagarajan Date: Mon, 6 May 2013 01:18:46 -0700 Subject: [PATCH 0270/1408] Remove roamChanged API Roam state listerner will be handled inside the Bluetooth apk. Remove the binder API from wrapper class bug 8781689 Change-Id: I31c2b899069774620b1406faeb3c68ad1e60b599 --- .../android/bluetooth/BluetoothHeadset.java | 21 ------------------- .../android/bluetooth/IBluetoothHeadset.aidl | 1 - 2 files changed, 22 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/IBluetoothHeadset.aidl diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 793d79858c6..963e9fcb211 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -814,27 +814,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } } - /** - * Notify Headset of phone roam state change. - * This is a backdoor for phone app to call BluetoothHeadset since - * there is currently not a good way to get roaming state change outside - * of phone app. - * - * @hide - */ - public void roamChanged(boolean roaming) { - if (mService != null && isEnabled()) { - try { - mService.roamChanged(roaming); - } 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())); - } - } - /** * Send Headset of CLCC response * diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl old mode 100644 new mode 100755 index fc7627ae5c8..285eea7546f --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,7 +50,6 @@ interface IBluetoothHeadset { boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); - void roamChanged(boolean roam); void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type); } -- GitLab From 4d0692aa70a7a275acbafd83d6cf786570b7f1c3 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 23 May 2013 17:39:35 -0700 Subject: [PATCH 0271/1408] Change GATT_FAILURE from 0 to 0x101 GATT_FAILURE 0 clashes with GATT_SUCCESS. For now, they represent different kind of faiure. One for Gatt operation, one for Gatt registration. But we should separate them as good practice. bug 9109238 Change-Id: I686bb6004bc8831aa3e3bbffe2f7c8e24412e3c3 --- framework/java/android/bluetooth/BluetoothGatt.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index bffe64b3570..f3032062cf0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -70,9 +70,6 @@ public final class BluetoothGatt implements BluetoothProfile { private List mServices; - /** A GATT operation failed */ - public static final int GATT_FAILURE = 0; - /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; @@ -97,6 +94,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + /** A GATT operation failed, errors other than the above */ + public static final int GATT_FAILURE = 0x101; + /** * No authentication required. * @hide -- GitLab From 1626eab8a14b70270ad1885ea906dd3499c89560 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Sun, 26 May 2013 21:03:16 +0000 Subject: [PATCH 0272/1408] Revert "Change GATT_FAILURE from 0 to 0x101" This reverts commit 4d0692aa70a7a275acbafd83d6cf786570b7f1c3 Change-Id: I467f7cfc8626b0fc54509a89158cd2d84a0c10b8 --- framework/java/android/bluetooth/BluetoothGatt.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index f3032062cf0..bffe64b3570 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -70,6 +70,9 @@ public final class BluetoothGatt implements BluetoothProfile { private List mServices; + /** A GATT operation failed */ + public static final int GATT_FAILURE = 0; + /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; @@ -94,9 +97,6 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; - /** A GATT operation failed, errors other than the above */ - public static final int GATT_FAILURE = 0x101; - /** * No authentication required. * @hide -- GitLab From b46e8ea169ac2afa88ed41011bfd1ca08cf61f61 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 28 May 2013 21:06:50 +0000 Subject: [PATCH 0273/1408] Revert "Revert "Change GATT_FAILURE from 0 to 0x101"" This reverts commit 1626eab8a14b70270ad1885ea906dd3499c89560 bug 9109238 Change-Id: Ie1fc3d7420e6c401d3db29499d8846e6b70a61bb --- framework/java/android/bluetooth/BluetoothGatt.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index bffe64b3570..f3032062cf0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -70,9 +70,6 @@ public final class BluetoothGatt implements BluetoothProfile { private List mServices; - /** A GATT operation failed */ - public static final int GATT_FAILURE = 0; - /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; @@ -97,6 +94,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + /** A GATT operation failed, errors other than the above */ + public static final int GATT_FAILURE = 0x101; + /** * No authentication required. * @hide -- GitLab From abb655872f7a950d640bbb78d14a0660f458db62 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 29 May 2013 10:19:06 -0700 Subject: [PATCH 0274/1408] Update javadoc to give app write better guidence Update javadoc of close methods of GATT cliet and server Update javadoc of BluetoothAdapter bug 8963528 Change-Id: I45ec618fd495225ed11a6171f33bfdc218397d4c --- .../java/android/bluetooth/BluetoothAdapter.java | 11 ++++++++--- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 3 +++ .../java/android/bluetooth/BluetoothGattCallback.java | 3 +++ .../java/android/bluetooth/BluetoothGattServer.java | 3 +++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7ec73ef7413..79bb476cb21 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -47,17 +47,22 @@ import java.util.UUID; * device discovery, query a list of bonded (paired) devices, * instantiate a {@link BluetoothDevice} using a known MAC address, and create * a {@link BluetoothServerSocket} to listen for connection requests from other - * devices. + * devices, and start a scan for Bluetooth LE devices. * *

      To get a {@link BluetoothAdapter} representing the local Bluetooth - * adapter, call the static {@link #getDefaultAdapter} method. + * adapter, when running on JELLY_BEAN_MR1 and below, call the + * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and + * higher, retrieve it through + * {@link android.content.Context#getSystemService} with + * {@link android.content.Context#BLUETOOTH_SERVICE}. * Fundamentally, this is your starting point for all * Bluetooth actions. Once you have the local adapter, you can get a set of * {@link BluetoothDevice} objects representing all paired devices with * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to * listen for incoming connection requests with - * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}. + * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for + * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. * *

      Note: * Most methods require the {@link android.Manifest.permission#BLUETOOTH} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 79a5ffea46a..3ee7142d8d6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -107,7 +107,7 @@ public final class BluetoothDevice implements Parcelable { *

      Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. *

      Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * @see {@link BluetoothClass} + * {@see BluetoothClass} */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CLASS_CHANGED = diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index bffe64b3570..69dc4e225f7 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -565,6 +565,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Close this Bluetooth GATT client. + * + * Application should call this method as early as possible after it is done with + * this GATT client. */ public void close() { if (DBG) Log.d(TAG, "close()"); diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 2259c1eff5c..80ea4a69569 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -27,6 +27,7 @@ public abstract class BluetoothGattCallback { * * @param gatt GATT client * @param status Status of the connect or disconnect operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. * @param newState Returns the new connection state. Can be one of * {@link BluetoothProfile#STATE_DISCONNECTED} or * {@link BluetoothProfile#STATE_CONNECTED} @@ -72,6 +73,7 @@ public abstract class BluetoothGattCallback { * @param characteristic Characteristic that was written to the associated * remote device. * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { @@ -108,6 +110,7 @@ public abstract class BluetoothGattCallback { * @param descriptor Descriptor that was writte to the associated * remote device. * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index d7f150b3191..78d536bffa1 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -289,6 +289,9 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Close this GATT server instance. + * + * Application should call this method as early as possible after it is done with + * this GATT server. */ public void close() { if (DBG) Log.d(TAG, "close()"); -- GitLab From c27359e696d9dd4aa89d77d3eb85c79348a206b5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 11 Jun 2013 14:13:09 -0700 Subject: [PATCH 0275/1408] Explicit locale when formatting machine strings. Bug: 9390451 Change-Id: I3581c53407554a1dffd541fb42b06d68f20a7be0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 ++- framework/java/android/bluetooth/BluetoothSocket.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 79bb476cb21..72ecda1211e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.HashMap; import java.util.LinkedList; +import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; @@ -433,7 +434,7 @@ public final class BluetoothAdapter { if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } - return new BluetoothDevice(String.format("%02X:%02X:%02X:%02X:%02X:%02X", + return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2], address[3], address[4], address[5])); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index a19341c07da..d10eaea2fba 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.Locale; import java.util.UUID; import android.net.LocalSocket; import java.nio.ByteOrder; @@ -473,7 +474,7 @@ public final class BluetoothSocket implements Closeable { return mPort; } private String convertAddr(final byte[] addr) { - return String.format("%02X:%02X:%02X:%02X:%02X:%02X", + return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); } private String waitSocketSignal(InputStream is) throws IOException { -- GitLab From ee66af1a828c8eaba3605d73c3d0cdae4ba7c1c2 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Mon, 10 Jun 2013 20:28:31 -0700 Subject: [PATCH 0276/1408] Add debug menu to enable btsnoop bug: 8059358 Change-Id: I2d5f13e68defefb92e0b11b749fe77ad67215f36 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 2 ++ 2 files changed, 21 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 79bb476cb21..507554402c6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -598,6 +598,25 @@ public final class BluetoothAdapter { return null; } + /** + * enable or disable Bluetooth HCI snoop log. + * + *

      Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission + * + * @return true to indicate configure HCI log successfully, or false on + * immediate error + * @hide + */ + public boolean configHciSnoopLog(boolean enable) { + try { + synchronized(mManagerCallback) { + if (mService != null) return mService.configHciSnoopLog(enable); + } + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Get the UUIDs supported by the local Bluetooth adapter. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 80806f97d48..07db8cc9c18 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -80,4 +80,6 @@ interface IBluetooth // For Socket ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); + + boolean configHciSnoopLog(boolean enable); } -- GitLab From 0000fe840e3b862476b663966f01035156398554 Mon Sep 17 00:00:00 2001 From: kmccormick Date: Thu, 6 Jun 2013 11:14:57 -0700 Subject: [PATCH 0277/1408] Doc change: new Bluetooth LE doc Change-Id: Ib6d67497a9b4d54d13b4da8f1f9ee991bc5ef562 --- framework/java/android/bluetooth/package.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index ba75034555e..200a21b8acf 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -1,15 +1,20 @@

      Provides classes that manage Bluetooth functionality, such as scanning for -devices, connecting with devices, and managing data transfer between devices.

      +devices, connecting with devices, and managing data transfer between devices. +The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.

      -

      For more information, see the -Bluetooth guide.

      +

      For more information about Classic Bluetooth, see the +Bluetooth guide. +For more information about Bluetooth Low Energy, see the + +Bluetooth Low Energy guide.

      {@more}

      The Bluetooth APIs let applications:

        -
      • Scan for other Bluetooth devices
      • +
      • Scan for other Bluetooth devices (including Bluetooth Low Energy + devices)
      • Query the local Bluetooth adapter for paired Bluetooth devices
      • Establish RFCOMM channels/sockets
      • Connect to specified sockets on other devices
      • -- GitLab From 626db853b28d64e493dd7b75073d2af838d0bc6a Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Wed, 24 Jul 2013 15:09:45 -0700 Subject: [PATCH 0278/1408] Bluetooth stopped working in master - "bad file descriptor" ignore IllegalArgumentException in BluetoothSocket.close(). bug: 9960585 Change-Id: I301aa388b738dcb92b643fdd96b5c9cb6f8b3efe --- framework/java/android/bluetooth/BluetoothSocket.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..5c534a1e8c1 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -461,8 +461,13 @@ public final class BluetoothSocket implements Closeable { mSocket.close(); mSocket = null; } - if(mPfd != null) - mPfd.detachFd(); + if(mPfd != null) { + try { + mPfd.detachFd(); + } catch (IllegalArgumentException e) { + Log.w(TAG, "ignore IllegalArgumentException"); + } + } } } } -- GitLab From b01210f0dec7a9b2ade13cfb83909d3d11d197f4 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Fri, 26 Jul 2013 18:10:06 +0000 Subject: [PATCH 0279/1408] Revert "Bluetooth stopped working in master - "bad file descriptor"" This reverts commit 626db853b28d64e493dd7b75073d2af838d0bc6a. Change-Id: I9ffaf5580924c150145d37ec38ffde602199b7e2 --- framework/java/android/bluetooth/BluetoothSocket.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 5c534a1e8c1..d10eaea2fba 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -461,13 +461,8 @@ public final class BluetoothSocket implements Closeable { mSocket.close(); mSocket = null; } - if(mPfd != null) { - try { - mPfd.detachFd(); - } catch (IllegalArgumentException e) { - Log.w(TAG, "ignore IllegalArgumentException"); - } - } + if(mPfd != null) + mPfd.detachFd(); } } } -- GitLab From 3875ec6a68e6257d1ac7abe94f4173248581d8de Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 4 Aug 2013 16:50:16 -0700 Subject: [PATCH 0280/1408] Start restricting service calls with implicit intents. The bindService() and startService() calls have always had undefined behavior when used with an implicit Intent and there are multiple matching services. Because of this, it is not safe for applications to use such Intents when interacting with services, yet the platform would merrily go about doing... something. In KLP I want to cause this case to be invalid, resulting in an exception thrown back to the app. Unfortunately there are lots of (scary) things relying on this behavior, so we can't immediately turn it into an exception, even one qualified by the caller's target SDK version. In this change, we start loggin a WTF when such a call happens, and clean up some stuff in Bluetooth that was doing this behavior. Change-Id: I62e25d07890588d2362104e20b054aebb6c0e007 --- .../java/android/bluetooth/BluetoothA2dp.java | 17 +++++++++---- .../android/bluetooth/BluetoothHeadset.java | 17 +++++++++---- .../android/bluetooth/BluetoothHealth.java | 17 +++++++++---- .../bluetooth/BluetoothInputDevice.java | 18 ++++++++----- .../java/android/bluetooth/BluetoothPan.java | 25 +++++++++++-------- .../java/android/bluetooth/BluetoothPbap.java | 19 ++++++++------ 6 files changed, 75 insertions(+), 38 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 6fdf3b47b85..d7d8cdbe439 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -128,9 +128,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -157,9 +155,18 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothA2dp.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); + return false; } + return true; } /*package*/ void close() { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 963e9fcb211..5a5764d1d0a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -241,9 +241,7 @@ public final class BluetoothHeadset implements BluetoothProfile { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -270,9 +268,18 @@ public final class BluetoothHeadset implements BluetoothProfile { } } - if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHeadset.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent); + return false; } + return true; } /** diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index cb23662f6c1..b1a084a7b30 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -117,9 +117,7 @@ public final class BluetoothHealth implements BluetoothProfile { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Health Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -483,9 +481,18 @@ public final class BluetoothHealth implements BluetoothProfile { } } - if (!context.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Health Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHealth.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent); + return false; } + return true; } /*package*/ void close() { diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index db7e424d40e..f9c789c58a4 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -206,9 +206,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -237,10 +235,18 @@ public final class BluetoothInputDevice implements BluetoothProfile { } } - if (!context.bindService(new Intent(IBluetoothInputDevice.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothInputDevice.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); + return false; } + return true; } /*package*/ void close() { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index e25ec86a866..83d4329e971 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -137,12 +137,20 @@ public final class BluetoothPan implements BluetoothProfile { } catch (RemoteException re) { Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); } - Log.d(TAG, "BluetoothPan() call bindService"); - if (!context.bindService(new Intent(IBluetoothPan.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service"); + if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); + doBind(); + if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothPan.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); + return false; } - Log.d(TAG, "BluetoothPan(), bindService called"); + return true; } /*package*/ void close() { @@ -170,11 +178,8 @@ public final class BluetoothPan implements BluetoothProfile { //Handle enable request to bind again. if (on) { Log.d(TAG, "onBluetoothStateChange(on) call bindService"); - if (!mContext.bindService(new Intent(IBluetoothPan.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service"); - } - Log.d(TAG, "BluetoothPan(), bindService called"); + doBind(); + if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); } else { if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index b5280e53377..c42251f4367 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -129,11 +129,7 @@ public class BluetoothPbap { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService( - new Intent(IBluetoothPbap.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth PBAP Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -158,9 +154,18 @@ public class BluetoothPbap { Log.e(TAG,"",e); } } - if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Pbap Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothPbap.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); + return false; } + return true; } protected void finalize() throws Throwable { -- GitLab From 4bb6a3bab63e4f4a498a3b9a8350c68281bfebec Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Wed, 7 Aug 2013 16:22:47 -0700 Subject: [PATCH 0281/1408] If in a mobile captive portal is detected enable fail fast. When captive portal checking completes pass back the result. This is used to enable/disable failing fast for mobile. When failing fast is enabled we don't check for data stalls and thus won't be continually trying to do recovery operations, such as restarting the radio. Bug: 9462512 Change-Id: I0dea0eee519f8ee7f94e79d40e82c18f30d7fe2e --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 81c0a6a87ea..0aedecb82dd 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -152,6 +152,11 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + /** * Re-enable connectivity to a network after a {@link #teardown()}. */ -- GitLab From c926d06d05e7a4839b68a26076064058f8abe029 Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Wed, 7 Aug 2013 16:22:47 -0700 Subject: [PATCH 0282/1408] If in a mobile captive portal is detected enable fail fast. When captive portal checking completes pass back the result. This is used to enable/disable failing fast for mobile. When failing fast is enabled we don't check for data stalls and thus won't be continually trying to do recovery operations, such as restarting the radio. Bug: 9462512 Change-Id: I0dea0eee519f8ee7f94e79d40e82c18f30d7fe2e --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 81c0a6a87ea..0aedecb82dd 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -152,6 +152,11 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + /** * Re-enable connectivity to a network after a {@link #teardown()}. */ -- GitLab From ce1452204226a20651ca4fa7a8bcdb5bdbaa8d71 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 18 Jul 2013 17:31:50 -0700 Subject: [PATCH 0283/1408] Bluetooth MAP profile - sms and mms support initial check-in bug:10116530 Change-Id: I57d022005bcff5bc3e56438a81ac92566f957744 --- .../android/bluetooth/BluetoothDevice.java | 3 + .../java/android/bluetooth/BluetoothMap.java | 300 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 6 + .../java/android/bluetooth/BluetoothUuid.java | 13 +- .../java/android/bluetooth/IBluetoothMap.aidl | 32 ++ 5 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothMap.java create mode 100644 framework/java/android/bluetooth/IBluetoothMap.aidl diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3ee7142d8d6..3acd9b00c05 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -343,6 +343,9 @@ public final class BluetoothDevice implements Parcelable { /**@hide*/ public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2; + /**@hide*/ + public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; + /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, * Contains package name to return reply intent to. diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java new file mode 100644 index 00000000000..7de309fb831 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2008 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +/** + * This class provides the APIs to control the Bluetooth MAP + * Profile. + *@hide + */ +public class BluetoothMap { + + private static final String TAG = "BluetoothMap"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** int extra for MAP_STATE_CHANGED_ACTION */ + public static final String MAP_STATE = + "android.bluetooth.map.intent.MAP_STATE"; + /** int extra for MAP_STATE_CHANGED_ACTION */ + public static final String MAP_PREVIOUS_STATE = + "android.bluetooth.map.intent.MAP_PREVIOUS_STATE"; + + /** Indicates the state of a Map connection state has changed. + * This intent will always contain MAP_STATE, MAP_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. + */ + public static final String MAP_STATE_CHANGED_ACTION = + "android.bluetooth.map.intent.action.MAP_STATE_CHANGED"; + + private IBluetoothMap 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; + /** No client currently connected */ + public static final int STATE_DISCONNECTED = 0; + /** Connection attempt in progress */ + public static final int STATE_CONNECTING = 1; + /** Client is currently connected */ + public static final int STATE_CONNECTED = 2; + + 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; + + /** + * An interface for notifying Bluetooth PCE IPC clients when they have + * been connected to the BluetoothMap service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothMap service. Clients must wait for + * this callback before making IPC calls on the BluetoothMap + * service. + */ + public void onServiceConnected(BluetoothMap proxy); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothMap service. Clients must not + * make IPC calls on the BluetoothMap service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothMap service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + 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..."); + if (!mContext.bindService( + new Intent(IBluetoothMap.class.getName()), + mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth MAP Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + + /** + * Create a BluetoothMap proxy object. + */ + public BluetoothMap(Context context, ServiceListener l) { + 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); + } + } + if (!context.bindService(new Intent(IBluetoothMap.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Map Service"); + } + } + + 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 synchronized 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); + mConnection = null; + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + mServiceListener = null; + } + + /** + * Get the current state of the BluetoothMap service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Map service. + */ + public int getState() { + if (VDBG) log("getState()"); + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return BluetoothMap.STATE_ERROR; + } + + /** + * Get the currently connected remote Bluetooth device (PCE). + * @return The remote Bluetooth device, or null if not in connected or + * connecting state, or if this proxy object is not connected to + * the Map service. + */ + public BluetoothDevice getClient() { + if (VDBG) log("getClient()"); + if (mService != null) { + try { + return mService.getClient(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return null; + } + + /** + * Returns true if the specified Bluetooth device is connected (does not + * include connecting). 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("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(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Disconnects the current Map Client. Currently this call blocks, + * it may soon be made asynchronous. Returns false if this proxy object is + * not currently connected to the Map service. + */ + public boolean disconnect() { + if (DBG) log("disconnect()"); + if (mService != null) { + try { + mService.disconnect(); + return true; + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Check class bits for possible Map support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might support Map. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might support Map. + */ + public static boolean doesClassMatchSink(BluetoothClass btClass) { + // TODO optimize the rule + switch (btClass.getDeviceClass()) { + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return true; + default: + return false; + } + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) log("Proxy object connected"); + mService = IBluetoothMap.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothMap.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) log("Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 43079f44f5b..15740908b0a 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -97,6 +97,12 @@ public interface BluetoothProfile { */ static public final int GATT_SERVER = 8; + /** + * MAP Profile + * @hide + */ + public static final int MAP = 9; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 59622351814..fe66fbddb78 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -64,10 +64,14 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid MAP = + ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid MNS = + ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP}; + ObexObjectPush, PANU, NAP, MAP, MNS}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -112,6 +116,13 @@ public final class BluetoothUuid { public static boolean isBnep(ParcelUuid uuid) { return uuid.equals(BNEP); } + public static boolean isMap(ParcelUuid uuid) { + return uuid.equals(MAP); + } + public static boolean isMns(ParcelUuid uuid) { + return uuid.equals(MNS); + } + /** * Returns true if ParcelUuid is present in uuidArray * diff --git a/framework/java/android/bluetooth/IBluetoothMap.aidl b/framework/java/android/bluetooth/IBluetoothMap.aidl new file mode 100644 index 00000000000..0c18e06a788 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothMap.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 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.bluetooth.BluetoothDevice; + +/** + * System private API for Bluetooth MAP service + * + * {@hide} + */ +interface IBluetoothMap { + int getState(); + BluetoothDevice getClient(); + boolean connect(in BluetoothDevice device); + void disconnect(); + boolean isConnected(in BluetoothDevice device); +} -- GitLab From 93aa2e43a01a8d908ad34c4de6bfcd037e4fbfee Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 8 Jul 2013 23:58:16 -0700 Subject: [PATCH 0284/1408] LE: Add instance ID to descriptors (1/4) If a remote devices offers multiple descriptors with the same UUID, the instance ID is used to differentiate between them. Change-Id: I0c36494c980c86abd23f9647196af8d59ef663e9 --- .../android/bluetooth/BluetoothAdapter.java | 6 ++-- .../java/android/bluetooth/BluetoothGatt.java | 36 +++++++++---------- .../BluetoothGattCharacteristic.java | 14 ++++++++ .../bluetooth/BluetoothGattDescriptor.java | 30 +++++++++++++--- .../bluetooth/BluetoothGattService.java | 4 +-- .../android/bluetooth/IBluetoothGatt.aidl | 7 ++-- .../bluetooth/IBluetoothGattCallback.aidl | 7 ++-- 7 files changed, 71 insertions(+), 33 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1ea13e10261..74d85c30800 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1704,7 +1704,7 @@ public final class BluetoothAdapter { public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descUuid) { + int descInstId, ParcelUuid descUuid) { // no op } @@ -1734,14 +1734,14 @@ public final class BluetoothAdapter { public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid, byte[] value) { + int descInstId, ParcelUuid descrUuid, byte[] value) { // no op } public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid) { + int descInstId, ParcelUuid descrUuid) { // no op } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index df3ec1afee8..1ad7bf27ddb 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -261,7 +261,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descUuid) { + int descrInstId, ParcelUuid descUuid) { if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); if (!address.equals(mDevice.getAddress())) { @@ -276,7 +276,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (characteristic == null) return; characteristic.addDescriptor(new BluetoothGattDescriptor( - characteristic, descUuid.getUuid(), 0)); + characteristic, descUuid.getUuid(), descrInstId, 0)); } /** @@ -429,7 +429,8 @@ public final class BluetoothGatt implements BluetoothProfile { public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid, byte[] value) { + int descrInstId, ParcelUuid descrUuid, + byte[] value) { if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { @@ -444,7 +445,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (characteristic == null) return; BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid()); + descrUuid.getUuid(), descrInstId); if (descriptor == null) return; if (status == 0) descriptor.setValue(value); @@ -456,7 +457,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = true; mService.readDescriptor(mClientIf, address, srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrUuid, AUTHENTICATION_MITM); + descrInstId, descrUuid, AUTHENTICATION_MITM); } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -478,7 +479,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid) { + int descrInstId, ParcelUuid descrUuid) { if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { @@ -493,7 +494,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (characteristic == null) return; BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid()); + descrUuid.getUuid(), descrInstId); if (descriptor == null) return; if ((status == GATT_INSUFFICIENT_AUTHENTICATION @@ -503,7 +504,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = true; mService.writeDescriptor(mClientIf, address, srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrUuid, characteristic.getWriteType(), + descrInstId, descrUuid, characteristic.getWriteType(), AUTHENTICATION_MITM, descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); @@ -915,11 +916,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (device == null) return false; try { - mService.readDescriptor(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE); + mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), + service.getInstanceId(), new ParcelUuid(service.getUuid()), + characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), + descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), + AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -953,11 +954,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (device == null) return false; try { - mService.writeDescriptor(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - new ParcelUuid(descriptor.getUuid()), + mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), + service.getInstanceId(), new ParcelUuid(service.getUuid()), + characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), + descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 033f0798165..f0ecbb4b504 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -282,6 +282,20 @@ public class BluetoothGattCharacteristic { return true; } + /** + * Get a descriptor by UUID and isntance id. + * @hide + */ + /*package*/ BluetoothGattDescriptor getDescriptor(UUID uuid, int instanceId) { + for(BluetoothGattDescriptor descriptor : mDescriptors) { + if (descriptor.getUuid().equals(uuid) + && descriptor.getInstanceId() == instanceId) { + return descriptor; + } + } + return null; + } + /** * Returns the service this characteristic belongs to. * @return The asscociated service diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 1cd68787ce0..5f525dc609a 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -90,6 +90,12 @@ public class BluetoothGattDescriptor { */ protected UUID mUuid; + /** + * Instance ID for this descriptor. + * @hide + */ + protected int mInstance; + /** * Permissions for this descriptor * @hide @@ -116,7 +122,7 @@ public class BluetoothGattDescriptor { * @param permissions Permissions for this descriptor */ public BluetoothGattDescriptor(UUID uuid, int permissions) { - initDescriptor(null, uuid, permissions); + initDescriptor(null, uuid, 0, permissions); } /** @@ -128,14 +134,15 @@ public class BluetoothGattDescriptor { * @param permissions Permissions for this descriptor */ /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int permissions) { - initDescriptor(characteristic, uuid, permissions); + int instance, int permissions) { + initDescriptor(characteristic, uuid, instance, permissions); } private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int permissions) { + int instance, int permissions) { mCharacteristic = characteristic; mUuid = uuid; + mInstance = instance; mPermissions = permissions; } @@ -164,6 +171,21 @@ public class BluetoothGattDescriptor { return mUuid; } + /** + * Returns the instance ID for this descriptor. + * + *

        If a remote device offers multiple descriptors with the same UUID, + * the instance ID is used to distuinguish between descriptors. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return Instance ID of this descriptor + * @hide + */ + public int getInstanceId() { + return mInstance; + } + /** * Returns the permissions for this descriptor. * diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 39a435becb5..1e663696010 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -152,8 +152,8 @@ public class BluetoothGattService { */ /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) { for(BluetoothGattCharacteristic characteristic : mCharacteristics) { - if (uuid.equals(characteristic.getUuid()) && - mInstanceId == instanceId) + if (uuid.equals(characteristic.getUuid()) + && characteristic.getInstanceId() == instanceId) return characteristic; } return null; diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index c89d132df7d..b58b8471691 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -50,12 +50,13 @@ interface IBluetoothGatt { void readDescriptor(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId, in int charInstanceId, in ParcelUuid charId, - in ParcelUuid descrUuid, in int authReq); + in int descrInstanceId, in ParcelUuid descrUuid, + in int authReq); void writeDescriptor(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId, in int charInstanceId, in ParcelUuid charId, - in ParcelUuid descrId, in int writeType, - in int authReq, in byte[] value); + in int descrInstanceId, in ParcelUuid descrId, + in int writeType, in int authReq, in byte[] value); void registerForNotification(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId, in int charInstanceId, in ParcelUuid charId, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index fc521726c6a..e3563fcc3a7 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -39,7 +39,7 @@ interface IBluetoothGattCallback { void onGetDescriptor(in String address, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, - in ParcelUuid descrUuid); + in int descrInstId, in ParcelUuid descrUuid); void onSearchComplete(in String address, in int status); void onCharacteristicRead(in String address, in int status, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, @@ -52,11 +52,12 @@ interface IBluetoothGattCallback { void onDescriptorRead(in String address, in int status, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, - in ParcelUuid descrUuid, in byte[] value); + in int descrInstId, in ParcelUuid descrUuid, + in byte[] value); void onDescriptorWrite(in String address, in int status, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, - in ParcelUuid descrUuid); + in int descrInstId, in ParcelUuid descrUuid); void onNotify(in String address, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, -- GitLab From 3b486eb57a332036e9c779020e1fc5921658b0a5 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 6 Aug 2013 19:57:48 -0700 Subject: [PATCH 0285/1408] LE: Add peripheral role support (1/4) Initial stack support for the LE peripheral role. Change-Id: I6222493488822b4289b90888ccc97ad9306f54d1 --- .../android/bluetooth/BluetoothAdapter.java | 4 ++ .../java/android/bluetooth/BluetoothGatt.java | 65 +++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 4 ++ .../bluetooth/IBluetoothGattCallback.aidl | 1 + 4 files changed, 74 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 74d85c30800..e062fa8b4eb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1752,6 +1752,10 @@ public final class BluetoothAdapter { public void onReadRemoteRssi(String address, int rssi, int status) { // no op } + + public void onListen(int status) { + // no op + } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1ad7bf27ddb..a8c310bd3e2 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -553,6 +553,14 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * Listen command status callback + * @hide + */ + public void onListen(int status) { + if (DBG) Log.d(TAG, "onListen() - status=" + status); + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { @@ -685,6 +693,63 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Starts or stops sending of advertisement packages to listen for connection + * requests from a central devices. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param start Start or stop advertising + */ + /*package*/ void listen(boolean start) { + if (DBG) Log.d(TAG, "listen() - start: " + start); + if (mService == null || mClientIf == 0) return; + + try { + mService.clientListen(mClientIf, start); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Sets the advertising data contained in the adv. response packet. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param advData true to set adv. data, false to set scan response + * @param includeName Inlucde the name in the adv. response + * @param includeTxPower Include TX power value + * @param minInterval Minimum desired scan interval (optional) + * @param maxInterval Maximum desired scan interval (optional) + * @param appearance The appearance flags for the device (optional) + * @param manufacturerData Manufacturer specific data including company ID (optional) + */ + /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, + Integer minInterval, Integer maxInterval, + Integer appearance, Byte[] manufacturerData) { + if (DBG) Log.d(TAG, "setAdvData()"); + if (mService == null || mClientIf == 0) return; + + byte[] data = new byte[0]; + if (manufacturerData != null) { + data = new byte[manufacturerData.length]; + for(int i = 0; i != manufacturerData.length; ++i) { + data[i] = manufacturerData[i]; + } + } + + try { + mService.setAdvData(mClientIf, !advData, + includeName, includeTxPower, + minInterval != null ? minInterval : 0, + maxInterval != null ? maxInterval : 0, + appearance != null ? appearance : 0, data); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index b58b8471691..df393dbb289 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,6 +37,10 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); + void clientListen(in int clientIf, in boolean start); + void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, + in boolean inclTxPower, in int minInterval, in int maxInterval, + in int appearance, in byte[] manufacturerData); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index e3563fcc3a7..60c297b5979 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,4 +63,5 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); + void onListen(in int status); } -- GitLab From 1f0c3da65d7ca3f7fda80fa1453ee60462068a04 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Thu, 7 Mar 2013 18:07:35 -0800 Subject: [PATCH 0286/1408] LE: Add support for the HID-over-GATT profile (2/3) bug:8330048 Change-Id: I600563d81dec1638dc35e31e19f61d6c04f09ae8 --- framework/java/android/bluetooth/BluetoothUuid.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index fe66fbddb78..6609b988b25 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -56,6 +56,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid Hid = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid Hogp = + ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid NAP = -- GitLab From 4487d4a77a91838e8c0f2efd4c32731cc3b60fff Mon Sep 17 00:00:00 2001 From: John Du Date: Fri, 19 Jul 2013 11:30:34 -0700 Subject: [PATCH 0287/1408] Adding support for Absolute Volume Change-Id: I7bbc6f9296221ca219a50a5e377ebac9dcf5a407 --- .../java/android/bluetooth/BluetoothA2dp.java | 60 +++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 3 + 2 files changed, 63 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d7d8cdbe439..e7e4a0f47fd 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -387,6 +387,66 @@ public final class BluetoothA2dp implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } + /** + * Checks if Avrcp device supports the absolute volume feature. + * + * @return true if device supports absolute volume + * @hide + */ + public boolean isAvrcpAbsoluteVolumeSupported() { + if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); + if (mService != null && isEnabled()) { + try { + return mService.isAvrcpAbsoluteVolumeSupported(); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Tells remote device to adjust volume. Only if absolute volume is supported. + * + * @param direction 1 to increase volume, or -1 to decrease volume + * @hide + */ + public void adjustAvrcpAbsoluteVolume(int direction) { + if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); + if (mService != null && isEnabled()) { + try { + mService.adjustAvrcpAbsoluteVolume(direction); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + /** + * Tells remote device to set an absolute volume. Only if absolute volume is supported + * + * @param volume Absolute volume to be set on AVRCP side + * @hide + */ + public void setAvrcpAbsoluteVolume(int volume) { + if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); + if (mService != null && isEnabled()) { + try { + mService.setAvrcpAbsoluteVolume(volume); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + /** * Check if A2DP profile is streaming music. * diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 1f1099868e6..26ff9e274c3 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -32,5 +32,8 @@ interface IBluetoothA2dp { int getConnectionState(in BluetoothDevice device); boolean setPriority(in BluetoothDevice device, int priority); int getPriority(in BluetoothDevice device); + boolean isAvrcpAbsoluteVolumeSupported(); + oneway void adjustAvrcpAbsoluteVolume(int direction); + oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); } -- GitLab From 013885408eefaddfa4feb0ff710cc537438a6f9b Mon Sep 17 00:00:00 2001 From: John Du Date: Mon, 19 Aug 2013 12:20:37 -0700 Subject: [PATCH 0288/1408] Change abortReliableWrite(BluetoothDevice) to abortReliableWrite() BluetoothGatt.abortReliableWrite() should not take a BluetoothDevice parameter. Instead, it should use mDevice instance variable. bug 10152994 Change-Id: I7fc79b9011cf878414128cc9f1696e5ccc597056 --- framework/java/android/bluetooth/BluetoothGatt.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a8c310bd3e2..86224e0306e 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1102,7 +1102,7 @@ public final class BluetoothGatt implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ - public void abortReliableWrite(BluetoothDevice mDevice) { + public void abortReliableWrite() { if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; @@ -1113,6 +1113,13 @@ public final class BluetoothGatt implements BluetoothProfile { } } + /** + * @deprecated Use {@link abortReliableWrite()} + */ + public void abortReliableWrite(BluetoothDevice mDevice) { + abortReliableWrite(); + } + /** * Enable or disable notifications/indications for a given characteristic. * -- GitLab From 709a574f2db61357a990d88859cd0b9fa6569137 Mon Sep 17 00:00:00 2001 From: John Du Date: Tue, 20 Aug 2013 14:03:28 -0700 Subject: [PATCH 0289/1408] Update comments bug 10152994 Change-Id: I05bbd917ebd0d140e0894f8bf7e43d0fc77378c8 --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 86224e0306e..b390aa12b95 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1114,7 +1114,7 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * @deprecated Use {@link abortReliableWrite()} + * @deprecated Use {@link #abortReliableWrite()} */ public void abortReliableWrite(BluetoothDevice mDevice) { abortReliableWrite(); -- GitLab From ac1d41c6ab24669f63bbbc4ae86971e52ef68e9b Mon Sep 17 00:00:00 2001 From: Vinit Deshapnde Date: Wed, 21 Aug 2013 13:09:01 -0700 Subject: [PATCH 0290/1408] Introduce network link quality statistics This change starts tracking traffic quality data for WiFi and mobile networks. The quality is tracked based on incidental traffic, and not on specific measurements. Theoretical bandwidths are hard-coded, as well as sampling interval; although sampling interval can be changed by setting a system policy. Bugs filed to remove shortcomings of this change - 10342372 Change LinkInfo name to something better 10342318 Move hardcoded values of MobileLinkInfo to resources so they can be updated without changing code Bug: 10006249 Change-Id: I83d8c7594da20fe53abbd5e1f909b1f606b035bb --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 0aedecb82dd..a9b71769335 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.net.BaseNetworkStateTracker; import android.os.IBinder; import android.os.ServiceManager; import android.os.INetworkManagementService; @@ -54,7 +55,7 @@ import java.util.concurrent.atomic.AtomicReference; * * @hide */ -public class BluetoothTetheringDataTracker implements NetworkStateTracker { +public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; private static final boolean DBG = true; @@ -66,18 +67,12 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); private final Object mLinkPropertiesLock = new Object(); - private LinkProperties mLinkProperties; - - private LinkCapabilities mLinkCapabilities; - private final Object mNetworkInfoLock = new Object(); - private NetworkInfo mNetworkInfo; private BluetoothPan mBluetoothPan; private static String mRevTetheredIface; /* For sending events to connectivity service handler */ private Handler mCsHandler; - protected Context mContext; private static BluetoothTetheringDataTracker sInstance; private BtdtHandler mBtdtHandler; private AtomicReference mAsyncChannel = new AtomicReference(null); -- GitLab From 7c81f1f98538abf57284e757870d25f2ca7e30c4 Mon Sep 17 00:00:00 2001 From: Edward Jee Date: Fri, 16 Aug 2013 04:07:49 -0700 Subject: [PATCH 0291/1408] Enables AT+ANDROID command in Bluetooth HFP. Adds another vendor-specific command AT+ANDROID in Bluetooth Hands-Free Profile. And creates an API function that sends an unsolicited result code +ANDROID . Bug: 8736189 Change-Id: I4757c410d31b97511c92f66d71035eb929f9fd29 --- .../bluetooth/BluetoothAssignedNumbers.java | 650 ++++++++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 45 ++ .../android/bluetooth/IBluetoothHeadset.aidl | 3 + 3 files changed, 698 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java index 580e9ff56f4..124bdc118d4 100644 --- a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java +++ b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java @@ -512,6 +512,656 @@ public class BluetoothAssignedNumbers { */ public static final int RIVIERAWAVES = 0x0060; + /* + * RDA Microelectronics. + */ + public static final int RDA_MICROELECTRONICS = 0x0061; + + /* + * Gibson Guitars. + */ + public static final int GIBSON_GUITARS = 0x0062; + + /* + * MiCommand Inc. + */ + public static final int MICOMMAND = 0x0063; + + /* + * Band XI International, LLC. + */ + public static final int BAND_XI_INTERNATIONAL = 0x0064; + + /* + * Hewlett-Packard Company. + */ + public static final int HEWLETT_PACKARD = 0x0065; + + /* + * 9Solutions Oy. + */ + public static final int NINE_SOLUTIONS = 0x0066; + + /* + * GN Netcom A/S. + */ + public static final int GN_NETCOM = 0x0067; + + /* + * General Motors. + */ + public static final int GENERAL_MOTORS = 0x0068; + + /* + * A&D Engineering, Inc. + */ + public static final int A_AND_D_ENGINEERING = 0x0069; + + /* + * MindTree Ltd. + */ + public static final int MINDTREE = 0x006A; + + /* + * Polar Electro OY. + */ + public static final int POLAR_ELECTRO = 0x006B; + + /* + * Beautiful Enterprise Co., Ltd. + */ + public static final int BEAUTIFUL_ENTERPRISE = 0x006C; + + /* + * BriarTek, Inc. + */ + public static final int BRIARTEK = 0x006D; + + /* + * Summit Data Communications, Inc. + */ + public static final int SUMMIT_DATA_COMMUNICATIONS = 0x006E; + + /* + * Sound ID. + */ + public static final int SOUND_ID = 0x006F; + + /* + * Monster, LLC. + */ + public static final int MONSTER = 0x0070; + + /* + * connectBlue AB. + */ + public static final int CONNECTBLUE = 0x0071; + + /* + * ShangHai Super Smart Electronics Co. Ltd. + */ + public static final int SHANGHAI_SUPER_SMART_ELECTRONICS = 0x0072; + + /* + * Group Sense Ltd. + */ + public static final int GROUP_SENSE = 0x0073; + + /* + * Zomm, LLC. + */ + public static final int ZOMM = 0x0074; + + /* + * Samsung Electronics Co. Ltd. + */ + public static final int SAMSUNG_ELECTRONICS = 0x0075; + + /* + * Creative Technology Ltd. + */ + public static final int CREATIVE_TECHNOLOGY = 0x0076; + + /* + * Laird Technologies. + */ + public static final int LAIRD_TECHNOLOGIES = 0x0077; + + /* + * Nike, Inc. + */ + public static final int NIKE = 0x0078; + + /* + * lesswire AG. + */ + public static final int LESSWIRE = 0x0079; + + /* + * MStar Semiconductor, Inc. + */ + public static final int MSTAR_SEMICONDUCTOR = 0x007A; + + /* + * Hanlynn Technologies. + */ + public static final int HANLYNN_TECHNOLOGIES = 0x007B; + + /* + * A & R Cambridge. + */ + public static final int A_AND_R_CAMBRIDGE = 0x007C; + + /* + * Seers Technology Co. Ltd. + */ + public static final int SEERS_TECHNOLOGY = 0x007D; + + /* + * Sports Tracking Technologies Ltd. + */ + public static final int SPORTS_TRACKING_TECHNOLOGIES = 0x007E; + + /* + * Autonet Mobile. + */ + public static final int AUTONET_MOBILE = 0x007F; + + /* + * DeLorme Publishing Company, Inc. + */ + public static final int DELORME_PUBLISHING_COMPANY = 0x0080; + + /* + * WuXi Vimicro. + */ + public static final int WUXI_VIMICRO = 0x0081; + + /* + * Sennheiser Communications A/S. + */ + public static final int SENNHEISER_COMMUNICATIONS = 0x0082; + + /* + * TimeKeeping Systems, Inc. + */ + public static final int TIMEKEEPING_SYSTEMS = 0x0083; + + /* + * Ludus Helsinki Ltd. + */ + public static final int LUDUS_HELSINKI = 0x0084; + + /* + * BlueRadios, Inc. + */ + public static final int BLUERADIOS = 0x0085; + + /* + * equinox AG. + */ + public static final int EQUINOX_AG = 0x0086; + + /* + * Garmin International, Inc. + */ + public static final int GARMIN_INTERNATIONAL = 0x0087; + + /* + * Ecotest. + */ + public static final int ECOTEST = 0x0088; + + /* + * GN ReSound A/S. + */ + public static final int GN_RESOUND = 0x0089; + + /* + * Jawbone. + */ + public static final int JAWBONE = 0x008A; + + /* + * Topcorn Positioning Systems, LLC. + */ + public static final int TOPCORN_POSITIONING_SYSTEMS = 0x008B; + + /* + * Qualcomm Labs, Inc. + */ + public static final int QUALCOMM_LABS = 0x008C; + + /* + * Zscan Software. + */ + public static final int ZSCAN_SOFTWARE = 0x008D; + + /* + * Quintic Corp. + */ + public static final int QUINTIC = 0x008E; + + /* + * Stollman E+V GmbH. + */ + public static final int STOLLMAN_E_PLUS_V = 0x008F; + + /* + * Funai Electric Co., Ltd. + */ + public static final int FUNAI_ELECTRIC = 0x0090; + + /* + * Advanced PANMOBIL Systems GmbH & Co. KG. + */ + public static final int ADVANCED_PANMOBIL_SYSTEMS = 0x0091; + + /* + * ThinkOptics, Inc. + */ + public static final int THINKOPTICS = 0x0092; + + /* + * Universal Electronics, Inc. + */ + public static final int UNIVERSAL_ELECTRONICS = 0x0093; + + /* + * Airoha Technology Corp. + */ + public static final int AIROHA_TECHNOLOGY = 0x0094; + + /* + * NEC Lighting, Ltd. + */ + public static final int NEC_LIGHTING = 0x0095; + + /* + * ODM Technology, Inc. + */ + public static final int ODM_TECHNOLOGY = 0x0096; + + /* + * Bluetrek Technologies Limited. + */ + public static final int BLUETREK_TECHNOLOGIES = 0x0097; + + /* + * zer01.tv GmbH. + */ + public static final int ZER01_TV = 0x0098; + + /* + * i.Tech Dynamic Global Distribution Ltd. + */ + public static final int I_TECH_DYNAMIC_GLOBAL_DISTRIBUTION = 0x0099; + + /* + * Alpwise. + */ + public static final int ALPWISE = 0x009A; + + /* + * Jiangsu Toppower Automotive Electronics Co., Ltd. + */ + public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS = 0x009B; + + /* + * Colorfy, Inc. + */ + public static final int COLORFY = 0x009C; + + /* + * Geoforce Inc. + */ + public static final int GEOFORCE = 0x009D; + + /* + * Bose Corporation. + */ + public static final int BOSE = 0x009E; + + /* + * Suunto Oy. + */ + public static final int SUUNTO = 0x009F; + + /* + * Kensington Computer Products Group. + */ + public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0; + + /* + * SR-Medizinelektronik. + */ + public static final int SR_MEDIZINELEKTRONIK = 0x00A1; + + /* + * Vertu Corporation Limited. + */ + public static final int VERTU = 0x00A2; + + /* + * Meta Watch Ltd. + */ + public static final int META_WATCH = 0x00A3; + + /* + * LINAK A/S. + */ + public static final int LINAK = 0x00A4; + + /* + * OTL Dynamics LLC. + */ + public static final int OTL_DYNAMICS = 0x00A5; + + /* + * Panda Ocean Inc. + */ + public static final int PANDA_OCEAN = 0x00A6; + + /* + * Visteon Corporation. + */ + public static final int VISTEON = 0x00A7; + + /* + * ARP Devices Limited. + */ + public static final int ARP_DEVICES = 0x00A8; + + /* + * Magneti Marelli S.p.A. + */ + public static final int MAGNETI_MARELLI = 0x00A9; + + /* + * CAEN RFID srl. + */ + public static final int CAEN_RFID = 0x00AA; + + /* + * Ingenieur-Systemgruppe Zahn GmbH. + */ + public static final int INGENIEUR_SYSTEMGRUPPE_ZAHN = 0x00AB; + + /* + * Green Throttle Games. + */ + public static final int GREEN_THROTTLE_GAMES = 0x00AC; + + /* + * Peter Systemtechnik GmbH. + */ + public static final int PETER_SYSTEMTECHNIK = 0x00AD; + + /* + * Omegawave Oy. + */ + public static final int OMEGAWAVE = 0x00AE; + + /* + * Cinetix. + */ + public static final int CINETIX = 0x00AF; + + /* + * Passif Semiconductor Corp. + */ + public static final int PASSIF_SEMICONDUCTOR = 0x00B0; + + /* + * Saris Cycling Group, Inc. + */ + public static final int SARIS_CYCLING_GROUP = 0x00B1; + + /* + * Bekey A/S. + */ + public static final int BEKEY = 0x00B2; + + /* + * Clarinox Technologies Pty. Ltd. + */ + public static final int CLARINOX_TECHNOLOGIES = 0x00B3; + + /* + * BDE Technology Co., Ltd. + */ + public static final int BDE_TECHNOLOGY = 0x00B4; + + /* + * Swirl Networks. + */ + public static final int SWIRL_NETWORKS = 0x00B5; + + /* + * Meso international. + */ + public static final int MESO_INTERNATIONAL = 0x00B6; + + /* + * TreLab Ltd. + */ + public static final int TRELAB = 0x00B7; + + /* + * Qualcomm Innovation Center, Inc. (QuIC). + */ + public static final int QUALCOMM_INNOVATION_CENTER = 0x00B8; + + /* + * Johnson Controls, Inc. + */ + public static final int JOHNSON_CONTROLS = 0x00B9; + + /* + * Starkey Laboratories Inc. + */ + public static final int STARKEY_LABORATORIES = 0x00BA; + + /* + * S-Power Electronics Limited. + */ + public static final int S_POWER_ELECTRONICS = 0x00BB; + + /* + * Ace Sensor Inc. + */ + public static final int ACE_SENSOR = 0x00BC; + + /* + * Aplix Corporation. + */ + public static final int APLIX = 0x00BD; + + /* + * AAMP of America. + */ + public static final int AAMP_OF_AMERICA = 0x00BE; + + /* + * Stalmart Technology Limited. + */ + public static final int STALMART_TECHNOLOGY = 0x00BF; + + /* + * AMICCOM Electronics Corporation. + */ + public static final int AMICCOM_ELECTRONICS = 0x00C0; + + /* + * Shenzhen Excelsecu Data Technology Co.,Ltd. + */ + public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY = 0x00C1; + + /* + * Geneq Inc. + */ + public static final int GENEQ = 0x00C2; + + /* + * adidas AG. + */ + public static final int ADIDAS = 0x00C3; + + /* + * LG Electronics. + */ + public static final int LG_ELECTRONICS = 0x00C4; + + /* + * Onset Computer Corporation. + */ + public static final int ONSET_COMPUTER = 0x00C5; + + /* + * Selfly BV. + */ + public static final int SELFLY = 0x00C6; + + /* + * Quuppa Oy. + */ + public static final int QUUPPA = 0x00C7; + + /* + * GeLo Inc. + */ + public static final int GELO = 0x00C8; + + /* + * Evluma. + */ + public static final int EVLUMA = 0x00C9; + + /* + * MC10. + */ + public static final int MC10 = 0x00CA; + + /* + * Binauric SE. + */ + public static final int BINAURIC = 0x00CB; + + /* + * Beats Electronics. + */ + public static final int BEATS_ELECTRONICS = 0x00CC; + + /* + * Microchip Technology Inc. + */ + public static final int MICROCHIP_TECHNOLOGY = 0x00CD; + + /* + * Elgato Systems GmbH. + */ + public static final int ELGATO_SYSTEMS = 0x00CE; + + /* + * ARCHOS SA. + */ + public static final int ARCHOS = 0x00CF; + + /* + * Dexcom, Inc. + */ + public static final int DEXCOM = 0x00D0; + + /* + * Polar Electro Europe B.V. + */ + public static final int POLAR_ELECTRO_EUROPE = 0x00D1; + + /* + * Dialog Semiconductor B.V. + */ + public static final int DIALOG_SEMICONDUCTOR = 0x00D2; + + /* + * Taixingbang Technology (HK) Co,. LTD. + */ + public static final int TAIXINGBANG_TECHNOLOGY = 0x00D3; + + /* + * Kawantech. + */ + public static final int KAWANTECH = 0x00D4; + + /* + * Austco Communication Systems. + */ + public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5; + + /* + * Timex Group USA, Inc. + */ + public static final int TIMEX_GROUP_USA = 0x00D6; + + /* + * Qualcomm Technologies, Inc. + */ + public static final int QUALCOMM_TECHNOLOGIES = 0x00D7; + + /* + * Qualcomm Connected Experiences, Inc. + */ + public static final int QUALCOMM_CONNECTED_EXPERIENCES = 0x00D8; + + /* + * Voyetra Turtle Beach. + */ + public static final int VOYETRA_TURTLE_BEACH = 0x00D9; + + /* + * txtr GmbH. + */ + public static final int TXTR = 0x00DA; + + /* + * Biosentronics. + */ + public static final int BIOSENTRONICS = 0x00DB; + + /* + * Procter & Gamble. + */ + public static final int PROCTER_AND_GAMBLE = 0x00DC; + + /* + * Hosiden Corporation. + */ + public static final int HOSIDEN = 0x00DD; + + /* + * Muzik LLC. + */ + public static final int MUZIK = 0x00DE; + + /* + * Misfit Wearables Corp. + */ + public static final int MISFIT_WEARABLES = 0x00DF; + + /* + * Google. + */ + public static final int GOOGLE = 0x00E0; + + /* + * Danlers Ltd. + */ + public static final int DANLERS = 0x00E1; + + /* + * Semilink Inc. + */ + public static final int SEMILINK = 0x00E2; + /* * You can't instantiate one of these. */ diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 5a5764d1d0a..49a1fb33987 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -192,6 +192,11 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid"; + /** + * A vendor-specific command for unsolicited result code. + */ + public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** * Headset state when SCO audio is not connected. * This state can be one of @@ -840,6 +845,46 @@ public final class BluetoothHeadset implements BluetoothProfile { } } + /** + * Sends a vendor-specific unsolicited result code to the headset. + * + *

        The actual string to be sent is command + ": " + arg. + * For example, if {@code command} is {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} + * is {@code "0"}, the string "+ANDROID: 0" will be sent. + * + *

        Currently only {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth headset. + * @param command A vendor-specific command. + * @param arg The argument that will be attached to the command. + * @return {@code false} if there is no headset connected, or if the command is not an allowed + * vendor-specific unsolicited result code, or on error. {@code true} otherwise. + * @throws IllegalArgumentException if {@code command} is {@code null}. + */ + public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, + String arg) { + if (DBG) { + log("sendVendorSpecificResultCode()"); + } + if (command == null) { + throw new IllegalArgumentException("command is null"); + } + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.sendVendorSpecificResultCode(device, command, arg); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 285eea7546f..524ca6f77c7 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -35,6 +35,9 @@ interface IBluetoothHeadset { boolean startVoiceRecognition(in BluetoothDevice device); boolean stopVoiceRecognition(in BluetoothDevice device); boolean isAudioConnected(in BluetoothDevice device); + boolean sendVendorSpecificResultCode(in BluetoothDevice device, + in String command, + in String arg); // APIs that can be made public in future int getBatteryUsageHint(in BluetoothDevice device); -- GitLab From 29d93890e0d8b374555523193a2cba060506eb33 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Mon, 26 Aug 2013 17:48:22 -0700 Subject: [PATCH 0292/1408] Fix docs build. Change-Id: I7bfcfea052de6959230bb361400e760eab35b78c --- framework/java/android/bluetooth/BluetoothHeadset.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 49a1fb33987..19625145112 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -849,10 +849,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * Sends a vendor-specific unsolicited result code to the headset. * *

        The actual string to be sent is command + ": " + arg. - * For example, if {@code command} is {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} + * For example, if {@code command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} * is {@code "0"}, the string "+ANDROID: 0" will be sent. * - *

        Currently only {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. + *

        Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * -- GitLab From 7d413916274aa6ef1bff09c243b017e3b6c01b48 Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Tue, 27 Aug 2013 12:39:40 +0100 Subject: [PATCH 0293/1408] Fix the build. Use correct javadoc in BluetoothHeadset.java. Change-Id: I17b4cf570036ebf426252229fe9ef377ced9b67b --- framework/java/android/bluetooth/BluetoothHeadset.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 49a1fb33987..19625145112 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -849,10 +849,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * Sends a vendor-specific unsolicited result code to the headset. * *

        The actual string to be sent is command + ": " + arg. - * For example, if {@code command} is {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} + * For example, if {@code command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} * is {@code "0"}, the string "+ANDROID: 0" will be sent. * - *

        Currently only {@link VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. + *

        Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * -- GitLab From 7380b8911784c5837316eb6be4d69d4a8b7a6c72 Mon Sep 17 00:00:00 2001 From: You Kim Date: Thu, 29 Aug 2013 03:07:12 +0900 Subject: [PATCH 0294/1408] Typo in method name: Clone Change-Id: I2aa8fc2797278216fbc23dd0d94feb1a9bf8a5d6 --- .../java/android/bluetooth/BluetoothTetheringDataTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 81c0a6a87ea..5e1a5cb1a83 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -96,7 +96,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return sInstance; } - public Object Clone() throws CloneNotSupportedException { + public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } -- GitLab From de269fc5e43a3ba2eacfba320930a73687651972 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Tue, 27 Aug 2013 22:42:29 -0700 Subject: [PATCH 0295/1408] Block ble adv api based on resource config bug 10516911 Change-Id: I7a62103a1116ffbf7c3c934c1794384c0e6fbdd3 --- framework/java/android/bluetooth/BluetoothGatt.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index b390aa12b95..a2bb78c4c15 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -702,6 +702,10 @@ public final class BluetoothGatt implements BluetoothProfile { * @param start Start or stop advertising */ /*package*/ void listen(boolean start) { + if (mContext == null || !mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { + throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); + } if (DBG) Log.d(TAG, "listen() - start: " + start); if (mService == null || mClientIf == 0) return; @@ -728,6 +732,10 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, Integer minInterval, Integer maxInterval, Integer appearance, Byte[] manufacturerData) { + if (mContext == null || !mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { + throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); + } if (DBG) Log.d(TAG, "setAdvData()"); if (mService == null || mClientIf == 0) return; -- GitLab From 008ce9e07b5e58c5ab31c5d73ec3d1ad1a6cf605 Mon Sep 17 00:00:00 2001 From: Dmitry Grinberg Date: Fri, 9 Aug 2013 15:22:59 -0700 Subject: [PATCH 0296/1408] Allow L2CAP sockets Change-Id: Icb498e6c0430789b6168bec3beb1d4650e4f1238 --- .../android/bluetooth/BluetoothAdapter.java | 36 +++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 27 ++++++++++++++ .../android/bluetooth/BluetoothSocket.java | 4 ++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e062fa8b4eb..676fd1f3291 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -923,6 +923,42 @@ public final class BluetoothAdapter { return BluetoothProfile.STATE_DISCONNECTED; } + /** + * Create a listening, L2CAP Bluetooth socket. + *

        A remote device connecting to this socket will optionally be + * authenticated and communication on this socket will optionally be + * encrypted. + *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * @param secure whether security and authentication are required + * @param fixedChannel whether we're looking for a PSM-based connection or a fixed channel + * @param channel L2CAP PSM or channel to use + * @return a listening L2CAP BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingL2CapOn(boolean secure, boolean fixedChannel, + int channel) throws IOException { + BluetoothServerSocket socket; + + if (fixedChannel) { + channel |= BluetoothSocket.PORT_MASK_FIXED_CHAN; + } + + socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_L2CAP, secure, secure, channel); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); + } + return socket; + } + /** * Create a listening, secure RFCOMM Bluetooth socket. *

        A remote device connecting to this socket will be authenticated and diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3acd9b00c05..2c85382fe13 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1144,6 +1144,33 @@ public final class BluetoothDevice implements Parcelable { return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } + + /** + * Construct a L2CAP socket ready to start an outgoing connection. + * Call #connect on the returned #BluetoothSocket to begin the connection. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param secure select whether security will be required + * @param fixedChannel select if this will be a "fixed channel" L2CAP connection + * or a PSM-based connection + * @param channel fixed channel or PSM to connect to + * @return a L2CAP BluetoothSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothSocket createL2CapSocket(boolean secure, boolean fixedChannel, int channel) + throws IOException { + + if (fixedChannel) { + channel |= BluetoothSocket.PORT_MASK_FIXED_CHAN; + } + + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, secure, secure, this, + channel, null); + } + + /** * Check that a pin is valid and convert to byte array. * diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..191bf67af34 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -103,6 +103,8 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; + /*package*/ static final int PORT_MASK_FIXED_CHAN = 1 << 16; + private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ private String mAddress; /* remote address */ @@ -115,7 +117,7 @@ public final class BluetoothSocket implements Closeable { private LocalSocket mSocket; private InputStream mSocketIS; private OutputStream mSocketOS; - private int mPort; /* RFCOMM channel or L2CAP psm */ + private int mPort; /* RFCOMM channel or L2CAP psm/channel */ private int mFd; private String mServiceName; private static int PROXY_CONNECTION_TIMEOUT = 5000; -- GitLab From 6816ee2b6d6820a727b59d78b6892473efd42f98 Mon Sep 17 00:00:00 2001 From: Kim Schulz Date: Thu, 22 Aug 2013 11:18:02 +0200 Subject: [PATCH 0297/1408] Fixed review comments - fixed review comments (internal+google) - corrected tabs/spaces - Add connection id header for obex client operations - added support for implementing ProfileService class Change-Id: Idab8b4fa54a0f31bec4ffa263a69a9850a07f858 Bug:10692365 --- .../android/bluetooth/BluetoothAdapter.java | 10 +- .../java/android/bluetooth/BluetoothMap.java | 244 +++++++++++++----- .../java/android/bluetooth/BluetoothUuid.java | 10 +- .../java/android/bluetooth/IBluetoothMap.aidl | 7 +- 4 files changed, 198 insertions(+), 73 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 676fd1f3291..2172a7b2069 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -27,7 +27,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; - import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -51,7 +50,7 @@ import java.util.UUID; * devices, and start a scan for Bluetooth LE devices. * *

        To get a {@link BluetoothAdapter} representing the local Bluetooth - * adapter, when running on JELLY_BEAN_MR1 and below, call the + * adapter, when running on JELLY_BEAN_MR1 and below, call the * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and * higher, retrieve it through * {@link android.content.Context#getSystemService} with @@ -1229,6 +1228,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; + } else if (profile == BluetoothProfile.MAP) { + BluetoothMap map = new BluetoothMap(context, listener); + return true; } else { return false; } @@ -1277,6 +1279,10 @@ public final class BluetoothAdapter { BluetoothGattServer gattServer = (BluetoothGattServer)proxy; gattServer.close(); break; + case BluetoothProfile.MAP: + BluetoothMap map = (BluetoothMap)proxy; + map.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 7de309fb831..fac8fd51848 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,6 +16,8 @@ package android.bluetooth; +import java.util.List; +import java.util.ArrayList; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,25 +32,14 @@ import android.util.Log; * Profile. *@hide */ -public class BluetoothMap { +public final class BluetoothMap implements BluetoothProfile { private static final String TAG = "BluetoothMap"; private static final boolean DBG = true; private static final boolean VDBG = false; - /** int extra for MAP_STATE_CHANGED_ACTION */ - public static final String MAP_STATE = - "android.bluetooth.map.intent.MAP_STATE"; - /** int extra for MAP_STATE_CHANGED_ACTION */ - public static final String MAP_PREVIOUS_STATE = - "android.bluetooth.map.intent.MAP_PREVIOUS_STATE"; - - /** Indicates the state of a Map connection state has changed. - * This intent will always contain MAP_STATE, MAP_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. - */ - public static final String MAP_STATE_CHANGED_ACTION = - "android.bluetooth.map.intent.action.MAP_STATE_CHANGED"; + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; private IBluetoothMap mService; private final Context mContext; @@ -57,41 +48,12 @@ public class BluetoothMap { /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; - /** No client currently connected */ - public static final int STATE_DISCONNECTED = 0; - /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; - /** Client is currently connected */ - public static final int STATE_CONNECTED = 2; 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; - /** - * An interface for notifying Bluetooth PCE IPC clients when they have - * been connected to the BluetoothMap service. - */ - public interface ServiceListener { - /** - * Called to notify the client when this proxy object has been - * connected to the BluetoothMap service. Clients must wait for - * this callback before making IPC calls on the BluetoothMap - * service. - */ - public void onServiceConnected(BluetoothMap proxy); - - /** - * Called to notify the client that this proxy object has been - * disconnected from the BluetoothMap service. Clients must not - * make IPC calls on the BluetoothMap service after this callback. - * This callback will currently only occur if the application hosting - * the BluetoothMap service, but may be called more often in future. - */ - public void onServiceDisconnected(); - } - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -111,11 +73,7 @@ public class BluetoothMap { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService( - new Intent(IBluetoothMap.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth MAP Service"); - } + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -128,7 +86,8 @@ public class BluetoothMap { /** * Create a BluetoothMap proxy object. */ - public BluetoothMap(Context context, ServiceListener l) { + /*package*/ BluetoothMap(Context context, ServiceListener l) { + if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -140,9 +99,18 @@ public class BluetoothMap { Log.e(TAG,"",e); } } - if (!context.bindService(new Intent(IBluetoothMap.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Map Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothMap.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); + return false; } + return true; } protected void finalize() throws Throwable { @@ -221,9 +189,9 @@ public class BluetoothMap { } /** - * Returns true if the specified Bluetooth device is connected (does not - * include connecting). Returns false if not connected, or if this proxy - * object is not currently connected to the Map service. + * 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("isConnected(" + device + ")"); @@ -239,21 +207,33 @@ public class BluetoothMap { } /** - * Disconnects the current Map Client. Currently this call blocks, - * it may soon be made asynchronous. Returns false if this proxy object is - * not currently connected to the Map service. + * Initiate connection. Initiation of outgoing connections is not + * supported for MAP server. */ - public boolean disconnect() { - if (DBG) log("disconnect()"); - if (mService != null) { + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")" + "not supported for MAPS"); + return false; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, + * true otherwise + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { try { - mService.disconnect(); - return true; - } catch (RemoteException e) {Log.e(TAG, e.toString());} - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); + return mService.disconnect(device); + } 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; } @@ -277,19 +257,132 @@ public class BluetoothMap { } } + /** + * Get the list of connected devices. Currently at most one. + * + * @return list of connected devices + */ + public List getConnectedDevices() { + if (DBG) log("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 + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("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 + */ + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("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 + * @param priority + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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("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; + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); mService = IBluetoothMap.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothMap.this); + mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); } } public void onServiceDisconnected(ComponentName className) { if (DBG) log("Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(); + mServiceListener.onServiceDisconnected(BluetoothProfile.MAP); } } }; @@ -297,4 +390,19 @@ public class BluetoothMap { private static void log(String msg) { Log.d(TAG, msg); } + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; + log("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; + } + + } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 6609b988b25..abdf949ebcf 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -67,13 +67,16 @@ public final class BluetoothUuid { public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAP = - ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MNS = ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid MAS = + ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS}; + ObexObjectPush, PANU, NAP, MAP, MNS, MAS}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -124,6 +127,9 @@ public final class BluetoothUuid { public static boolean isMns(ParcelUuid uuid) { return uuid.equals(MNS); } + public static boolean isMas(ParcelUuid uuid) { + return uuid.equals(MAS); + } /** * Returns true if ParcelUuid is present in uuidArray diff --git a/framework/java/android/bluetooth/IBluetoothMap.aidl b/framework/java/android/bluetooth/IBluetoothMap.aidl index 0c18e06a788..d4af63d1fd2 100644 --- a/framework/java/android/bluetooth/IBluetoothMap.aidl +++ b/framework/java/android/bluetooth/IBluetoothMap.aidl @@ -27,6 +27,11 @@ interface IBluetoothMap { int getState(); BluetoothDevice getClient(); boolean connect(in BluetoothDevice device); - void disconnect(); + boolean disconnect(in BluetoothDevice device); boolean isConnected(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); } -- GitLab From 4b707768a2a68f12fec8c7a664e88829604ef409 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Mon, 23 Sep 2013 23:23:13 -0700 Subject: [PATCH 0298/1408] Unhide setPin, setPin, setPairingConfirmation, and relevant intent fields bug 9987787 Change-Id: Id1c149964137e35703310e2f2893c8830aacddea --- .../android/bluetooth/BluetoothDevice.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 2c85382fe13..1efdc818644 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -219,7 +219,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}, * {@link #BOND_BONDING}, * {@link #BOND_BONDED}. - */ + */ public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; /** * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. @@ -228,7 +228,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}, * {@link #BOND_BONDING}, * {@link #BOND_BONDED}. - */ + */ public static final String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; /** @@ -253,12 +253,26 @@ public final class BluetoothDevice implements Parcelable { */ public static final int BOND_BONDED = 12; - /** @hide */ + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents for unbond reason. + * @hide + */ public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; - /** @hide */ + + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents to indicate pairing method used. Possible values are: + * {@link #PAIRING_VARIANT_PIN}, + * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION}, + */ public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; - /** @hide */ + + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents as the value of passkey. + */ public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; /** @@ -306,7 +320,10 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_NAME_FAILED = "android.bluetooth.device.action.NAME_FAILED"; - /** @hide */ + /** + * Broadcast Action: This intent is used to broadcast PAIRING REQUEST + *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; @@ -446,8 +463,8 @@ public final class BluetoothDevice implements Parcelable { public static final int UNBOND_REASON_REMOVED = 9; /** - * The user will be prompted to enter a pin - * @hide + * The user will be prompted to enter a pin or + * a privileged app will enter a pin for user. */ public static final int PAIRING_VARIANT_PIN = 0; @@ -458,8 +475,8 @@ public final class BluetoothDevice implements Parcelable { public static final int PAIRING_VARIANT_PASSKEY = 1; /** - * The user will be prompted to confirm the passkey displayed on the screen - * @hide + * The user will be prompted to confirm the passkey displayed on the screen or + * a privileged app will confirm the passkey for the user. */ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; @@ -707,10 +724,9 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

        Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return false on immediate error, true if bonding will begin - * @hide */ public boolean createBond() { if (sService == null) { @@ -946,7 +962,13 @@ public final class BluetoothDevice implements Parcelable { return BluetoothDevice.ERROR; } - /** @hide */ + /** + * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @return true pin has been set + * false for error + */ public boolean setPin(byte[] pin) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); @@ -968,7 +990,13 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ + /** + * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @return true confirmation has been sent out + * false for error + */ public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); -- GitLab From 9cffff7bd584bf1a49298dc645aa640f2c989a54 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 2 Oct 2013 07:56:46 -0700 Subject: [PATCH 0299/1408] Revert "Allow L2CAP sockets" This reverts commit 008ce9e07b5e58c5ab31c5d73ec3d1ad1a6cf605. --- .../android/bluetooth/BluetoothAdapter.java | 36 ------------------- .../android/bluetooth/BluetoothDevice.java | 27 -------------- .../android/bluetooth/BluetoothSocket.java | 4 +-- 3 files changed, 1 insertion(+), 66 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2172a7b2069..e2bc80aad66 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -922,42 +922,6 @@ public final class BluetoothAdapter { return BluetoothProfile.STATE_DISCONNECTED; } - /** - * Create a listening, L2CAP Bluetooth socket. - *

        A remote device connecting to this socket will optionally be - * authenticated and communication on this socket will optionally be - * encrypted. - *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * @param secure whether security and authentication are required - * @param fixedChannel whether we're looking for a PSM-based connection or a fixed channel - * @param channel L2CAP PSM or channel to use - * @return a listening L2CAP BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. - * @hide - */ - public BluetoothServerSocket listenUsingL2CapOn(boolean secure, boolean fixedChannel, - int channel) throws IOException { - BluetoothServerSocket socket; - - if (fixedChannel) { - channel |= BluetoothSocket.PORT_MASK_FIXED_CHAN; - } - - socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, secure, secure, channel); - int errno = socket.mSocket.bindListen(); - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - /** * Create a listening, secure RFCOMM Bluetooth socket. *

        A remote device connecting to this socket will be authenticated and diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1efdc818644..5eb642cafb0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1172,33 +1172,6 @@ public final class BluetoothDevice implements Parcelable { return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } - - /** - * Construct a L2CAP socket ready to start an outgoing connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * - * @param secure select whether security will be required - * @param fixedChannel select if this will be a "fixed channel" L2CAP connection - * or a PSM-based connection - * @param channel fixed channel or PSM to connect to - * @return a L2CAP BluetoothSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions. - * @hide - */ - public BluetoothSocket createL2CapSocket(boolean secure, boolean fixedChannel, int channel) - throws IOException { - - if (fixedChannel) { - channel |= BluetoothSocket.PORT_MASK_FIXED_CHAN; - } - - return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, secure, secure, this, - channel, null); - } - - /** * Check that a pin is valid and convert to byte array. * diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 191bf67af34..d10eaea2fba 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -103,8 +103,6 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; - /*package*/ static final int PORT_MASK_FIXED_CHAN = 1 << 16; - private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ private String mAddress; /* remote address */ @@ -117,7 +115,7 @@ public final class BluetoothSocket implements Closeable { private LocalSocket mSocket; private InputStream mSocketIS; private OutputStream mSocketOS; - private int mPort; /* RFCOMM channel or L2CAP psm/channel */ + private int mPort; /* RFCOMM channel or L2CAP psm */ private int mFd; private String mServiceName; private static int PROXY_CONNECTION_TIMEOUT = 5000; -- GitLab From 04be95381fa074488fe6180675eafa7e23580960 Mon Sep 17 00:00:00 2001 From: Edward Jee Date: Mon, 7 Oct 2013 18:12:15 -0700 Subject: [PATCH 0300/1408] Makes ACTION_PAIRING_REQUEST require BLUETOOTH_PRIVILEGED. Bug: 11087521 Change-Id: I99cc584987bf12fe0648b048398b4eb9836e6172 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5eb642cafb0..5822e468b36 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -322,7 +322,8 @@ public final class BluetoothDevice implements Parcelable { /** * Broadcast Action: This intent is used to broadcast PAIRING REQUEST - *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} to + * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = -- GitLab From 86418bac9b6f772f8998b78e1dad07cede88097b Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 10 Oct 2013 11:21:40 -0700 Subject: [PATCH 0301/1408] Check callback null condition for register/unregsiter state change callback Fix BluetoothPan closing sequence for unregsiter state change callback bug 11160007 Change-Id: I405ec91a938289fe9541de2ebd9abc1fb938f44a --- .../java/android/bluetooth/BluetoothA2dp.java | 2 +- .../android/bluetooth/BluetoothHeadset.java | 2 +- .../android/bluetooth/BluetoothHealth.java | 2 +- .../bluetooth/BluetoothInputDevice.java | 2 +- .../java/android/bluetooth/BluetoothMap.java | 3 +- .../java/android/bluetooth/BluetoothPan.java | 31 +++++++++++++------ .../java/android/bluetooth/BluetoothPbap.java | 3 +- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index e7e4a0f47fd..6f929f261ae 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -513,7 +513,7 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothA2dp.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 19625145112..8ee955d2379 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -885,7 +885,7 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index b1a084a7b30..2e950faeb4e 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -519,7 +519,7 @@ public final class BluetoothHealth implements BluetoothProfile { mServiceListener = null; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHealth.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index f9c789c58a4..844f432ca32 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -458,7 +458,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothInputDevice.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index fac8fd51848..92a2f1e4253 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -142,7 +142,6 @@ public final class BluetoothMap implements BluetoothProfile { try { mService = null; mContext.unbindService(mConnection); - mConnection = null; } catch (Exception re) { Log.e(TAG,"",re); } @@ -370,7 +369,7 @@ public final class BluetoothMap implements BluetoothProfile { return PRIORITY_OFF; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); mService = IBluetoothMap.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 83d4329e971..b7a37f42b3f 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -155,23 +155,34 @@ public final class BluetoothPan implements BluetoothProfile { /*package*/ void close() { if (VDBG) log("close()"); - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re); + } } - mServiceListener = null; - try { - mAdapter.getBluetoothManager().unregisterStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + + synchronized (mConnection) { + if (mPanService != null) { + try { + mPanService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } } + mServiceListener = null; } protected void finalize() { close(); } - private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { + final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @Override public void onBluetoothStateChange(boolean on) throws RemoteException { @@ -339,7 +350,7 @@ public final class BluetoothPan implements BluetoothProfile { return false; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); mPanService = IBluetoothPan.Stub.asInterface(service); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index c42251f4367..7f456528a9f 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -197,7 +197,6 @@ public class BluetoothPbap { try { mService = null; mContext.unbindService(mConnection); - mConnection = null; } catch (Exception re) { Log.e(TAG,"",re); } @@ -300,7 +299,7 @@ public class BluetoothPbap { } } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); mService = IBluetoothPbap.Stub.asInterface(service); -- GitLab From 0461471825d942147c0c0cbce02d0867b1f1a10e Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 29 Aug 2013 13:40:43 +0100 Subject: [PATCH 0302/1408] Remove captive portal code that has no effect. Note that this CL does not change any behaviour. At the center of this change is CaptivePortalTracker#detectCaptivePortal(), which does nothing except call back into ConnectivityService. Removing it allows us to simplify code in ConnectivityService. It also allows us to remove ConnectivityService#captivePortalCheckComplete which was only ever called in response to this method. While this does not change any behaviour, it preserves existing bad behaviour, i.e, that the CAPTIVE_PORTAL_CHECK NetworkInfo state does not correspond to actual captive portal detection. We transition into that state and immediately (and unconditionally) out of it and into CONNECTED. Change-Id: Ib3797f956d2db5e3cacaaa53e899d81aa8e958af --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index a9b71769335..ca7749d279e 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -142,11 +142,6 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { return true; } - @Override - public void captivePortalCheckComplete() { - // not implemented - } - @Override public void captivePortalCheckCompleted(boolean isCaptivePortal) { // not implemented -- GitLab From 9927a756ebba0b458b1d2d433ca7f94fdc87bc97 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Mon, 21 Oct 2013 14:56:33 -0700 Subject: [PATCH 0303/1408] Change pairing api/intent permission from PRIVILEGED to ADMIN The APIs are createBond, setPin, setPairingConfirmation The intent is ACTION_PAIRING_REQUEST bug 11101076 Change-Id: I3a314efd973b3ce078ab5347159c336f222d9f15 --- .../java/android/bluetooth/BluetoothDevice.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5822e468b36..d789a944f5f 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -322,7 +322,7 @@ public final class BluetoothDevice implements Parcelable { /** * Broadcast Action: This intent is used to broadcast PAIRING REQUEST - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} to + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -465,7 +465,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter a pin or - * a privileged app will enter a pin for user. + * an app will enter a pin for user. */ public static final int PAIRING_VARIANT_PIN = 0; @@ -477,7 +477,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to confirm the passkey displayed on the screen or - * a privileged app will confirm the passkey for the user. + * an app will confirm the passkey for the user. */ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; @@ -725,7 +725,7 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

        Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return false on immediate error, true if bonding will begin */ @@ -965,7 +965,7 @@ public final class BluetoothDevice implements Parcelable { /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true pin has been set * false for error @@ -993,7 +993,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true confirmation has been sent out * false for error -- GitLab From 9c530c2e37aa238af783db1f1715a60b4b74b02d Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Tue, 19 Nov 2013 16:54:46 -0500 Subject: [PATCH 0304/1408] Remove unused imports from frameworks/base. Change-Id: Ia1f99bd2c1105b0b0f70aa614f1f4a67b2840906 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ---- framework/java/android/bluetooth/BluetoothDevice.java | 2 -- framework/java/android/bluetooth/BluetoothGatt.java | 9 --------- .../android/bluetooth/BluetoothGattCharacteristic.java | 1 - .../java/android/bluetooth/BluetoothGattServer.java | 9 --------- .../android/bluetooth/BluetoothGattServerCallback.java | 2 -- framework/java/android/bluetooth/BluetoothHealth.java | 1 - .../java/android/bluetooth/BluetoothInputDevice.java | 1 - framework/java/android/bluetooth/BluetoothMap.java | 1 - framework/java/android/bluetooth/BluetoothPan.java | 1 - framework/java/android/bluetooth/BluetoothPbap.java | 1 - .../java/android/bluetooth/BluetoothServerSocket.java | 1 - framework/java/android/bluetooth/BluetoothSocket.java | 5 ----- .../android/bluetooth/BluetoothTetheringDataTracker.java | 8 -------- 14 files changed, 46 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e2bc80aad66..7ee231338f8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -19,9 +19,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; -import android.os.Binder; import android.os.IBinder; -import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; @@ -34,10 +32,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.HashMap; -import java.util.LinkedList; import java.util.Locale; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.UUID; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d789a944f5f..1bd698d0a2b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -19,12 +19,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.io.IOException; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a2bb78c4c15..b6e1bb379e2 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -18,18 +18,9 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.IBluetoothStateChangeCallback; - -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index f0ecbb4b504..a86677cfaab 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -16,7 +16,6 @@ package android.bluetooth; import java.util.ArrayList; -import java.util.IllegalFormatConversionException; import java.util.List; import java.util.UUID; diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 58ee54fdb10..09072f9bc6d 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -19,18 +19,9 @@ package android.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.IBluetoothStateChangeCallback; - -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index f9f1d975951..fc3ffe88bcc 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -18,8 +18,6 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.util.Log; - /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. */ diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 2e950faeb4e..daf3bad3774 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -23,7 +23,6 @@ import android.content.ServiceConnection; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 844f432ca32..c4ba5b13d8f 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 92a2f1e4253..5a1b7aa818c 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; import android.os.IBinder; -import android.os.ServiceManager; import android.util.Log; /** diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index b7a37f42b3f..e72832c531c 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 7f456528a9f..8522ee016b7 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; import android.os.IBinder; -import android.os.ServiceManager; import android.util.Log; /** diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 96be8a2fb67..bc56e556492 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.os.Handler; -import android.os.Message; import android.os.ParcelUuid; import java.io.Closeable; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..ddefc70dfd4 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,21 +16,16 @@ package android.bluetooth; -import android.os.IBinder; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.io.Closeable; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.List; import java.util.Locale; import java.util.UUID; import android.net.LocalSocket; diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index ca7749d279e..6dd551e8525 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -17,9 +17,6 @@ package android.bluetooth; import android.net.BaseNetworkStateTracker; -import android.os.IBinder; -import android.os.ServiceManager; -import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpResults; @@ -35,11 +32,6 @@ import android.os.Message; import android.os.Messenger; import android.text.TextUtils; import android.util.Log; -import java.net.InterfaceAddress; -import android.net.LinkAddress; -import android.net.RouteInfo; -import java.net.Inet4Address; -import android.os.SystemProperties; import com.android.internal.util.AsyncChannel; -- GitLab From ff55490c00567fcce045df46ac9bbb398b22eafc Mon Sep 17 00:00:00 2001 From: Katie McCormick Date: Tue, 26 Nov 2013 15:59:03 -0800 Subject: [PATCH 0305/1408] Doc update: Update package summary for BT. Fixes: b/8584860 Change-Id: Ic85436f90b46508e6a182e3fc51365f5bfb8da14 --- framework/java/android/bluetooth/package.html | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/package.html b/framework/java/android/bluetooth/package.html index 200a21b8acf..d9ca4f13101 100644 --- a/framework/java/android/bluetooth/package.html +++ b/framework/java/android/bluetooth/package.html @@ -8,17 +8,19 @@ The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.

        Bluetooth guide. For more information about Bluetooth Low Energy, see the -Bluetooth Low Energy guide.

        +Bluetooth Low Energy (BLE) guide.

        {@more}

        The Bluetooth APIs let applications:

          -
        • Scan for other Bluetooth devices (including Bluetooth Low Energy - devices)
        • -
        • Query the local Bluetooth adapter for paired Bluetooth devices
        • -
        • Establish RFCOMM channels/sockets
        • -
        • Connect to specified sockets on other devices
        • -
        • Transfer data to and from other devices
        • +
        • Scan for other Bluetooth devices (including BLE devices).
        • +
        • Query the local Bluetooth adapter for paired Bluetooth devices.
        • +
        • Establish RFCOMM channels/sockets.
        • +
        • Connect to specified sockets on other devices.
        • +
        • Transfer data to and from other devices.
        • +
        • Communicate with BLE devices, such as proximity sensors, heart rate + monitors, fitness devices, and so on.
        • +
        • Act as a GATT client or a GATT server (BLE).

        -- GitLab From f4f4b3be361ff067ec055a1096f539c67a49b7c0 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Tue, 17 Dec 2013 11:39:20 -0800 Subject: [PATCH 0306/1408] NPE in BluetoothSocket.write() If calling connect succeed, we should not see this NPE. An IOException may happen after call BluetoothSocket.connect. If you still call write after the IOException, you will get this NPE. add NPE protection for possible wrong calling sequence from application, To make bluetoothSocket more error-tolerant. bug:12104154 Change-Id: I7fa4e847b500ca9b9d2a43df432f31a1bb016c0a --- .../android/bluetooth/BluetoothSocket.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..1e75fc2a1c0 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -417,27 +417,28 @@ public final class BluetoothSocket implements Closeable { * if an i/o error occurs. */ /*package*/ void flush() throws IOException { + if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); if (VDBG) Log.d(TAG, "flush: " + mSocketOS); mSocketOS.flush(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - int ret = mSocketIS.read(b, offset, length); - if(ret < 0) - throw new IOException("bt socket closed, read return: " + ret); - if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); - return ret; + if (mSocketIS == null) throw new IOException("read is called on null InputStream"); + if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + int ret = mSocketIS.read(b, offset, length); + if(ret < 0) + throw new IOException("bt socket closed, read return: " + ret); + if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - mSocketOS.write(b, offset, length); - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; + if (mSocketOS == null) throw new IOException("write is called on null OutputStream"); + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); + mSocketOS.write(b, offset, length); + // There is no good way to confirm since the entire process is asynchronous anyway + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; } @Override -- GitLab From b4422d237f39921deaa51f410653658d2e3dc262 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 22 Nov 2013 08:25:26 -0800 Subject: [PATCH 0307/1408] Move some system services to separate directories Refactored the directory structure so that services can be optionally excluded. This is step 1. Will be followed by another change that makes it possible to remove services from the build. Change-Id: Ideacedfd34b5e213217ad3ff4ebb21c4a8e73f85 --- .../bluetooth/BluetoothManagerService.java | 1262 +++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 service/java/com/android/server/bluetooth/BluetoothManagerService.java diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java new file mode 100644 index 00000000000..546324a73e0 --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -0,0 +1,1262 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.ActivityManager; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.IBluetoothManagerCallback; +import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; +class BluetoothManagerService extends IBluetoothManager.Stub { + private static final String TAG = "BluetoothManagerService"; + private static final boolean DBG = true; + + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; + private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; + private static final String EXTRA_ACTION="action"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; + private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind + private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save + //Maximum msec to wait for service restart + private static final int SERVICE_RESTART_TIME_MS = 200; + //Maximum msec to wait for restart due to error + private static final int ERROR_RESTART_TIME_MS = 3000; + //Maximum msec to delay MESSAGE_USER_SWITCHED + private static final int USER_SWITCHED_TIME_MS = 200; + + private static final int MESSAGE_ENABLE = 1; + private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_REGISTER_ADAPTER = 20; + private static final int MESSAGE_UNREGISTER_ADAPTER = 21; + private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; + private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; + private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; + private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; + private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42; + private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60; + private static final int MESSAGE_TIMEOUT_BIND =100; + private static final int MESSAGE_TIMEOUT_UNBIND =101; + private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; + private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; + private static final int MESSAGE_USER_SWITCHED = 300; + private static final int MAX_SAVE_RETRIES=3; + private static final int MAX_ERROR_RESTART_RETRIES=6; + + // Bluetooth persisted setting is off + private static final int BLUETOOTH_OFF=0; + // Bluetooth persisted setting is on + // and Airplane mode won't affect Bluetooth state at start up + private static final int BLUETOOTH_ON_BLUETOOTH=1; + // Bluetooth persisted setting is on + // but Airplane mode will affect Bluetooth state at start up + // and Airplane mode will have higher priority. + private static final int BLUETOOTH_ON_AIRPLANE=2; + + private static final int SERVICE_IBLUETOOTH = 1; + private static final int SERVICE_IBLUETOOTHGATT = 2; + + private final Context mContext; + + // Locks are not provided for mName and mAddress. + // They are accessed in handler or broadcast receiver, same thread context. + private String mAddress; + private String mName; + private final ContentResolver mContentResolver; + private final RemoteCallbackList mCallbacks; + private final RemoteCallbackList mStateChangeCallbacks; + private IBluetooth mBluetooth; + private IBluetoothGatt mBluetoothGatt; + private boolean mBinding; + private boolean mUnbinding; + // used inside handler thread + private boolean mQuietEnable = false; + // configuarion from external IBinder call which is used to + // synchronize with broadcast receiver. + private boolean mQuietEnableExternal; + // configuarion from external IBinder call which is used to + // synchronize with broadcast receiver. + private boolean mEnableExternal; + // used inside handler thread + private boolean mEnable; + private int mState; + private final BluetoothHandler mHandler; + private int mErrorRecoveryRetryCounter; + + private void registerForAirplaneMode(IntentFilter filter) { + final ContentResolver resolver = mContext.getContentResolver(); + final String airplaneModeRadios = Settings.Global.getString(resolver, + Settings.Global.AIRPLANE_MODE_RADIOS); + final String toggleableRadios = Settings.Global.getString(resolver, + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); + boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : + airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH); + if (mIsAirplaneSensitive) { + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + } + } + + private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { + @Override + public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { + Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); + mHandler.sendMessage(msg); + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { + String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); + if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName); + if (newName != null) { + storeNameAndAddress(newName, null); + } + } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + synchronized(mReceiver) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + } + if (isAirplaneModeOn()) { + // disable without persisting the setting + sendDisableMsg(); + } else if (mEnableExternal) { + // enable without persisting the setting + sendEnableMsg(mQuietEnableExternal); + } + } + } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); + } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + synchronized(mReceiver) { + if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { + //Enable + if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); + sendEnableMsg(mQuietEnableExternal); + } + } + + if (!isNameAndAddressSet()) { + //Sync the Bluetooth name and address from the Bluetooth Adapter + if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); + getNameAndAddress(); + } + } + } + }; + + BluetoothManagerService(Context context) { + mHandler = new BluetoothHandler(IoThread.get().getLooper()); + + mContext = context; + mBluetooth = null; + mBinding = false; + mUnbinding = false; + mEnable = false; + mState = BluetoothAdapter.STATE_OFF; + mQuietEnableExternal = false; + mEnableExternal = false; + mAddress = null; + mName = null; + mErrorRecoveryRetryCounter = 0; + mContentResolver = context.getContentResolver(); + mCallbacks = new RemoteCallbackList(); + mStateChangeCallbacks = new RemoteCallbackList(); + IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); + filter.addAction(Intent.ACTION_USER_SWITCHED); + registerForAirplaneMode(filter); + mContext.registerReceiver(mReceiver, filter); + loadStoredNameAndAddress(); + if (isBluetoothPersistedStateOn()) { + mEnableExternal = true; + } + } + + /** + * Returns true if airplane mode is currently on + */ + private final boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + /** + * Returns true if the Bluetooth saved state is "on" + */ + private final boolean isBluetoothPersistedStateOn() { + return Settings.Global.getInt(mContentResolver, + Settings.Global.BLUETOOTH_ON, 0) != BLUETOOTH_OFF; + } + + /** + * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH + */ + private final boolean isBluetoothPersistedStateOnBluetooth() { + return Settings.Global.getInt(mContentResolver, + Settings.Global.BLUETOOTH_ON, 0) == BLUETOOTH_ON_BLUETOOTH; + } + + /** + * Save the Bluetooth on/off state + * + */ + private void persistBluetoothSetting(int value) { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_ON, + value); + } + + /** + * Returns true if the Bluetooth Adapter's name and address is + * locally cached + * @return + */ + private boolean isNameAndAddressSet() { + return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0; + } + + /** + * Retrieve the Bluetooth Adapter's name and address and save it in + * in the local cache + */ + private void loadStoredNameAndAddress() { + if (DBG) Log.d(TAG, "Loading stored name and address"); + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_address_validation) && + Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) { + // if the valid flag is not set, don't load the address and name + if (DBG) Log.d(TAG, "invalid bluetooth name and address stored"); + return; + } + mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); + mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); + if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); + } + + /** + * Save the Bluetooth name and address in the persistent store. + * Only non-null values will be saved. + * @param name + * @param address + */ + private void storeNameAndAddress(String name, String address) { + if (name != null) { + Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); + mName = name; + if (DBG) Log.d(TAG,"Stored Bluetooth name: " + + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); + } + + if (address != null) { + Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); + mAddress=address; + if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " + + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + } + + if ((name != null) && (address != null)) { + Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1); + } + } + + public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ + Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); + msg.obj = callback; + mHandler.sendMessage(msg); + synchronized(mConnection) { + return mBluetooth; + } + } + + public void unregisterAdapter(IBluetoothManagerCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); + msg.obj = callback; + mHandler.sendMessage(msg); + } + + public boolean isEnabled() { + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && + (!checkIfCallerIsForegroundUser())) { + Log.w(TAG,"isEnabled(): not allowed for non-active and non system user"); + return false; + } + + synchronized(mConnection) { + try { + return (mBluetooth != null && mBluetooth.isEnabled()); + } catch (RemoteException e) { + Log.e(TAG, "isEnabled()", e); + } + } + return false; + } + + public void getNameAndAddress() { + if (DBG) { + Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth + + " mBinding = " + mBinding); + } + Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(msg); + } + public boolean enableNoAutoConnect() + { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (DBG) { + Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding); + } + int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); + + if (callingAppId != Process.NFC_UID) { + throw new SecurityException("no permission to enable Bluetooth quietly"); + } + + synchronized(mReceiver) { + mQuietEnableExternal = true; + mEnableExternal = true; + sendEnableMsg(true); + } + return true; + + } + public boolean enable() { + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && + (!checkIfCallerIsForegroundUser())) { + Log.w(TAG,"enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + if (DBG) { + Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized(mReceiver) { + mQuietEnableExternal = false; + mEnableExternal = true; + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + Binder.restoreCallingIdentity(callingIdentity); + sendEnableMsg(false); + } + return true; + } + + public boolean disable(boolean persist) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && + (!checkIfCallerIsForegroundUser())) { + Log.w(TAG,"disable(): not allowed for non-active and non system user"); + return false; + } + + if (DBG) { + Log.d(TAG,"disable(): mBluetooth = " + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized(mReceiver) { + if (persist) { + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); + persistBluetoothSetting(BLUETOOTH_OFF); + Binder.restoreCallingIdentity(callingIdentity); + } + mEnableExternal = false; + sendDisableMsg(); + } + return true; + } + + public void unbindAndFinish() { + if (DBG) { + Log.d(TAG,"unbindAndFinish(): " + mBluetooth + + " mBinding = " + mBinding); + } + + synchronized (mConnection) { + if (mUnbinding) return; + mUnbinding = true; + if (mBluetooth != null) { + if (!mConnection.isGetNameAddressOnly()) { + //Unregister callback object + try { + mBluetooth.unregisterCallback(mBluetoothCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothCallback",re); + } + } + if (DBG) Log.d(TAG, "Sending unbind request."); + mBluetooth = null; + //Unbind + mContext.unbindService(mConnection); + mUnbinding = false; + mBinding = false; + } else { + mUnbinding=false; + } + } + } + + public IBluetoothGatt getBluetoothGatt() { + // sync protection + return mBluetoothGatt; + } + + private void sendBluetoothStateCallback(boolean isUp) { + int n = mStateChangeCallbacks.beginBroadcast(); + if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); + for (int i=0; i " + newState); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + BLUETOOTH_PERM); + } + } + + /** + * if on is true, wait for state become ON + * if off is true, wait for state become OFF + * if both on and off are false, wait for state not ON + */ + private boolean waitForOnOff(boolean on, boolean off) { + int i = 0; + while (i < 10) { + synchronized(mConnection) { + try { + if (mBluetooth == null) break; + if (on) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; + } else if (off) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; + } else { + if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; + } + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + break; + } + } + if (on || off) { + SystemClock.sleep(300); + } else { + SystemClock.sleep(50); + } + i++; + } + Log.e(TAG,"waitForOnOff time out"); + return false; + } + + private void sendDisableMsg() { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); + } + + private void sendEnableMsg(boolean quietMode) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, + quietMode ? 1 : 0, 0)); + } + + private boolean canUnbindBluetoothService() { + synchronized(mConnection) { + //Only unbind with mEnable flag not set + //For race condition: disable and enable back-to-back + //Avoid unbind right after enable due to callback from disable + //Only unbind with Bluetooth at OFF state + //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message + try { + if (mEnable || (mBluetooth == null)) return false; + if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; + return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + } + } + return false; + } + + private void recoverBluetoothServiceFromError() { + Log.e(TAG,"recoverBluetoothServiceFromError"); + synchronized (mConnection) { + if (mBluetooth != null) { + //Unregister callback object + try { + mBluetooth.unregisterCallback(mBluetoothCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister",re); + } + } + } + + SystemClock.sleep(500); + + // disable + handleDisable(); + + waitForOnOff(false, true); + + sendBluetoothServiceDownCallback(); + synchronized (mConnection) { + if (mBluetooth != null) { + mBluetooth = null; + //Unbind + mContext.unbindService(mConnection); + } + } + + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mState = BluetoothAdapter.STATE_OFF; + + mEnable = false; + + if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { + // Send a Bluetooth Restart message to reenable bluetooth + Message restartMsg = mHandler.obtainMessage( + MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); + } else { + // todo: notify user to power down and power up phone to make bluetooth work. + } + } +} -- GitLab From 2f7544ce3f0c2e0697318e9961c4ced457b71d5f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 29 Oct 2013 21:05:37 -0700 Subject: [PATCH 0308/1408] BLE peripheral mode (3/4): Add peripheral mode API. Change-Id: Id9d2f566b6d9ed0fffe73b67efad2e3d045360b4 Conflicts: core/java/android/bluetooth/BluetoothAdapter.java core/java/android/bluetooth/BluetoothGatt.java --- .../android/bluetooth/BluetoothAdapter.java | 209 +++++++++++++++++- .../bluetooth/BluetoothAdvScanData.java | 147 ++++++++++++ .../java/android/bluetooth/BluetoothGatt.java | 73 ------ .../bluetooth/BluetoothGattServer.java | 2 +- .../bluetooth/BluetoothGattService.java | 23 +- .../java/android/bluetooth/BluetoothUuid.java | 16 ++ .../android/bluetooth/IBluetoothGatt.aidl | 15 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 - 8 files changed, 394 insertions(+), 92 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdvScanData.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7ee231338f8..38a71aaa25f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -25,13 +25,14 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -178,6 +179,43 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + /** + * Activity Action: Show a system activity to request BLE advertising.
        + * If the device is not doing BLE advertising, this activity will start BLE advertising for the + * device, otherwise it will continue BLE advertising using the current + * {@link BluetoothAdvScanData}.
        + * Note this activity will also request the user to turn on Bluetooth if it's not currently + * enabled. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_START_ADVERTISING = + "android.bluetooth.adapter.action.START_ADVERTISING"; + + /** + * Activity Action: Stop the current BLE advertising. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_STOP_ADVERTISING = + "android.bluetooth.adapter.action.STOP_ADVERTISING"; + + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Activity Action: Show a system activity that allows the user to turn on * Bluetooth. @@ -247,7 +285,6 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -361,6 +398,8 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map mLeScanClients; + private BluetoothAdvScanData mBluetoothAdvScanData = null; + private GattCallbackWrapper mAdvertisingCallback; /** * Get a handle to the default local Bluetooth adapter. @@ -433,6 +472,97 @@ public final class BluetoothAdapter { address[0], address[1], address[2], address[3], address[4], address[5])); } + /** + * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * @hide + */ + public BluetoothAdvScanData getAdvScanData() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + Log.e(TAG, "failed to start, iGatt null"); + return null; + } + if (mBluetoothAdvScanData == null) { + mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); + } + return mBluetoothAdvScanData; + } catch (RemoteException e) { + Log.e(TAG, "failed to get advScanData, error: " + e); + return null; + } + } + + + /** + * Start BLE advertising using current {@link BluetoothAdvScanData}. + * An app should start advertising by requesting + * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @return true if BLE avertising succeeds, false otherwise. + * @hide + */ + public boolean startAdvertising() { + if (getState() != STATE_ON) return false; + + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported. + return false; + } + // Restart/reset advertising packets if advertising is in progress. + if (isAdvertising()) { + // Invalid advertising callback. + if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) { + Log.e(TAG, "failed to restart advertising, invalid callback"); + return false; + } + iGatt.startAdvertising(mAdvertisingCallback.mLeHandle); + return true; + } + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = + new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV); + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + mAdvertisingCallback = wrapper; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Stop BLE advertising. + * An app should stop advertising by requesting + * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * @return true if BLE advertising stops, false otherwise. + * @hide + */ + public boolean stopAdvertisting() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + if (mAdvertisingCallback == null) { + // no callback. + return false; + } + mAdvertisingCallback.stopAdvertising(); + mAdvertisingCallback = null; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Return true if Bluetooth is currently enabled and ready for use. *

        Equivalent to: @@ -844,6 +974,23 @@ public final class BluetoothAdapter { return false; } + /** + * Returns whether BLE is currently advertising. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @hide + */ + public boolean isAdvertising() { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return iGatt.isAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. @@ -1542,8 +1689,12 @@ public final class BluetoothAdapter { private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + private static final int CALLBACK_TYPE_SCAN = 0; + private static final int CALLBACK_TYPE_ADV = 1; private final LeScanCallback mLeScanCb; + private int mCallbackType; + // mLeHandle 0: not registered // -1: scan stopped // >0: registered and scan started @@ -1557,6 +1708,16 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; + mCallbackType = CALLBACK_TYPE_SCAN; + } + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, + UUID[] uuid, int type) { + mBluetoothAdapter = new WeakReference(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + mCallbackType = type; } public boolean scanStarted() { @@ -1579,6 +1740,30 @@ public final class BluetoothAdapter { return started; } + public void stopAdvertising() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopAdvertising(); + Log.d(TAG, "unregeistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising and unregister" + e); + } + } else { + Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); + } + mLeHandle = -1; + notifyAll(); + } + } + public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1620,14 +1805,18 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); + if (mCallbackType == CALLBACK_TYPE_ADV) { + iGatt.startAdvertising(mLeHandle); } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -1638,7 +1827,7 @@ public final class BluetoothAdapter { mLeHandle = -1; } if (mLeHandle == -1) { - // registration succeeded but start scan failed + // registration succeeded but start scan or advertise failed if (iGatt != null) { try { iGatt.unregisterClient(mLeHandle); diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java new file mode 100644 index 00000000000..a97b0a806e3 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdvScanData.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + + +/** + * This class provides the public APIs to set advertising and scan response data when BLE device + * operates in peripheral mode.
        + * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * @hide + */ +public final class BluetoothAdvScanData { + + /** + * Available data types of {@link BluetoothAdvScanData}. + */ + public static final int AD = 0; // Advertising Data + public static final int SCAN_RESPONSE = 1; // Scan Response + public static final int EIR = 2; // Extended Inquiry Response + + private static final String TAG = "BluetoothAdvScanData"; + + /** + * Data type of BluetoothAdvScanData. + */ + private final int mDataType; + /** + * Bluetooth Gatt Service. + */ + private IBluetoothGatt mBluetoothGatt; + + /** + * @param mBluetoothGatt + * @param dataType + */ + public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { + this.mBluetoothGatt = mBluetoothGatt; + this.mDataType = dataType; + } + + /** + * @return advertising data type. + */ + public int getDataType() { + return mDataType; + } + + /** + * Set manufactureCode and manufactureData. + * Returns true if manufacturer data is set, false if there is no enough room to set + * manufacturer data or the data is already set. + * @param manufacturerCode - unique identifier for the manufacturer + * @param manufacturerData - data associated with the specific manufacturer. + */ + public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { + if (mDataType != AD) return false; + try { + return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Set service data. Note the service data can only be set when the data type is {@code AD}; + * @param serviceData + */ + public boolean setServiceData(byte[] serviceData) { + + if (mDataType != AD) return false; + if (serviceData == null) return false; + try { + return mBluetoothGatt.setAdvServiceData(serviceData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Returns an immutable list of service uuids that will be advertised. + */ + public List getServiceUuids() { + try { + return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns manufacturer data. + */ + public byte[] getManufacturerData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvManufacturerData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns service data. + */ + public byte[] getServiceData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvServiceData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Remove manufacturer data based on given manufacturer code. + * @param manufacturerCode + */ + public void removeManufacturerCodeAndData(int manufacturerCode) { + if (mBluetoothGatt != null) { + try { + mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index b6e1bb379e2..e3820a2e2f2 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -544,14 +544,6 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } - - /** - * Listen command status callback - * @hide - */ - public void onListen(int status) { - if (DBG) Log.d(TAG, "onListen() - status=" + status); - } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { @@ -684,71 +676,6 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } - /** - * Starts or stops sending of advertisement packages to listen for connection - * requests from a central devices. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param start Start or stop advertising - */ - /*package*/ void listen(boolean start) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); - } - if (DBG) Log.d(TAG, "listen() - start: " + start); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientListen(mClientIf, start); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Sets the advertising data contained in the adv. response packet. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param advData true to set adv. data, false to set scan response - * @param includeName Inlucde the name in the adv. response - * @param includeTxPower Include TX power value - * @param minInterval Minimum desired scan interval (optional) - * @param maxInterval Maximum desired scan interval (optional) - * @param appearance The appearance flags for the device (optional) - * @param manufacturerData Manufacturer specific data including company ID (optional) - */ - /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, - Integer minInterval, Integer maxInterval, - Integer appearance, Byte[] manufacturerData) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); - } - if (DBG) Log.d(TAG, "setAdvData()"); - if (mService == null || mClientIf == 0) return; - - byte[] data = new byte[0]; - if (manufacturerData != null) { - data = new byte[manufacturerData.length]; - for(int i = 0; i != manufacturerData.length; ++i) { - data[i] = manufacturerData[i]; - } - } - - try { - mService.setAdvData(mClientIf, !advData, - includeName, includeTxPower, - minInterval != null ? minInterval : 0, - maxInterval != null ? maxInterval : 0, - appearance != null ? appearance : 0, data); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 09072f9bc6d..0c00c06712a 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -528,7 +528,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.beginServiceDeclaration(mServerIf, service.getType(), service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid())); + new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); List includedServices = service.getIncludedServices(); for (BluetoothGattService includedService : includedServices) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 1e663696010..52bc0f796cb 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,8 +15,6 @@ */ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -81,6 +79,11 @@ public class BluetoothGattService { */ protected List mIncludedServices; + /** + * Whether the service uuid should be advertised. + */ + private boolean mAdvertisePreferred; + /** * Create a new BluetoothGattService. *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -263,4 +266,20 @@ public class BluetoothGattService { } return null; } + + /** + * Returns whether the uuid of the service should be advertised. + * @hide + */ + public boolean isAdvertisePreferred() { + return mAdvertisePreferred; + } + + /** + * Set whether the service uuid should be advertised. + * @hide + */ + public void setAdvertisePreferred(boolean advertisePreferred) { + this.mAdvertisePreferred = advertisePreferred; + } } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index abdf949ebcf..4b28516e15a 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -73,6 +73,9 @@ public final class BluetoothUuid { public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = + ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -211,4 +214,17 @@ public final class BluetoothUuid { long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; return (int)value; } + + /** + * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * @param parcelUuid + * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + */ + public static boolean isShortUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + } } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index df393dbb289..784cdcc5318 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,10 +37,15 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); - void clientListen(in int clientIf, in boolean start); - void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, - in boolean inclTxPower, in int minInterval, in int maxInterval, - in int appearance, in byte[] manufacturerData); + void startAdvertising(in int appIf); + void stopAdvertising(); + boolean setAdvServiceData(in byte[] serviceData); + byte[] getAdvServiceData(); + boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); + byte[] getAdvManufacturerData(); + List getAdvServiceUuids(); + void removeAdvManufacturerCodeAndData(int manufacturerCode); + boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, @@ -75,7 +80,7 @@ interface IBluetoothGatt { void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId); + in ParcelUuid srvcId, boolean advertisePreferred); void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId); void addCharacteristic(in int serverIf, in ParcelUuid charId, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 60c297b5979..e3563fcc3a7 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,5 +63,4 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onListen(in int status); } -- GitLab From 4907d56a6cc1ae4906c324dc39dc4d44a6c5fa63 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Mon, 13 Jan 2014 16:54:38 -0800 Subject: [PATCH 0309/1408] NPE in BluetoothDevice.fetchUuidsWithSdp This is caused by access fetchUuidsWithSdp after bluetooth is turned off. We can add check null pointer for sService, before call sService.fetchRemoteUuids(this) to fix this problem; bug:12533948 Change-Id: Id2cab92a56185073fabcabcfb21a243e99a60cee --- framework/java/android/bluetooth/BluetoothDevice.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d789a944f5f..0e677416aea 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -947,8 +947,13 @@ public final class BluetoothDevice implements Parcelable { * was started. */ public boolean fetchUuidsWithSdp() { + IBluetooth service = sService; + if (service == null) { + Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); + return false; + } try { - return sService.fetchRemoteUuids(this); + return service.fetchRemoteUuids(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } -- GitLab From 85c4f02c03e459009051ad706db3babecccfa5c2 Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Tue, 17 Dec 2013 11:39:20 -0800 Subject: [PATCH 0310/1408] NPE in BluetoothSocket.write() If calling connect succeed, we should not see this NPE. An IOException may happen after call BluetoothSocket.connect. If you still call write after the IOException, you will get this NPE. add NPE protection for possible wrong calling sequence from application, To make bluetoothSocket more error-tolerant. bug:12104154 Change-Id: I7fa4e847b500ca9b9d2a43df432f31a1bb016c0a --- .../android/bluetooth/BluetoothSocket.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..1e75fc2a1c0 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -417,27 +417,28 @@ public final class BluetoothSocket implements Closeable { * if an i/o error occurs. */ /*package*/ void flush() throws IOException { + if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); if (VDBG) Log.d(TAG, "flush: " + mSocketOS); mSocketOS.flush(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - int ret = mSocketIS.read(b, offset, length); - if(ret < 0) - throw new IOException("bt socket closed, read return: " + ret); - if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); - return ret; + if (mSocketIS == null) throw new IOException("read is called on null InputStream"); + if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + int ret = mSocketIS.read(b, offset, length); + if(ret < 0) + throw new IOException("bt socket closed, read return: " + ret); + if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - mSocketOS.write(b, offset, length); - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; + if (mSocketOS == null) throw new IOException("write is called on null OutputStream"); + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); + mSocketOS.write(b, offset, length); + // There is no good way to confirm since the entire process is asynchronous anyway + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; } @Override -- GitLab From aa07cb29cad49e5e7fdab7a7fc34105c47c3ada0 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 29 Oct 2013 21:05:37 -0700 Subject: [PATCH 0311/1408] BLE peripheral mode (3/4): Add peripheral mode API. Change-Id: Id9d2f566b6d9ed0fffe73b67efad2e3d045360b4 Conflicts: core/java/android/bluetooth/BluetoothAdapter.java core/java/android/bluetooth/BluetoothGatt.java Conflicts: core/java/android/bluetooth/BluetoothAdapter.java --- .../android/bluetooth/BluetoothAdapter.java | 210 +++++++++++++++++- .../bluetooth/BluetoothAdvScanData.java | 147 ++++++++++++ .../java/android/bluetooth/BluetoothGatt.java | 73 ------ .../bluetooth/BluetoothGattServer.java | 2 +- .../bluetooth/BluetoothGattService.java | 23 +- .../java/android/bluetooth/BluetoothUuid.java | 16 ++ .../android/bluetooth/IBluetoothGatt.aidl | 15 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 - 8 files changed, 394 insertions(+), 93 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdvScanData.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e2bc80aad66..d1f1f2af44c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -27,14 +27,14 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; -import java.util.LinkedList; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Random; @@ -182,6 +182,43 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + /** + * Activity Action: Show a system activity to request BLE advertising.
        + * If the device is not doing BLE advertising, this activity will start BLE advertising for the + * device, otherwise it will continue BLE advertising using the current + * {@link BluetoothAdvScanData}.
        + * Note this activity will also request the user to turn on Bluetooth if it's not currently + * enabled. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_START_ADVERTISING = + "android.bluetooth.adapter.action.START_ADVERTISING"; + + /** + * Activity Action: Stop the current BLE advertising. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_STOP_ADVERTISING = + "android.bluetooth.adapter.action.STOP_ADVERTISING"; + + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Activity Action: Show a system activity that allows the user to turn on * Bluetooth. @@ -251,7 +288,6 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -365,6 +401,8 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map mLeScanClients; + private BluetoothAdvScanData mBluetoothAdvScanData = null; + private GattCallbackWrapper mAdvertisingCallback; /** * Get a handle to the default local Bluetooth adapter. @@ -437,6 +475,97 @@ public final class BluetoothAdapter { address[0], address[1], address[2], address[3], address[4], address[5])); } + /** + * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * @hide + */ + public BluetoothAdvScanData getAdvScanData() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + Log.e(TAG, "failed to start, iGatt null"); + return null; + } + if (mBluetoothAdvScanData == null) { + mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); + } + return mBluetoothAdvScanData; + } catch (RemoteException e) { + Log.e(TAG, "failed to get advScanData, error: " + e); + return null; + } + } + + + /** + * Start BLE advertising using current {@link BluetoothAdvScanData}. + * An app should start advertising by requesting + * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @return true if BLE avertising succeeds, false otherwise. + * @hide + */ + public boolean startAdvertising() { + if (getState() != STATE_ON) return false; + + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported. + return false; + } + // Restart/reset advertising packets if advertising is in progress. + if (isAdvertising()) { + // Invalid advertising callback. + if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) { + Log.e(TAG, "failed to restart advertising, invalid callback"); + return false; + } + iGatt.startAdvertising(mAdvertisingCallback.mLeHandle); + return true; + } + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = + new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV); + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + mAdvertisingCallback = wrapper; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Stop BLE advertising. + * An app should stop advertising by requesting + * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * @return true if BLE advertising stops, false otherwise. + * @hide + */ + public boolean stopAdvertisting() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + if (mAdvertisingCallback == null) { + // no callback. + return false; + } + mAdvertisingCallback.stopAdvertising(); + mAdvertisingCallback = null; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Return true if Bluetooth is currently enabled and ready for use. *

        Equivalent to: @@ -848,6 +977,23 @@ public final class BluetoothAdapter { return false; } + /** + * Returns whether BLE is currently advertising. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @hide + */ + public boolean isAdvertising() { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return iGatt.isAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. @@ -1546,8 +1692,12 @@ public final class BluetoothAdapter { private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + private static final int CALLBACK_TYPE_SCAN = 0; + private static final int CALLBACK_TYPE_ADV = 1; private final LeScanCallback mLeScanCb; + private int mCallbackType; + // mLeHandle 0: not registered // -1: scan stopped // >0: registered and scan started @@ -1561,6 +1711,16 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; + mCallbackType = CALLBACK_TYPE_SCAN; + } + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, + UUID[] uuid, int type) { + mBluetoothAdapter = new WeakReference(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + mCallbackType = type; } public boolean scanStarted() { @@ -1583,6 +1743,30 @@ public final class BluetoothAdapter { return started; } + public void stopAdvertising() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopAdvertising(); + Log.d(TAG, "unregeistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising and unregister" + e); + } + } else { + Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); + } + mLeHandle = -1; + notifyAll(); + } + } + public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1624,14 +1808,18 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); + if (mCallbackType == CALLBACK_TYPE_ADV) { + iGatt.startAdvertising(mLeHandle); } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -1642,7 +1830,7 @@ public final class BluetoothAdapter { mLeHandle = -1; } if (mLeHandle == -1) { - // registration succeeded but start scan failed + // registration succeeded but start scan or advertise failed if (iGatt != null) { try { iGatt.unregisterClient(mLeHandle); diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java new file mode 100644 index 00000000000..a97b0a806e3 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdvScanData.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + + +/** + * This class provides the public APIs to set advertising and scan response data when BLE device + * operates in peripheral mode.
        + * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * @hide + */ +public final class BluetoothAdvScanData { + + /** + * Available data types of {@link BluetoothAdvScanData}. + */ + public static final int AD = 0; // Advertising Data + public static final int SCAN_RESPONSE = 1; // Scan Response + public static final int EIR = 2; // Extended Inquiry Response + + private static final String TAG = "BluetoothAdvScanData"; + + /** + * Data type of BluetoothAdvScanData. + */ + private final int mDataType; + /** + * Bluetooth Gatt Service. + */ + private IBluetoothGatt mBluetoothGatt; + + /** + * @param mBluetoothGatt + * @param dataType + */ + public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { + this.mBluetoothGatt = mBluetoothGatt; + this.mDataType = dataType; + } + + /** + * @return advertising data type. + */ + public int getDataType() { + return mDataType; + } + + /** + * Set manufactureCode and manufactureData. + * Returns true if manufacturer data is set, false if there is no enough room to set + * manufacturer data or the data is already set. + * @param manufacturerCode - unique identifier for the manufacturer + * @param manufacturerData - data associated with the specific manufacturer. + */ + public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { + if (mDataType != AD) return false; + try { + return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Set service data. Note the service data can only be set when the data type is {@code AD}; + * @param serviceData + */ + public boolean setServiceData(byte[] serviceData) { + + if (mDataType != AD) return false; + if (serviceData == null) return false; + try { + return mBluetoothGatt.setAdvServiceData(serviceData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Returns an immutable list of service uuids that will be advertised. + */ + public List getServiceUuids() { + try { + return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns manufacturer data. + */ + public byte[] getManufacturerData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvManufacturerData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns service data. + */ + public byte[] getServiceData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvServiceData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Remove manufacturer data based on given manufacturer code. + * @param manufacturerCode + */ + public void removeManufacturerCodeAndData(int manufacturerCode) { + if (mBluetoothGatt != null) { + try { + mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a2bb78c4c15..cd093c5a441 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -553,14 +553,6 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } - - /** - * Listen command status callback - * @hide - */ - public void onListen(int status) { - if (DBG) Log.d(TAG, "onListen() - status=" + status); - } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { @@ -693,71 +685,6 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } - /** - * Starts or stops sending of advertisement packages to listen for connection - * requests from a central devices. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param start Start or stop advertising - */ - /*package*/ void listen(boolean start) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); - } - if (DBG) Log.d(TAG, "listen() - start: " + start); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientListen(mClientIf, start); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Sets the advertising data contained in the adv. response packet. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param advData true to set adv. data, false to set scan response - * @param includeName Inlucde the name in the adv. response - * @param includeTxPower Include TX power value - * @param minInterval Minimum desired scan interval (optional) - * @param maxInterval Maximum desired scan interval (optional) - * @param appearance The appearance flags for the device (optional) - * @param manufacturerData Manufacturer specific data including company ID (optional) - */ - /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, - Integer minInterval, Integer maxInterval, - Integer appearance, Byte[] manufacturerData) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); - } - if (DBG) Log.d(TAG, "setAdvData()"); - if (mService == null || mClientIf == 0) return; - - byte[] data = new byte[0]; - if (manufacturerData != null) { - data = new byte[manufacturerData.length]; - for(int i = 0; i != manufacturerData.length; ++i) { - data[i] = manufacturerData[i]; - } - } - - try { - mService.setAdvData(mClientIf, !advData, - includeName, includeTxPower, - minInterval != null ? minInterval : 0, - maxInterval != null ? maxInterval : 0, - appearance != null ? appearance : 0, data); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 58ee54fdb10..153215cb525 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -537,7 +537,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.beginServiceDeclaration(mServerIf, service.getType(), service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid())); + new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); List includedServices = service.getIncludedServices(); for (BluetoothGattService includedService : includedServices) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 1e663696010..52bc0f796cb 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,8 +15,6 @@ */ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -81,6 +79,11 @@ public class BluetoothGattService { */ protected List mIncludedServices; + /** + * Whether the service uuid should be advertised. + */ + private boolean mAdvertisePreferred; + /** * Create a new BluetoothGattService. *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -263,4 +266,20 @@ public class BluetoothGattService { } return null; } + + /** + * Returns whether the uuid of the service should be advertised. + * @hide + */ + public boolean isAdvertisePreferred() { + return mAdvertisePreferred; + } + + /** + * Set whether the service uuid should be advertised. + * @hide + */ + public void setAdvertisePreferred(boolean advertisePreferred) { + this.mAdvertisePreferred = advertisePreferred; + } } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index abdf949ebcf..4b28516e15a 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -73,6 +73,9 @@ public final class BluetoothUuid { public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = + ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -211,4 +214,17 @@ public final class BluetoothUuid { long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; return (int)value; } + + /** + * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * @param parcelUuid + * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + */ + public static boolean isShortUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + } } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index df393dbb289..784cdcc5318 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,10 +37,15 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); - void clientListen(in int clientIf, in boolean start); - void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, - in boolean inclTxPower, in int minInterval, in int maxInterval, - in int appearance, in byte[] manufacturerData); + void startAdvertising(in int appIf); + void stopAdvertising(); + boolean setAdvServiceData(in byte[] serviceData); + byte[] getAdvServiceData(); + boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); + byte[] getAdvManufacturerData(); + List getAdvServiceUuids(); + void removeAdvManufacturerCodeAndData(int manufacturerCode); + boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, @@ -75,7 +80,7 @@ interface IBluetoothGatt { void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId); + in ParcelUuid srvcId, boolean advertisePreferred); void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId); void addCharacteristic(in int serverIf, in ParcelUuid charId, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 60c297b5979..e3563fcc3a7 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,5 +63,4 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onListen(in int status); } -- GitLab From 0ad591620a446d857fbae445b5c0badf3d366c4e Mon Sep 17 00:00:00 2001 From: Zhihai Xu Date: Mon, 20 Jan 2014 12:04:23 -0800 Subject: [PATCH 0312/1408] bluetoothsocket fd leak, need close the file descriptor after detachFd. Perform ParcelFileDescriptor close after detach to avoid bluetooth socket leaks in strict mode. bug:12647433 Change-Id: I22f422547b2fb33b9bf12065dc689ed7785a7269 --- framework/java/android/bluetooth/BluetoothSocket.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 22322e338ea..f532f7ce30a 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -457,8 +457,10 @@ public final class BluetoothSocket implements Closeable { mSocket.close(); mSocket = null; } - if(mPfd != null) - mPfd.detachFd(); + if (mPfd != null) { + mPfd.close(); + mPfd = null; + } } } } -- GitLab From d34191aaf9cc37a0e43e2f756448c317d36c29b1 Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Mon, 27 Jan 2014 16:27:04 -0800 Subject: [PATCH 0313/1408] BluetoothInputDevice: make REPORT_TYPE values match hal So we don't have to do conversion when using getReport/setReport and can pass the value to the BT HAL directly. Change-Id: I8d4af72d2e040d43cc9ff8a8cddfbe6cc8cfdb9a Signed-off-by: Mike J. Chen --- framework/java/android/bluetooth/BluetoothInputDevice.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 844f432ca32..bc3ad443d35 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -130,17 +130,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * @hide */ - public static final byte REPORT_TYPE_INPUT = 0; + public static final byte REPORT_TYPE_INPUT = 1; /** * @hide */ - public static final byte REPORT_TYPE_OUTPUT = 1; + public static final byte REPORT_TYPE_OUTPUT = 2; /** * @hide */ - public static final byte REPORT_TYPE_FEATURE = 2; + public static final byte REPORT_TYPE_FEATURE = 3; /** * @hide -- GitLab From b6ff8d3e137c032a92bc876331cf298819580119 Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Mon, 27 Jan 2014 17:55:40 -0800 Subject: [PATCH 0314/1408] BluetoothInputJava: Add broadcast action REPORT It's returned by getReport() when callback data is ready. Change-Id: I751c9e31de3d82a454a62cb8485a230e722b740d Signed-off-by: Mike J. Chen --- framework/java/android/bluetooth/BluetoothInputDevice.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index bc3ad443d35..33232edf58c 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -76,6 +76,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_PROTOCOL_MODE_CHANGED = "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REPORT = + "android.bluetooth.input.profile.action.REPORT"; /** * @hide -- GitLab From 7cc271a83d91f0893988d78b92d73ad3719c1502 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Tue, 25 Feb 2014 09:40:05 -0500 Subject: [PATCH 0315/1408] Tabs -> spaces in frameworks/base. Change-Id: I5a84e8e93ac99b5ed0212b37bf66efa5e53864be --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 546324a73e0..0d6f548139c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1005,7 +1005,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_OFF; // enable handleEnable(mQuietEnable); - } else if (mBinding || mBluetooth != null) { + } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; // if user is switched when service is being binding @@ -1014,7 +1014,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2); } - } + } break; } } -- GitLab From b3e6bd826a300caa0b4a2f42699fb450d23d4fd3 Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Tue, 4 Mar 2014 17:27:16 -0800 Subject: [PATCH 0316/1408] Add handshake broadcast support Also make a log message verbose only. Change-Id: I805ca376c0c0c37ec67897e5473a78fc943fdc63 Signed-off-by: Mike J. Chen --- .../android/bluetooth/BluetoothInputDevice.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 33232edf58c..c48b15d1024 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -76,6 +76,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_PROTOCOL_MODE_CHANGED = "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HANDSHAKE = + "android.bluetooth.input.profile.action.HANDSHAKE"; + /** * @hide */ @@ -183,6 +190,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT"; + /** + * @hide + */ + public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS"; + /** * @hide */ @@ -609,7 +621,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { - if (DBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); + if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.setReport(device, reportType, report); -- GitLab From 3364fc19ce4cb908b4e157a76297b29da8b7d927 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 29 Oct 2013 21:05:37 -0700 Subject: [PATCH 0317/1408] DO NOT MERGE BLE peripheral mode (3/4): Add peripheral mode API. Add bluetooth adv data, APIs in BluetoothAdpater etc. Cherry picking ble advertising changes from master to KLP MR2. b/13137996 Change-Id: Id9d2f566b6d9ed0fffe73b67efad2e3d045360b4 Conflicts: core/java/android/bluetooth/BluetoothAdapter.java core/java/android/bluetooth/BluetoothGatt.java Conflicts: core/java/android/bluetooth/BluetoothAdapter.java --- .../android/bluetooth/BluetoothAdapter.java | 210 +++++++++++++++++- .../bluetooth/BluetoothAdvScanData.java | 147 ++++++++++++ .../java/android/bluetooth/BluetoothGatt.java | 73 ------ .../bluetooth/BluetoothGattServer.java | 2 +- .../bluetooth/BluetoothGattService.java | 23 +- .../java/android/bluetooth/BluetoothUuid.java | 16 ++ .../android/bluetooth/IBluetoothGatt.aidl | 15 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 - 8 files changed, 394 insertions(+), 93 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAdvScanData.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e2bc80aad66..d1f1f2af44c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -27,14 +27,14 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; -import java.util.LinkedList; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Random; @@ -182,6 +182,43 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; + /** + * Activity Action: Show a system activity to request BLE advertising.
        + * If the device is not doing BLE advertising, this activity will start BLE advertising for the + * device, otherwise it will continue BLE advertising using the current + * {@link BluetoothAdvScanData}.
        + * Note this activity will also request the user to turn on Bluetooth if it's not currently + * enabled. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_START_ADVERTISING = + "android.bluetooth.adapter.action.START_ADVERTISING"; + + /** + * Activity Action: Stop the current BLE advertising. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_STOP_ADVERTISING = + "android.bluetooth.adapter.action.STOP_ADVERTISING"; + + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Activity Action: Show a system activity that allows the user to turn on * Bluetooth. @@ -251,7 +288,6 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -365,6 +401,8 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map mLeScanClients; + private BluetoothAdvScanData mBluetoothAdvScanData = null; + private GattCallbackWrapper mAdvertisingCallback; /** * Get a handle to the default local Bluetooth adapter. @@ -437,6 +475,97 @@ public final class BluetoothAdapter { address[0], address[1], address[2], address[3], address[4], address[5])); } + /** + * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * @hide + */ + public BluetoothAdvScanData getAdvScanData() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + Log.e(TAG, "failed to start, iGatt null"); + return null; + } + if (mBluetoothAdvScanData == null) { + mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); + } + return mBluetoothAdvScanData; + } catch (RemoteException e) { + Log.e(TAG, "failed to get advScanData, error: " + e); + return null; + } + } + + + /** + * Start BLE advertising using current {@link BluetoothAdvScanData}. + * An app should start advertising by requesting + * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @return true if BLE avertising succeeds, false otherwise. + * @hide + */ + public boolean startAdvertising() { + if (getState() != STATE_ON) return false; + + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported. + return false; + } + // Restart/reset advertising packets if advertising is in progress. + if (isAdvertising()) { + // Invalid advertising callback. + if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) { + Log.e(TAG, "failed to restart advertising, invalid callback"); + return false; + } + iGatt.startAdvertising(mAdvertisingCallback.mLeHandle); + return true; + } + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = + new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV); + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + mAdvertisingCallback = wrapper; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Stop BLE advertising. + * An app should stop advertising by requesting + * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * @return true if BLE advertising stops, false otherwise. + * @hide + */ + public boolean stopAdvertisting() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + if (mAdvertisingCallback == null) { + // no callback. + return false; + } + mAdvertisingCallback.stopAdvertising(); + mAdvertisingCallback = null; + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Return true if Bluetooth is currently enabled and ready for use. *

        Equivalent to: @@ -848,6 +977,23 @@ public final class BluetoothAdapter { return false; } + /** + * Returns whether BLE is currently advertising. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @hide + */ + public boolean isAdvertising() { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return iGatt.isAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. @@ -1546,8 +1692,12 @@ public final class BluetoothAdapter { private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + private static final int CALLBACK_TYPE_SCAN = 0; + private static final int CALLBACK_TYPE_ADV = 1; private final LeScanCallback mLeScanCb; + private int mCallbackType; + // mLeHandle 0: not registered // -1: scan stopped // >0: registered and scan started @@ -1561,6 +1711,16 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; + mCallbackType = CALLBACK_TYPE_SCAN; + } + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, + UUID[] uuid, int type) { + mBluetoothAdapter = new WeakReference(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + mCallbackType = type; } public boolean scanStarted() { @@ -1583,6 +1743,30 @@ public final class BluetoothAdapter { return started; } + public void stopAdvertising() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopAdvertising(); + Log.d(TAG, "unregeistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising and unregister" + e); + } + } else { + Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); + } + mLeHandle = -1; + notifyAll(); + } + } + public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1624,14 +1808,18 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); + if (mCallbackType == CALLBACK_TYPE_ADV) { + iGatt.startAdvertising(mLeHandle); } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -1642,7 +1830,7 @@ public final class BluetoothAdapter { mLeHandle = -1; } if (mLeHandle == -1) { - // registration succeeded but start scan failed + // registration succeeded but start scan or advertise failed if (iGatt != null) { try { iGatt.unregisterClient(mLeHandle); diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java new file mode 100644 index 00000000000..a97b0a806e3 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAdvScanData.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + + +/** + * This class provides the public APIs to set advertising and scan response data when BLE device + * operates in peripheral mode.
        + * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * @hide + */ +public final class BluetoothAdvScanData { + + /** + * Available data types of {@link BluetoothAdvScanData}. + */ + public static final int AD = 0; // Advertising Data + public static final int SCAN_RESPONSE = 1; // Scan Response + public static final int EIR = 2; // Extended Inquiry Response + + private static final String TAG = "BluetoothAdvScanData"; + + /** + * Data type of BluetoothAdvScanData. + */ + private final int mDataType; + /** + * Bluetooth Gatt Service. + */ + private IBluetoothGatt mBluetoothGatt; + + /** + * @param mBluetoothGatt + * @param dataType + */ + public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { + this.mBluetoothGatt = mBluetoothGatt; + this.mDataType = dataType; + } + + /** + * @return advertising data type. + */ + public int getDataType() { + return mDataType; + } + + /** + * Set manufactureCode and manufactureData. + * Returns true if manufacturer data is set, false if there is no enough room to set + * manufacturer data or the data is already set. + * @param manufacturerCode - unique identifier for the manufacturer + * @param manufacturerData - data associated with the specific manufacturer. + */ + public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { + if (mDataType != AD) return false; + try { + return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Set service data. Note the service data can only be set when the data type is {@code AD}; + * @param serviceData + */ + public boolean setServiceData(byte[] serviceData) { + + if (mDataType != AD) return false; + if (serviceData == null) return false; + try { + return mBluetoothGatt.setAdvServiceData(serviceData); + } catch (RemoteException e) { + return false; + } + } + + /** + * Returns an immutable list of service uuids that will be advertised. + */ + public List getServiceUuids() { + try { + return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns manufacturer data. + */ + public byte[] getManufacturerData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvManufacturerData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Returns service data. + */ + public byte[] getServiceData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvServiceData(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Remove manufacturer data based on given manufacturer code. + * @param manufacturerCode + */ + public void removeManufacturerCodeAndData(int manufacturerCode) { + if (mBluetoothGatt != null) { + try { + mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a2bb78c4c15..cd093c5a441 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -553,14 +553,6 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } - - /** - * Listen command status callback - * @hide - */ - public void onListen(int status) { - if (DBG) Log.d(TAG, "onListen() - status=" + status); - } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { @@ -693,71 +685,6 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } - /** - * Starts or stops sending of advertisement packages to listen for connection - * requests from a central devices. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param start Start or stop advertising - */ - /*package*/ void listen(boolean start) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); - } - if (DBG) Log.d(TAG, "listen() - start: " + start); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientListen(mClientIf, start); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Sets the advertising data contained in the adv. response packet. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param advData true to set adv. data, false to set scan response - * @param includeName Inlucde the name in the adv. response - * @param includeTxPower Include TX power value - * @param minInterval Minimum desired scan interval (optional) - * @param maxInterval Maximum desired scan interval (optional) - * @param appearance The appearance flags for the device (optional) - * @param manufacturerData Manufacturer specific data including company ID (optional) - */ - /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, - Integer minInterval, Integer maxInterval, - Integer appearance, Byte[] manufacturerData) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); - } - if (DBG) Log.d(TAG, "setAdvData()"); - if (mService == null || mClientIf == 0) return; - - byte[] data = new byte[0]; - if (manufacturerData != null) { - data = new byte[manufacturerData.length]; - for(int i = 0; i != manufacturerData.length; ++i) { - data[i] = manufacturerData[i]; - } - } - - try { - mService.setAdvData(mClientIf, !advData, - includeName, includeTxPower, - minInterval != null ? minInterval : 0, - maxInterval != null ? maxInterval : 0, - appearance != null ? appearance : 0, data); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 58ee54fdb10..153215cb525 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -537,7 +537,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.beginServiceDeclaration(mServerIf, service.getType(), service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid())); + new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); List includedServices = service.getIncludedServices(); for (BluetoothGattService includedService : includedServices) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 1e663696010..52bc0f796cb 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,8 +15,6 @@ */ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -81,6 +79,11 @@ public class BluetoothGattService { */ protected List mIncludedServices; + /** + * Whether the service uuid should be advertised. + */ + private boolean mAdvertisePreferred; + /** * Create a new BluetoothGattService. *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -263,4 +266,20 @@ public class BluetoothGattService { } return null; } + + /** + * Returns whether the uuid of the service should be advertised. + * @hide + */ + public boolean isAdvertisePreferred() { + return mAdvertisePreferred; + } + + /** + * Set whether the service uuid should be advertised. + * @hide + */ + public void setAdvertisePreferred(boolean advertisePreferred) { + this.mAdvertisePreferred = advertisePreferred; + } } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index abdf949ebcf..4b28516e15a 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -73,6 +73,9 @@ public final class BluetoothUuid { public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = + ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -211,4 +214,17 @@ public final class BluetoothUuid { long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; return (int)value; } + + /** + * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * @param parcelUuid + * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + */ + public static boolean isShortUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + } } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index df393dbb289..784cdcc5318 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,10 +37,15 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); - void clientListen(in int clientIf, in boolean start); - void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, - in boolean inclTxPower, in int minInterval, in int maxInterval, - in int appearance, in byte[] manufacturerData); + void startAdvertising(in int appIf); + void stopAdvertising(); + boolean setAdvServiceData(in byte[] serviceData); + byte[] getAdvServiceData(); + boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); + byte[] getAdvManufacturerData(); + List getAdvServiceUuids(); + void removeAdvManufacturerCodeAndData(int manufacturerCode); + boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, @@ -75,7 +80,7 @@ interface IBluetoothGatt { void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId); + in ParcelUuid srvcId, boolean advertisePreferred); void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId); void addCharacteristic(in int serverIf, in ParcelUuid charId, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 60c297b5979..e3563fcc3a7 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,5 +63,4 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onListen(in int status); } -- GitLab From ff51763220f24d110fdf5f48565219410fe02670 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 11 Mar 2014 22:22:41 -0700 Subject: [PATCH 0318/1408] Add status callback for start/stop advertising. Fixes b/13289050, b/13418851, also fixes 13418671. Change-Id: I231ba51aaa67b1f917e476ef0f2c8f82c762df77 --- .../android/bluetooth/BluetoothAdapter.java | 183 +++++++++++------- .../bluetooth/BluetoothAdvScanData.java | 7 +- .../java/android/bluetooth/BluetoothGatt.java | 11 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 + 4 files changed, 131 insertions(+), 71 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 38a71aaa25f..8aee4db08ca 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -19,7 +19,9 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; @@ -179,43 +181,6 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; - /** - * Activity Action: Show a system activity to request BLE advertising.
        - * If the device is not doing BLE advertising, this activity will start BLE advertising for the - * device, otherwise it will continue BLE advertising using the current - * {@link BluetoothAdvScanData}.
        - * Note this activity will also request the user to turn on Bluetooth if it's not currently - * enabled. - * @hide - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_START_ADVERTISING = - "android.bluetooth.adapter.action.START_ADVERTISING"; - - /** - * Activity Action: Stop the current BLE advertising. - * @hide - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_STOP_ADVERTISING = - "android.bluetooth.adapter.action.STOP_ADVERTISING"; - - /** - * Broadcast Action: Indicate BLE Advertising is started. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = - "android.bluetooth.adapter.action.ADVERTISING_STARTED"; - - /** - * Broadcast Action: Indicated BLE Advertising is stopped. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = - "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; - /** * Activity Action: Show a system activity that allows the user to turn on * Bluetooth. @@ -247,6 +212,22 @@ public final class BluetoothAdapter { public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the current scan mode. Possible values are: @@ -383,9 +364,27 @@ public final class BluetoothAdapter { /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; + /** States for Bluetooth LE advertising */ + /** @hide */ + public static final int STATE_ADVERTISE_STARTING = 0; + /** @hide */ + public static final int STATE_ADVERTISE_STARTED = 1; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPING = 2; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPED = 3; + /** + * Force stopping advertising without callback in case the advertising app dies. + * @hide + */ + public static final int STATE_ADVERTISE_FORCE_STOPPING = 4; + /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + /** @hide */ + public static final int ADVERTISE_CALLBACK_SUCCESS = 0; + private static final int ADDRESS_LENGTH = 17; /** @@ -399,7 +398,9 @@ public final class BluetoothAdapter { private final Map mLeScanClients; private BluetoothAdvScanData mBluetoothAdvScanData = null; - private GattCallbackWrapper mAdvertisingCallback; + private GattCallbackWrapper mAdvertisingGattCallback; + private final Handler mHandler; // Handler to post the advertise callback to run on main thread. + private final Object mLock = new Object(); /** * Get a handle to the default local Bluetooth adapter. @@ -435,6 +436,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap(); + mHandler = new Handler(Looper.getMainLooper()); } /** @@ -474,6 +476,7 @@ public final class BluetoothAdapter { /** * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * Data will be reset when bluetooth service is turned off. * @hide */ public BluetoothAdvScanData getAdvScanData() { @@ -494,19 +497,34 @@ public final class BluetoothAdapter { } } + /** + * Interface for BLE advertising callback. + * + * @hide + */ + public interface AdvertiseCallback { + /** + * Callback when advertise starts. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStart(int status); + /** + * Callback when advertise stops. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStop(int status); + } /** * Start BLE advertising using current {@link BluetoothAdvScanData}. - * An app should start advertising by requesting - * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} * - * @return true if BLE avertising succeeds, false otherwise. + * @param callback - {@link AdvertiseCallback} + * @return true if BLE advertising succeeds, false otherwise. * @hide */ - public boolean startAdvertising() { + public boolean startAdvertising(final AdvertiseCallback callback) { if (getState() != STATE_ON) return false; - try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { @@ -516,18 +534,31 @@ public final class BluetoothAdapter { // Restart/reset advertising packets if advertising is in progress. if (isAdvertising()) { // Invalid advertising callback. - if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) { + if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) { Log.e(TAG, "failed to restart advertising, invalid callback"); return false; } - iGatt.startAdvertising(mAdvertisingCallback.mLeHandle); + iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle); + // Run the callback from main thread. + mHandler.post(new Runnable() { + @Override + public void run() { + // callback with status success. + callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS); + } + }); return true; } UUID uuid = UUID.randomUUID(); GattCallbackWrapper wrapper = - new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV); + new GattCallbackWrapper(this, null, null, callback); iGatt.registerClient(new ParcelUuid(uuid), wrapper); - mAdvertisingCallback = wrapper; + if (!wrapper.advertiseStarted()) { + return false; + } + synchronized (mLock) { + mAdvertisingGattCallback = wrapper; + } return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -537,25 +568,29 @@ public final class BluetoothAdapter { /** * Stop BLE advertising. - * An app should stop advertising by requesting - * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @param callback - {@link AdvertiseCallback} * @return true if BLE advertising stops, false otherwise. * @hide */ - public boolean stopAdvertisting() { + public boolean stopAdvertising(AdvertiseCallback callback) { try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { // BLE is not supported return false; } - if (mAdvertisingCallback == null) { + if (mAdvertisingGattCallback == null) { // no callback. return false; } - mAdvertisingCallback.stopAdvertising(); - mAdvertisingCallback = null; + // Make sure same callback is used for start and stop advertising. + if (callback != mAdvertisingGattCallback.mAdvertiseCallback) { + Log.e(TAG, "must use the same callback for star/stop advertising"); + return false; + } + mAdvertisingGattCallback.stopAdvertising(); + mAdvertisingGattCallback = null; return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1415,6 +1450,8 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + // Reset bluetooth adv scan data when Gatt service is down. + mBluetoothAdvScanData = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1689,11 +1726,9 @@ public final class BluetoothAdapter { private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; - private static final int CALLBACK_TYPE_SCAN = 0; - private static final int CALLBACK_TYPE_ADV = 1; + private final AdvertiseCallback mAdvertiseCallback; private final LeScanCallback mLeScanCb; - private int mCallbackType; // mLeHandle 0: not registered // -1: scan stopped @@ -1708,26 +1743,34 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; - mCallbackType = CALLBACK_TYPE_SCAN; + mAdvertiseCallback = null; } public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, - UUID[] uuid, int type) { + UUID[] uuid, AdvertiseCallback callback) { mBluetoothAdapter = new WeakReference(bluetoothAdapter); mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; - mCallbackType = type; + mAdvertiseCallback = callback; } public boolean scanStarted() { + return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); + } + + public boolean advertiseStarted() { + // Wait for registeration callback. + return waitForRegisteration(1); + } + + private boolean waitForRegisteration(int maxWaitCount) { boolean started = false; synchronized(this) { if (mLeHandle == -1) return false; - int count = 0; // wait for callback registration and LE scan to start - while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + while (mLeHandle == 0 && count < maxWaitCount) { try { wait(LE_CALLBACK_REG_TIMEOUT); } catch (InterruptedException e) { @@ -1751,7 +1794,7 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); iGatt.stopAdvertising(); - Log.d(TAG, "unregeistering client " + mLeHandle); + Log.d(TAG, "unregistering client " + mLeHandle); iGatt.unregisterClient(mLeHandle); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising and unregister" + e); @@ -1805,7 +1848,7 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mCallbackType == CALLBACK_TYPE_ADV) { + if (mAdvertiseCallback != null) { iGatt.startAdvertising(mLeHandle); } else { if (mScanFilter == null) { @@ -1855,7 +1898,7 @@ public final class BluetoothAdapter { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); // Check null in case the scan has been stopped synchronized(this) { @@ -1944,9 +1987,13 @@ public final class BluetoothAdapter { // no op } - public void onListen(int status) { - // no op + public void onAdvertiseStateChange(int advertiseState, int status) { + Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); + if (advertiseState == STATE_ADVERTISE_STARTED) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + mAdvertiseCallback.onAdvertiseStop(status); + } } } - } diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java index a97b0a806e3..df2c25629a3 100644 --- a/framework/java/android/bluetooth/BluetoothAdvScanData.java +++ b/framework/java/android/bluetooth/BluetoothAdvScanData.java @@ -77,6 +77,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); } catch (RemoteException e) { + Log.e(TAG, "Unable to set manufacturer id and data.", e); return false; } } @@ -92,6 +93,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.setAdvServiceData(serviceData); } catch (RemoteException e) { + Log.e(TAG, "Unable to set service data.", e); return false; } } @@ -103,6 +105,7 @@ public final class BluetoothAdvScanData { try { return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service uuids.", e); return null; } } @@ -115,6 +118,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvManufacturerData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get manufacturer data.", e); return null; } } @@ -127,6 +131,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvServiceData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service data.", e); return null; } } @@ -140,7 +145,7 @@ public final class BluetoothAdvScanData { try { mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); } catch (RemoteException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e); } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index e3820a2e2f2..39305b05c5f 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; @@ -544,6 +542,15 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * Advertise state change callback + * @hide + */ + public void onAdvertiseStateChange(int state, int status) { + if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + + state + " status=" + status); + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index e3563fcc3a7..7c69a066b46 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,4 +63,5 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); + oneway void onAdvertiseStateChange(in int advertiseState, in int status); } -- GitLab From a00d04bb647962db3dcdfc891cbc58b4bc00a708 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 19 Mar 2014 15:47:12 -0700 Subject: [PATCH 0319/1408] Move advetise clean up to callback code. fixes b/13289050 Change-Id: Ibf3c772561125821817c947730cf21defafd4cb2 --- .../android/bluetooth/BluetoothAdapter.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8aee4db08ca..a4374b86f1a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -590,7 +590,6 @@ public final class BluetoothAdapter { return false; } mAdvertisingGattCallback.stopAdvertising(); - mAdvertisingGattCallback = null; return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1794,15 +1793,12 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); iGatt.stopAdvertising(); - Log.d(TAG, "unregistering client " + mLeHandle); - iGatt.unregisterClient(mLeHandle); } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising and unregister" + e); + Log.e(TAG, "Failed to stop advertising" + e); } } else { Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); } - mLeHandle = -1; notifyAll(); } } @@ -1992,6 +1988,26 @@ public final class BluetoothAdapter { if (advertiseState == STATE_ADVERTISE_STARTED) { mAdvertiseCallback.onAdvertiseStart(status); } else { + synchronized (this) { + if (status == ADVERTISE_CALLBACK_SUCCESS) { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = + adapter.getBluetoothManager().getBluetoothGatt(); + Log.d(TAG, "unregistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + // Reset advertise app handle. + mLeHandle = -1; + adapter.mAdvertisingGattCallback = null; + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister client" + e); + } + } else { + Log.e(TAG, "cannot unregister client, BluetoothAdapter is null"); + } + } + } mAdvertiseCallback.onAdvertiseStop(status); } } -- GitLab From abbeba65561fa82434c82964dc06e5fdb5b4d307 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 11 Mar 2014 22:22:41 -0700 Subject: [PATCH 0320/1408] DO NOT MERGE: Add status callback for start/stop advertising. Cherrypick from master to fix b/13289050 Change-Id: I231ba51aaa67b1f917e476ef0f2c8f82c762df77 Conflicts: core/java/android/bluetooth/BluetoothAdapter.java core/java/android/bluetooth/BluetoothGatt.java --- .../android/bluetooth/BluetoothAdapter.java | 183 +++++++++++------- .../bluetooth/BluetoothAdvScanData.java | 7 +- .../java/android/bluetooth/BluetoothGatt.java | 9 + .../bluetooth/IBluetoothGattCallback.aidl | 1 + 4 files changed, 131 insertions(+), 69 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d1f1f2af44c..f973d843ea0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,7 +20,9 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; @@ -182,43 +184,6 @@ public final class BluetoothAdapter { public static final String EXTRA_DISCOVERABLE_DURATION = "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; - /** - * Activity Action: Show a system activity to request BLE advertising.
        - * If the device is not doing BLE advertising, this activity will start BLE advertising for the - * device, otherwise it will continue BLE advertising using the current - * {@link BluetoothAdvScanData}.
        - * Note this activity will also request the user to turn on Bluetooth if it's not currently - * enabled. - * @hide - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_START_ADVERTISING = - "android.bluetooth.adapter.action.START_ADVERTISING"; - - /** - * Activity Action: Stop the current BLE advertising. - * @hide - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_STOP_ADVERTISING = - "android.bluetooth.adapter.action.STOP_ADVERTISING"; - - /** - * Broadcast Action: Indicate BLE Advertising is started. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = - "android.bluetooth.adapter.action.ADVERTISING_STARTED"; - - /** - * Broadcast Action: Indicated BLE Advertising is stopped. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = - "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; - /** * Activity Action: Show a system activity that allows the user to turn on * Bluetooth. @@ -250,6 +215,22 @@ public final class BluetoothAdapter { public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; + /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the current scan mode. Possible values are: @@ -386,9 +367,27 @@ public final class BluetoothAdapter { /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; + /** States for Bluetooth LE advertising */ + /** @hide */ + public static final int STATE_ADVERTISE_STARTING = 0; + /** @hide */ + public static final int STATE_ADVERTISE_STARTED = 1; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPING = 2; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPED = 3; + /** + * Force stopping advertising without callback in case the advertising app dies. + * @hide + */ + public static final int STATE_ADVERTISE_FORCE_STOPPING = 4; + /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + /** @hide */ + public static final int ADVERTISE_CALLBACK_SUCCESS = 0; + private static final int ADDRESS_LENGTH = 17; /** @@ -402,7 +401,9 @@ public final class BluetoothAdapter { private final Map mLeScanClients; private BluetoothAdvScanData mBluetoothAdvScanData = null; - private GattCallbackWrapper mAdvertisingCallback; + private GattCallbackWrapper mAdvertisingGattCallback; + private final Handler mHandler; // Handler to post the advertise callback to run on main thread. + private final Object mLock = new Object(); /** * Get a handle to the default local Bluetooth adapter. @@ -438,6 +439,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap(); + mHandler = new Handler(Looper.getMainLooper()); } /** @@ -477,6 +479,7 @@ public final class BluetoothAdapter { /** * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * Data will be reset when bluetooth service is turned off. * @hide */ public BluetoothAdvScanData getAdvScanData() { @@ -497,19 +500,34 @@ public final class BluetoothAdapter { } } + /** + * Interface for BLE advertising callback. + * + * @hide + */ + public interface AdvertiseCallback { + /** + * Callback when advertise starts. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStart(int status); + /** + * Callback when advertise stops. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStop(int status); + } /** * Start BLE advertising using current {@link BluetoothAdvScanData}. - * An app should start advertising by requesting - * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} * - * @return true if BLE avertising succeeds, false otherwise. + * @param callback - {@link AdvertiseCallback} + * @return true if BLE advertising succeeds, false otherwise. * @hide */ - public boolean startAdvertising() { + public boolean startAdvertising(final AdvertiseCallback callback) { if (getState() != STATE_ON) return false; - try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { @@ -519,18 +537,31 @@ public final class BluetoothAdapter { // Restart/reset advertising packets if advertising is in progress. if (isAdvertising()) { // Invalid advertising callback. - if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) { + if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) { Log.e(TAG, "failed to restart advertising, invalid callback"); return false; } - iGatt.startAdvertising(mAdvertisingCallback.mLeHandle); + iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle); + // Run the callback from main thread. + mHandler.post(new Runnable() { + @Override + public void run() { + // callback with status success. + callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS); + } + }); return true; } UUID uuid = UUID.randomUUID(); GattCallbackWrapper wrapper = - new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV); + new GattCallbackWrapper(this, null, null, callback); iGatt.registerClient(new ParcelUuid(uuid), wrapper); - mAdvertisingCallback = wrapper; + if (!wrapper.advertiseStarted()) { + return false; + } + synchronized (mLock) { + mAdvertisingGattCallback = wrapper; + } return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -540,25 +571,29 @@ public final class BluetoothAdapter { /** * Stop BLE advertising. - * An app should stop advertising by requesting - * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @param callback - {@link AdvertiseCallback} * @return true if BLE advertising stops, false otherwise. * @hide */ - public boolean stopAdvertisting() { + public boolean stopAdvertising(AdvertiseCallback callback) { try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { // BLE is not supported return false; } - if (mAdvertisingCallback == null) { + if (mAdvertisingGattCallback == null) { // no callback. return false; } - mAdvertisingCallback.stopAdvertising(); - mAdvertisingCallback = null; + // Make sure same callback is used for start and stop advertising. + if (callback != mAdvertisingGattCallback.mAdvertiseCallback) { + Log.e(TAG, "must use the same callback for star/stop advertising"); + return false; + } + mAdvertisingGattCallback.stopAdvertising(); + mAdvertisingGattCallback = null; return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1418,6 +1453,8 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + // Reset bluetooth adv scan data when Gatt service is down. + mBluetoothAdvScanData = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1692,11 +1729,9 @@ public final class BluetoothAdapter { private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; - private static final int CALLBACK_TYPE_SCAN = 0; - private static final int CALLBACK_TYPE_ADV = 1; + private final AdvertiseCallback mAdvertiseCallback; private final LeScanCallback mLeScanCb; - private int mCallbackType; // mLeHandle 0: not registered // -1: scan stopped @@ -1711,26 +1746,34 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; - mCallbackType = CALLBACK_TYPE_SCAN; + mAdvertiseCallback = null; } public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, - UUID[] uuid, int type) { + UUID[] uuid, AdvertiseCallback callback) { mBluetoothAdapter = new WeakReference(bluetoothAdapter); mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; - mCallbackType = type; + mAdvertiseCallback = callback; } public boolean scanStarted() { + return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); + } + + public boolean advertiseStarted() { + // Wait for registeration callback. + return waitForRegisteration(1); + } + + private boolean waitForRegisteration(int maxWaitCount) { boolean started = false; synchronized(this) { if (mLeHandle == -1) return false; - int count = 0; // wait for callback registration and LE scan to start - while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + while (mLeHandle == 0 && count < maxWaitCount) { try { wait(LE_CALLBACK_REG_TIMEOUT); } catch (InterruptedException e) { @@ -1754,7 +1797,7 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); iGatt.stopAdvertising(); - Log.d(TAG, "unregeistering client " + mLeHandle); + Log.d(TAG, "unregistering client " + mLeHandle); iGatt.unregisterClient(mLeHandle); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising and unregister" + e); @@ -1808,7 +1851,7 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mCallbackType == CALLBACK_TYPE_ADV) { + if (mAdvertiseCallback != null) { iGatt.startAdvertising(mLeHandle); } else { if (mScanFilter == null) { @@ -1858,7 +1901,7 @@ public final class BluetoothAdapter { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); // Check null in case the scan has been stopped synchronized(this) { @@ -1947,9 +1990,13 @@ public final class BluetoothAdapter { // no op } - public void onListen(int status) { - // no op + public void onAdvertiseStateChange(int advertiseState, int status) { + Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); + if (advertiseState == STATE_ADVERTISE_STARTED) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + mAdvertiseCallback.onAdvertiseStop(status); + } } } - } diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java index a97b0a806e3..df2c25629a3 100644 --- a/framework/java/android/bluetooth/BluetoothAdvScanData.java +++ b/framework/java/android/bluetooth/BluetoothAdvScanData.java @@ -77,6 +77,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); } catch (RemoteException e) { + Log.e(TAG, "Unable to set manufacturer id and data.", e); return false; } } @@ -92,6 +93,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.setAdvServiceData(serviceData); } catch (RemoteException e) { + Log.e(TAG, "Unable to set service data.", e); return false; } } @@ -103,6 +105,7 @@ public final class BluetoothAdvScanData { try { return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service uuids.", e); return null; } } @@ -115,6 +118,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvManufacturerData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get manufacturer data.", e); return null; } } @@ -127,6 +131,7 @@ public final class BluetoothAdvScanData { try { return mBluetoothGatt.getAdvServiceData(); } catch (RemoteException e) { + Log.e(TAG, "Unable to get service data.", e); return null; } } @@ -140,7 +145,7 @@ public final class BluetoothAdvScanData { try { mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); } catch (RemoteException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e); } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index cd093c5a441..ae6ad3b4f26 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -553,6 +553,15 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * Advertise state change callback + * @hide + */ + public void onAdvertiseStateChange(int state, int status) { + if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + + state + " status=" + status); + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index e3563fcc3a7..7c69a066b46 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,4 +63,5 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); + oneway void onAdvertiseStateChange(in int advertiseState, in int status); } -- GitLab From da4a111edbd716f8b27831b4975d772bca8ac4d7 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 19 Mar 2014 15:47:12 -0700 Subject: [PATCH 0321/1408] DO NOT MERGE: Move advetise clean up to callback code. fixes b/13289050 Cherrypick from master to fix b/13289050 Change-Id: Ibf3c772561125821817c947730cf21defafd4cb2 --- .../android/bluetooth/BluetoothAdapter.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f973d843ea0..75b007c75ce 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -593,7 +593,6 @@ public final class BluetoothAdapter { return false; } mAdvertisingGattCallback.stopAdvertising(); - mAdvertisingGattCallback = null; return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1797,15 +1796,12 @@ public final class BluetoothAdapter { try { IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); iGatt.stopAdvertising(); - Log.d(TAG, "unregistering client " + mLeHandle); - iGatt.unregisterClient(mLeHandle); } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising and unregister" + e); + Log.e(TAG, "Failed to stop advertising" + e); } } else { Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); } - mLeHandle = -1; notifyAll(); } } @@ -1995,6 +1991,26 @@ public final class BluetoothAdapter { if (advertiseState == STATE_ADVERTISE_STARTED) { mAdvertiseCallback.onAdvertiseStart(status); } else { + synchronized (this) { + if (status == ADVERTISE_CALLBACK_SUCCESS) { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = + adapter.getBluetoothManager().getBluetoothGatt(); + Log.d(TAG, "unregistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + // Reset advertise app handle. + mLeHandle = -1; + adapter.mAdvertisingGattCallback = null; + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister client" + e); + } + } else { + Log.e(TAG, "cannot unregister client, BluetoothAdapter is null"); + } + } + } mAdvertiseCallback.onAdvertiseStop(status); } } -- GitLab From 4ebffef0752d467f15199f1626d0a55648a17f9b Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 19 Mar 2014 18:06:58 -0700 Subject: [PATCH 0322/1408] Get rid of not specifying a user errors in bluetooth. W/ContextImpl( 1772): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1559 android.content.ContextWrapper.bindService:513 android.bluetooth.BluetoothInputDevice.doBind:262 android.bluetooth.BluetoothInputDevice.:255 android.bluetooth.BluetoothAdapter.getProfileProxy:1365 W/ContextImpl( 1772): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1559 android.content.ContextWrapper.bindService:513 android.bluetooth.BluetoothPan.doBind:148 android.bluetooth.BluetoothPan.:140 android.bluetooth.BluetoothAdapter.getProfileProxy:1368 W/ContextImpl( 1772): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1559 android.content.ContextWrapper.bindService:513 android.bluetooth.BluetoothMap.doBind:108 android.bluetooth.BluetoothMap.:101 android.bluetooth.BluetoothAdapter.getProfileProxy:1374 W/ContextImpl( 1772): Calling a method in the system process without a qualified user: android.app.ContextImpl.bindService:1559 android.content.ContextWrapper.bindService:513 android.bluetooth.BluetoothPbap.doBind:163 android.bluetooth.BluetoothPbap.:156 com.android.settings.bluetooth.PbapServerProfile.:68 Change-Id: I0a1e24ee71aef7d796fcee5692b9d19462a93637 --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- framework/java/android/bluetooth/BluetoothHeadset.java | 3 ++- framework/java/android/bluetooth/BluetoothHealth.java | 3 ++- framework/java/android/bluetooth/BluetoothInputDevice.java | 3 ++- framework/java/android/bluetooth/BluetoothMap.java | 6 +++--- framework/java/android/bluetooth/BluetoothPan.java | 3 ++- framework/java/android/bluetooth/BluetoothPbap.java | 3 ++- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 6f929f261ae..7b709acd911 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -162,7 +162,8 @@ public final class BluetoothA2dp implements BluetoothProfile { Intent intent = new Intent(IBluetoothA2dp.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8ee955d2379..f88a173908c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -280,7 +280,8 @@ public final class BluetoothHeadset implements BluetoothProfile { Intent intent = new Intent(IBluetoothHeadset.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index daf3bad3774..4949c24372a 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -487,7 +487,8 @@ public final class BluetoothHealth implements BluetoothProfile { Intent intent = new Intent(IBluetoothHealth.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 333f825fe47..554df3e4554 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -259,7 +259,8 @@ public final class BluetoothInputDevice implements BluetoothProfile { Intent intent = new Intent(IBluetoothInputDevice.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 5a1b7aa818c..7f57acf3dc6 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -22,8 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; -import android.os.IBinder; +import android.os.*; import android.util.Log; /** @@ -105,7 +104,8 @@ public final class BluetoothMap implements BluetoothProfile { Intent intent = new Intent(IBluetoothMap.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index e72832c531c..4f81f9863c5 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -145,7 +145,8 @@ public final class BluetoothPan implements BluetoothProfile { Intent intent = new Intent(IBluetoothPan.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 8522ee016b7..dc01fc7a949 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -160,7 +160,8 @@ public class BluetoothPbap { Intent intent = new Intent(IBluetoothPbap.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !mContext.bindService(intent, mConnection, 0)) { + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); return false; } -- GitLab From 91057ef842c47e70f4b97fafc950bed6c4382f03 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 25 Mar 2014 06:31:50 -0700 Subject: [PATCH 0323/1408] LE: Add API to configure MTU for a given connection (3/4) bug:13571470 Change-Id: I3619617eaf864701a35f7802bc71805784d768d0 --- .../android/bluetooth/BluetoothAdapter.java | 8 ++++ .../java/android/bluetooth/BluetoothGatt.java | 47 +++++++++++++++++++ .../bluetooth/BluetoothGattCallback.java | 15 ++++++ .../android/bluetooth/IBluetoothGatt.aidl | 1 + .../bluetooth/IBluetoothGattCallback.aidl | 1 + 5 files changed, 72 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 75b007c75ce..182ef03c599 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2014,5 +2014,13 @@ public final class BluetoothAdapter { mAdvertiseCallback.onAdvertiseStop(status); } } + + /** + * Callback reporting LE ATT MTU. + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ae6ad3b4f26..bbd0439e51a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -561,6 +561,23 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); + } + + /** + * Callback invoked when the MTU for a given connection changes + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address + + " mtu=" + mtu + " status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } + try { + mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } }; @@ -1147,6 +1164,36 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Configure the MTU used for a given connection. + * + *

        When performing a write request operation (write without response), + * the data sent is truncated to the MTU size. This function may be used + * to request a larget MTU size to be able to send more data at once. + * + *

        A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate + * whether this operation was successful. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the new MTU value has been requested successfully + * @hide + */ + public boolean configureMTU(int mtu) { + if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() + + " mtu: " + mtu); + if (mService == null || mClientIf == 0) return false; + + try { + mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + /** * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 80ea4a69569..5180259cc12 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -138,4 +138,19 @@ public abstract class BluetoothGattCallback { */ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#configureMTU} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#configureMTU} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + * @hide + */ + public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { + } } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 784cdcc5318..c6b5c3d309a 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -73,6 +73,7 @@ interface IBluetoothGatt { void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); + void configureMTU(in int clientIf, in String address, in int mtu); void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 7c69a066b46..a78c29b0064 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,4 +64,5 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); + void onConfigureMTU(in String address, in int mtu, in int status); } -- GitLab From d02986b29fa12e91de202f185de1f718a1c2ad7d Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 27 Mar 2014 19:34:46 -0700 Subject: [PATCH 0324/1408] Add comment of using same callback for start/stop advertising. Change-Id: Ice268e83e4f2ceb5053a0e03f73b877f548bd13b --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 75b007c75ce..229bcbffec3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -570,7 +570,7 @@ public final class BluetoothAdapter { } /** - * Stop BLE advertising. + * Stop BLE advertising. The callback has to be the same one used for start advertising. * * @param callback - {@link AdvertiseCallback} * @return true if BLE advertising stops, false otherwise. -- GitLab From e0811cc380dbe9c2bb34c54af7848a3f1b3b81af Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 28 Mar 2014 18:40:29 -0700 Subject: [PATCH 0325/1408] Callback on correct method when status is unsuccessful. Change-Id: I63c07bbae559765af1aecb492379ab18268336d8 --- .../java/android/bluetooth/BluetoothAdapter.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 229bcbffec3..63ebb63944b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1989,7 +1989,13 @@ public final class BluetoothAdapter { public void onAdvertiseStateChange(int advertiseState, int status) { Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); if (advertiseState == STATE_ADVERTISE_STARTED) { - mAdvertiseCallback.onAdvertiseStart(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + // If status is unsuccessful and advertise state is started, it means stop + // advertising fails. + mAdvertiseCallback.onAdvertiseStop(status); + } } else { synchronized (this) { if (status == ADVERTISE_CALLBACK_SUCCESS) { @@ -2011,7 +2017,13 @@ public final class BluetoothAdapter { } } } - mAdvertiseCallback.onAdvertiseStop(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStop(status); + } else{ + // If status is unsuccesful and advertise state is stopped, it means start + // advertising fails. + mAdvertiseCallback.onAdvertiseStart(status); + } } } } -- GitLab From e3171371fbd9286fd7dc6014df8853431ca9555c Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 18 Mar 2014 14:26:51 -0700 Subject: [PATCH 0326/1408] LE: Return false if an attribute read/write is in progress bug:12109128 Change-Id: I0155fcddc850aba47a1bf3e174d4ee582d50b187 --- .../java/android/bluetooth/BluetoothGatt.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index bbd0439e51a..e7ab8de25bc 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -61,6 +61,7 @@ public final class BluetoothGatt implements BluetoothProfile { private boolean mAutoConnect; private int mConnState; private final Object mStateLock = new Object(); + private Boolean mDeviceBusy = false; private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; @@ -177,6 +178,10 @@ public final class BluetoothGatt implements BluetoothProfile { mConnState = CONN_STATE_IDLE; } } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } } /** @@ -312,6 +317,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) && mAuthRetry == false) { @@ -359,6 +369,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -436,6 +451,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -485,6 +505,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -530,6 +555,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + try { mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); } catch (Exception ex) { @@ -862,6 +892,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.readCharacteristic(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), @@ -869,6 +904,7 @@ public final class BluetoothGatt implements BluetoothProfile { new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -901,6 +937,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.writeCharacteristic(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), @@ -910,6 +951,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -941,6 +983,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), new ParcelUuid(service.getUuid()), @@ -949,6 +996,7 @@ public final class BluetoothGatt implements BluetoothProfile { AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -979,6 +1027,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), new ParcelUuid(service.getUuid()), @@ -988,6 +1041,7 @@ public final class BluetoothGatt implements BluetoothProfile { descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -1045,10 +1099,16 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } -- GitLab From b27c3c9edfb885c861b58032cc5f1934693304d2 Mon Sep 17 00:00:00 2001 From: Ariel Gertzenstein Date: Wed, 2 Apr 2014 20:36:57 -0700 Subject: [PATCH 0327/1408] DO NOT MERGE Tear down PAN if we can't successfully complete a DHCP request after establishing a PANU connection. http://b/13731957 Change-Id: I0c808d5196c66783b52942ba7b7212bdce9f1c07 --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index a9b71769335..7745bb7bccd 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -61,6 +61,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final boolean DBG = true; private static final boolean VDBG = true; + // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. + private static final int EVENT_NETWORK_FAILED = 1; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); @@ -328,6 +331,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } if (!success) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); return; } mLinkProperties = dhcpResults.linkProperties; @@ -420,6 +424,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); mBtdt.stopReverseTether(); break; + case EVENT_NETWORK_FAILED: + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); + mBtdt.teardown(); + break; } } } -- GitLab From 77718147262500640ddb8d50a5c189130dbc16ad Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Mon, 21 Apr 2014 19:06:17 -0700 Subject: [PATCH 0328/1408] add bt instrumentation to run as a util some bt test util methods are useful as device setup util, this change exposes some of them via a BluetoothInstrumentation so that they can be invoked like commands for setting up or controlling bt state in test harness Change-Id: I1d40baf374e145ab550ae4fa5f581e2eaf2e29bb --- framework/tests/AndroidManifest.xml | 3 + .../bluetooth/BluetoothInstrumentation.java | 96 +++++++++++++++++++ .../android/bluetooth/BluetoothTestUtils.java | 12 +++ 3 files changed, 111 insertions(+) create mode 100644 framework/tests/src/android/bluetooth/BluetoothInstrumentation.java diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index 60b6dc18f0c..cbfa84d0ef8 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -31,5 +31,8 @@ + diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java new file mode 100644 index 00000000000..34393f98ae4 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 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.Activity; +import android.app.Instrumentation; +import android.content.Context; +import android.os.Bundle; + +public class BluetoothInstrumentation extends Instrumentation { + + private BluetoothTestUtils mUtils = null; + private BluetoothAdapter mAdapter = null; + private Bundle mArgs = null; + + private BluetoothTestUtils getBluetoothTestUtils() { + if (mUtils == null) { + mUtils = new BluetoothTestUtils(getContext(), + BluetoothInstrumentation.class.getSimpleName()); + } + return mUtils; + } + + private BluetoothAdapter getBluetoothAdapter() { + if (mAdapter == null) { + mAdapter = ((BluetoothManager)getContext().getSystemService( + Context.BLUETOOTH_SERVICE)).getAdapter(); + } + return mAdapter; + } + + @Override + public void onCreate(Bundle arguments) { + super.onCreate(arguments); + mArgs = arguments; + start(); + } + + @Override + public void onStart() { + String command = mArgs.getString("command"); + if ("enable".equals(command)) { + enable(); + } else if ("disable".equals(command)) { + disable(); + } else if ("unpairAll".equals(command)) { + unpairAll(); + } else if ("getName".equals(command)) { + getName(); + } else { + finish(null); + } + } + + public void enable() { + getBluetoothTestUtils().enable(getBluetoothAdapter()); + finish(null); + } + + public void disable() { + getBluetoothTestUtils().disable(getBluetoothAdapter()); + finish(null); + } + + public void unpairAll() { + getBluetoothTestUtils().unpairAll(getBluetoothAdapter()); + finish(null); + } + + public void getName() { + String name = getBluetoothAdapter().getName(); + Bundle bundle = new Bundle(); + bundle.putString("name", name); + finish(bundle); + } + + public void finish(Bundle result) { + if (result == null) { + result = new Bundle(); + } + finish(Activity.RESULT_OK, result); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 4858be8c2a8..8fbd2147c9d 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -34,6 +34,7 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Set; public class BluetoothTestUtils extends Assert { @@ -892,6 +893,17 @@ public class BluetoothTestUtils extends Assert { methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask)); } + /** + * Deletes all pairings of remote devices + * @param adapter the BT adapter + */ + public void unpairAll(BluetoothAdapter adapter) { + Set devices = adapter.getBondedDevices(); + for (BluetoothDevice device : devices) { + unpair(adapter, device); + } + } + /** * Connects a profile from the local device to a remote device and checks to make sure that the * profile is connected and that the correct actions were broadcast. -- GitLab From 07abb66ec870de44641ade0548865a60fcda16e7 Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Mon, 28 Apr 2014 20:12:35 -0700 Subject: [PATCH 0329/1408] make success explicit in BT instrumentation util Change-Id: I6c8eb8cdb7928fa6a7c1bc96b374667d1d6ae6a1 --- .../bluetooth/BluetoothInstrumentation.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 34393f98ae4..0cd19f26e0a 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -25,6 +25,7 @@ public class BluetoothInstrumentation extends Instrumentation { private BluetoothTestUtils mUtils = null; private BluetoothAdapter mAdapter = null; private Bundle mArgs = null; + private Bundle mSuccessResult = null; private BluetoothTestUtils getBluetoothTestUtils() { if (mUtils == null) { @@ -46,6 +47,9 @@ public class BluetoothInstrumentation extends Instrumentation { public void onCreate(Bundle arguments) { super.onCreate(arguments); mArgs = arguments; + // create the default result response, but only use it in success code path + mSuccessResult = new Bundle(); + mSuccessResult.putString("result", "SUCCESS"); start(); } @@ -67,24 +71,23 @@ public class BluetoothInstrumentation extends Instrumentation { public void enable() { getBluetoothTestUtils().enable(getBluetoothAdapter()); - finish(null); + finish(mSuccessResult); } public void disable() { getBluetoothTestUtils().disable(getBluetoothAdapter()); - finish(null); + finish(mSuccessResult); } public void unpairAll() { getBluetoothTestUtils().unpairAll(getBluetoothAdapter()); - finish(null); + finish(mSuccessResult); } public void getName() { String name = getBluetoothAdapter().getName(); - Bundle bundle = new Bundle(); - bundle.putString("name", name); - finish(bundle); + mSuccessResult.putString("name", name); + finish(mSuccessResult); } public void finish(Bundle result) { -- GitLab From 24fe135adce66ec47af8c7c5e3379e27ab71bf05 Mon Sep 17 00:00:00 2001 From: Natalie Silvanovich Date: Thu, 1 May 2014 16:12:23 -0700 Subject: [PATCH 0330/1408] Null checks in register/unregister BT Adapters Prevents system crash Bug: 13743852 Change-Id: I05bcb31fc8377866b93899a01c004d15e041f21f --- .../android/server/bluetooth/BluetoothManagerService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0d6f548139c..f22020f575d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -307,6 +307,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ + if (callback == null) { + Log.w(TAG, "Callback is null in registerAdapter"); + return null; + } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); msg.obj = callback; mHandler.sendMessage(msg); @@ -316,6 +320,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void unregisterAdapter(IBluetoothManagerCallback callback) { + if (callback == null) { + Log.w(TAG, "Callback is null in unregisterAdapter"); + return; + } mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); -- GitLab From 848ee54edbe5fa7a51be9d48c83b3f0579558acc Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Fri, 2 May 2014 12:38:51 -0700 Subject: [PATCH 0331/1408] add getAddress function to return BT mac Change-Id: Ib2bcd123e066672e411550856db862059949100e --- .../src/android/bluetooth/BluetoothInstrumentation.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 0cd19f26e0a..67203b261b4 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -64,6 +64,8 @@ public class BluetoothInstrumentation extends Instrumentation { unpairAll(); } else if ("getName".equals(command)) { getName(); + } else if ("getAddress".equals(command)) { + getAddress(); } else { finish(null); } @@ -90,6 +92,12 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } + public void getAddress() { + String name = getBluetoothAdapter().getAddress(); + mSuccessResult.putString("address", name); + finish(mSuccessResult); + } + public void finish(Bundle result) { if (result == null) { result = new Bundle(); -- GitLab From 229d7fa1f8427ceacd3704e349b0f754b5a05f3a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 2 May 2014 16:28:33 -0700 Subject: [PATCH 0332/1408] Bump up priority of system receiving BOOT_COMPLETED. Change-Id: I5166f88f11f781914312e867cb653c8ecbefa705 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0d6f548139c..e2a8ca2b99e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -212,6 +212,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(Intent.ACTION_USER_SWITCHED); registerForAirplaneMode(filter); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { -- GitLab From 8b3a52fb341f9c13b184733f1314e9374eb6c2ae Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Tue, 8 Apr 2014 14:51:15 -0700 Subject: [PATCH 0333/1408] Make sure BluetoothSocket#connect throws on error. It sometimes fails silently, resulting in callers using the socket even though it hasn't been initialized. http://b/13909270 Change-Id: Ied08982b51d44c3d2dec72785888ea6c6497a664 --- framework/java/android/bluetooth/BluetoothSocket.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index f532f7ce30a..00fd7ced189 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -325,6 +325,7 @@ public final class BluetoothSocket implements Closeable { } } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); + throw new IOException("unable to send RPC: " + e.getMessage()); } } -- GitLab From c38680442471fa7e45d09303d3d7c66c500b5dff Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 15 Apr 2014 14:57:16 -0700 Subject: [PATCH 0334/1408] Add parse method to parse UUID from bytes for 16 bit, 32 bit and 128 bit Bluetooth UUIDs. Added unit tests. Change-Id: I4ecf0ede02561a5e9815acf8a5a1309d5756b887 --- .../java/android/bluetooth/BluetoothUuid.java | 63 +++++++++++++++++-- .../android/bluetooth/BluetoothUuidTest.java | 50 +++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 framework/tests/src/android/bluetooth/BluetoothUuidTest.java diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 4b28516e15a..ab53fb050dd 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.os.ParcelUuid; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashSet; import java.util.UUID; @@ -76,6 +78,12 @@ public final class BluetoothUuid { public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + /** Length of bytes for 16 bit UUID */ + public static final int UUID_BYTES_16_BIT = 2; + /** Length of bytes for 32 bit UUID */ + public static final int UUID_BYTES_32_BIT = 4; + /** Length of bytes for 128 bit UUID */ + public static final int UUID_BYTES_128_BIT = 16; public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -215,16 +223,61 @@ public final class BluetoothUuid { return (int)value; } + /** + * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, + * but the returned UUID is always in 128-bit format. + * Note UUID is little endian in Bluetooth. + * + * @param uuidBytes Byte representation of uuid. + * @return {@link ParcelUuid} parsed from bytes. + * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. + */ + public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { + if (uuidBytes == null) { + throw new IllegalArgumentException("uuidBytes cannot be null"); + } + int length = uuidBytes.length; + if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT && + length != UUID_BYTES_128_BIT) { + throw new IllegalArgumentException("uuidBytes length invalid - " + length); + } + + // Construct a 128 bit UUID. + if (length == UUID_BYTES_128_BIT) { + ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); + long msb = buf.getLong(8); + long lsb = buf.getLong(0); + return new ParcelUuid(new UUID(msb, lsb)); + } + + // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. + // 128_bit_value = uuid * 2^96 + BASE_UUID + long shortUuid; + if (length == UUID_BYTES_16_BIT) { + shortUuid = uuidBytes[0] & 0xFF; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + } else { + shortUuid = uuidBytes[0] & 0xFF ; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + shortUuid += (uuidBytes[2] & 0xFF) << 16; + shortUuid += (uuidBytes[3] & 0xFF) << 24; + } + long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); + long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); + return new ParcelUuid(new UUID(msb, lsb)); + } + /** * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ public static boolean isShortUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { - return false; - } - return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); } } diff --git a/framework/tests/src/android/bluetooth/BluetoothUuidTest.java b/framework/tests/src/android/bluetooth/BluetoothUuidTest.java new file mode 100644 index 00000000000..0f3ea0ec000 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothUuidTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 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.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothUuid}. + *

        + * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothUuidTest extends TestCase { + + @SmallTest + public void testUuidParser() { + byte[] uuid16 = new byte[] { + 0x0B, 0x11 }; + assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid16)); + + byte[] uuid32 = new byte[] { + 0x0B, 0x11, 0x33, (byte) 0xFE }; + assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid32)); + + byte[] uuid128 = new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF }; + assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"), + BluetoothUuid.parseUuidFrom(uuid128)); + } +} -- GitLab From d4c0158b93a98c883245f6abdefb1bf2b828f179 Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Wed, 2 Apr 2014 20:36:57 -0700 Subject: [PATCH 0335/1408] Tear down PAN if we can't successfully complete a DHCP request after establishing a PANU connection. http://b/13731957 Change-Id: I0c808d5196c66783b52942ba7b7212bdce9f1c07 --- .../android/bluetooth/BluetoothTetheringDataTracker.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index 6dd551e8525..a0b603e7864 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -53,6 +53,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final boolean DBG = true; private static final boolean VDBG = true; + // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. + private static final int EVENT_NETWORK_FAILED = 1; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); @@ -315,6 +318,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } if (!success) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); return; } mLinkProperties = dhcpResults.linkProperties; @@ -407,6 +411,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); mBtdt.stopReverseTether(); break; + case EVENT_NETWORK_FAILED: + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); + mBtdt.teardown(); + break; } } } -- GitLab From ec661918b6b6ac36905fceb26043633f3e8a6a21 Mon Sep 17 00:00:00 2001 From: Ganesh Ganapathi Batta Date: Fri, 18 Apr 2014 10:00:40 -0700 Subject: [PATCH 0336/1408] Add transport param to Connect APIs Support for passing preferred transport for GATT connections as part of Connect APIs Change-Id: I93938dce519b8fa12de41d7e8690dc9355ce2dc5 --- .../android/bluetooth/BluetoothDevice.java | 43 ++++++++++++++++++- .../java/android/bluetooth/BluetoothGatt.java | 9 ++-- .../bluetooth/BluetoothGattServer.java | 6 ++- .../android/bluetooth/BluetoothManager.java | 22 +++++++++- .../android/bluetooth/IBluetoothGatt.aidl | 4 +- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a396a05e6f0..7f8d0aba7b0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -512,6 +512,25 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + /** + * No preferrence of physical transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_AUTO = 0; + + /** + * Prefer BR/EDR transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_BREDR = 1; + + /** + * Prefer LE transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_LE = 2; + + /** * Lazy initialization. Guaranteed final after first object constructed, or * getService() called. @@ -1216,6 +1235,27 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @throws IllegalArgumentException if callback is null + * @hide + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1226,10 +1266,11 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); + BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } + } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ff3af7c6ea3..601d9eee873 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; + private int mTransport; private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; @@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -600,10 +601,12 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { + /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, + int transport) { mContext = context; mService = iGatt; mDevice = device; + mTransport = transport; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -759,7 +762,7 @@ public final class BluetoothGatt implements BluetoothProfile { public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), - false); // autoConnect is inverse of "isDirect" + false, mTransport); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 0c00c06712a..34e86055db7 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -50,6 +50,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private Object mServerIfLock = new Object(); private int mServerIf; + private int mTransport; private List mServices; private static final int CALLBACK_REG_TIMEOUT = 10000; @@ -269,12 +270,13 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) { + /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { mContext = context; mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); mCallback = null; mServerIf = 0; + mTransport = transport; mServices = new ArrayList(); } @@ -401,7 +403,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.serverConnect(mServerIf, device.getAddress(), - autoConnect ? false : true); // autoConnect is inverse of "isDirect" + autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); return false; diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 172f3bcdefe..b1618cf3289 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -194,6 +194,26 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) { + + return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback,int transport) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -208,7 +228,7 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt); + BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport); Boolean regStatus = mGattServer.registerCallback(callback); return regStatus? mGattServer : null; } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index c6b5c3d309a..49b156dffa4 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -35,7 +35,7 @@ interface IBluetoothGatt { void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); void clientDisconnect(in int clientIf, in String address); void startAdvertising(in int appIf); void stopAdvertising(); @@ -77,7 +77,7 @@ interface IBluetoothGatt { void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); - void serverConnect(in int servertIf, in String address, in boolean isDirect); + void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, -- GitLab From 38ba4ac3387463bc110b43adad972181eb3ad406 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Tue, 8 Apr 2014 17:34:00 -0700 Subject: [PATCH 0337/1408] Replace LinkCapabilities with NetworkCapabilities Also remove unused LinkSocket and LinkSocketNotifier. bug:13885501 Change-Id: Id426e31b201fa4f29109b5fea485d8efb34519d3 --- .../bluetooth/BluetoothTetheringDataTracker.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java index a0b603e7864..f0c82997aa9 100644 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -20,7 +20,7 @@ import android.net.BaseNetworkStateTracker; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpResults; -import android.net.LinkCapabilities; +import android.net.NetworkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -75,7 +75,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private BluetoothTetheringDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); mNetworkInfo.setIsAvailable(false); setTeardownRequested(false); @@ -242,16 +242,6 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } } - /** - * A capability is an Integer/String pair, the capabilities - * are defined in the class LinkSocket#Key. - * - * @return a copy of this connections capabilities, may be empty but never null. - */ - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - /** * Fetch default gateway address for the network */ -- GitLab From a49d75deaf8381d9b42418fec1a5202ee66c2e1d Mon Sep 17 00:00:00 2001 From: Joe LaPenna Date: Tue, 13 May 2014 18:17:46 -0700 Subject: [PATCH 0338/1408] Log accept and close debug statements in BluetoothSocket. Bug: 14902781 Bug: 14784262 Change-Id: Id0c2cd216244e05c218568427bda863a538b1041 --- .../android/bluetooth/BluetoothSocket.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 1e75fc2a1c0..5738b9a6fb2 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -86,8 +86,8 @@ import java.nio.ByteBuffer; */ public final class BluetoothSocket implements Closeable { private static final String TAG = "BluetoothSocket"; - private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; @@ -190,7 +190,7 @@ public final class BluetoothSocket implements Closeable { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); + if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); if(fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); as.close(); @@ -356,24 +356,24 @@ public final class BluetoothSocket implements Closeable { // read out port number try { synchronized(this) { - if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + + if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket "); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket "); mSocket = new LocalSocket(fd); - if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } - if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); + if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); int channel = readInt(mSocketIS); synchronized(this) { if(mSocketState == SocketState.INIT) mSocketState = SocketState.LISTENING; } - if (VDBG) Log.d(TAG, "channel: " + channel); + if (DBG) Log.d(TAG, "channel: " + channel); if (mPort == -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -443,7 +443,7 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -453,10 +453,10 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); if(mSocket != null) { - if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket); + if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); mSocket.shutdownOutput(); mSocket.close(); -- GitLab From 7171f4c44ffee13a635a4907a0d64bb3a56659e3 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Mon, 21 Apr 2014 10:13:44 -0700 Subject: [PATCH 0339/1408] set scan parameters in BT stack. Apply the most demanding request in terms of duty cycle for all outstanding requests.Add api to set scan parameters. Change-Id: Iee460003d210455ab180367f3518bfb10a6c2539 --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 49b156dffa4..6e9f871115d 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -31,6 +31,8 @@ interface IBluetoothGatt { void startScan(in int appIf, in boolean isServer); void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); + void startScanWithUuidsAndScanWindowInterval(in int appIf, in boolean isServer, + in ParcelUuid[] ids, int scanWindow, int scanInterval); void stopScan(in int appIf, in boolean isServer); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); -- GitLab From efc4efe251337e32b50ee562e18c4d1f30f5c1e2 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 13 May 2014 10:16:44 -0700 Subject: [PATCH 0340/1408] Improve name of api. Change-Id: I4db24db621ab746f5e5efcbaf87b8856cf234e3e --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 6e9f871115d..3dd70945138 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -31,7 +31,7 @@ interface IBluetoothGatt { void startScan(in int appIf, in boolean isServer); void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); - void startScanWithUuidsAndScanWindowInterval(in int appIf, in boolean isServer, + void startScanWithUuidsScanParam(in int appIf, in boolean isServer, in ParcelUuid[] ids, int scanWindow, int scanInterval); void stopScan(in int appIf, in boolean isServer); -- GitLab From a8f5c7b258c60872fe2dbf25216b53dd580a3fe9 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 7 May 2014 14:54:43 -0700 Subject: [PATCH 0341/1408] Add a method to check whether a UUID is 32bit. Change-Id: I92e8bab560b2083e9f4855d28b3f54f2120ef628 --- .../java/android/bluetooth/BluetoothUuid.java | 20 ++++++++++++++++++- .../android/bluetooth/BluetoothUuidTest.java | 16 +++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index ab53fb050dd..1e22eb39289 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -273,11 +273,29 @@ public final class BluetoothUuid { * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ - public static boolean isShortUuid(ParcelUuid parcelUuid) { + public static boolean is16BitUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { return false; } return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); } + + + /** + * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid. + * + * @param parcelUuid + * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. + */ + public static boolean is32BitUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + if (is16BitUuid(parcelUuid)) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L); + } } diff --git a/framework/tests/src/android/bluetooth/BluetoothUuidTest.java b/framework/tests/src/android/bluetooth/BluetoothUuidTest.java index 0f3ea0ec000..536d722679b 100644 --- a/framework/tests/src/android/bluetooth/BluetoothUuidTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothUuidTest.java @@ -47,4 +47,20 @@ public class BluetoothUuidTest extends TestCase { assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"), BluetoothUuid.parseUuidFrom(uuid128)); } + + @SmallTest + public void testUuidType() { + assertTrue(BluetoothUuid.is16BitUuid( + ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"))); + assertFalse(BluetoothUuid.is32BitUuid( + ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"))); + + assertFalse(BluetoothUuid.is16BitUuid( + ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"))); + assertTrue(BluetoothUuid.is32BitUuid( + ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"))); + assertFalse(BluetoothUuid.is32BitUuid( + ParcelUuid.fromString("FE33110B-1000-1000-8000-00805F9B34FB"))); + + } } -- GitLab From 46ff4e6f7056d16bb83c094bbf287208dd9b8b55 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 16 Apr 2014 18:49:18 -0700 Subject: [PATCH 0342/1408] APIs for BLE scan, scan filter, batch scan, onFound/onLost and multiple advertising. Change-Id: I1655eb9cffa890b6fe38108bf51078662e90bc03 --- .../android/bluetooth/BluetoothAdapter.java | 32 + .../java/android/bluetooth/BluetoothGatt.java | 10 +- .../BluetoothLeAdvertiseScanData.aidl | 19 + .../BluetoothLeAdvertiseScanData.java | 649 +++++++++++++++ .../bluetooth/BluetoothLeAdvertiser.aidl | 19 + .../bluetooth/BluetoothLeAdvertiser.java | 600 ++++++++++++++ .../bluetooth/BluetoothLeScanFilter.aidl | 19 + .../bluetooth/BluetoothLeScanFilter.java | 577 +++++++++++++ .../android/bluetooth/BluetoothLeScanner.aidl | 20 + .../android/bluetooth/BluetoothLeScanner.java | 759 ++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 11 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 + .../BluetoothLeAdvertiseScanDataTest.java | 74 ++ .../bluetooth/BluetoothLeScanFilterTest.java | 185 +++++ .../bluetooth/BluetoothLeScannerTest.java | 53 ++ 15 files changed, 3026 insertions(+), 2 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.java create mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.java create mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e79deeccbdc..9e1c995bbb5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -497,6 +497,34 @@ public final class BluetoothAdapter { } } + /** + * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. + */ + public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { + // TODO: Return null if this feature is not supported by hardware. + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return new BluetoothLeAdvertiser(iGatt); + } catch (RemoteException e) { + Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e); + return null; + } + } + + /** + * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. + */ + public BluetoothLeScanner getBluetoothLeScanner() { + // TODO: Return null if BLE scan is not supported by hardware. + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return new BluetoothLeScanner(iGatt); + } catch (RemoteException e) { + Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e); + return null; + } + } + /** * Interface for BLE advertising callback. * @@ -2024,6 +2052,10 @@ public final class BluetoothAdapter { } } + @Override + public void onMultiAdvertiseCallback(int status) { + // no op + } /** * Callback reporting LE ATT MTU. * @hide diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 601d9eee873..c9df9c0eede 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -581,7 +581,15 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); - } + } + + /** + * @hide + */ + @Override + public void onMultiAdvertiseCallback(int status) { + // no op. + } /** * Callback invoked when the MTU for a given connection changes diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl new file mode 100644 index 00000000000..4aa8881422d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java new file mode 100644 index 00000000000..3d85810a00e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement + * data to be advertised, or the scan record obtained from BLE scans. + *

        + * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth + * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth + * Core Specification Version 4. Currently the following fields are allowed to be set: + *

      • Service UUIDs which identify the bluetooth gatt services running on the device. + *
      • Tx power level which is the transmission power level. + *
      • Service data which is the data associated with a service. + *
      • Manufacturer specific data which is the data associated with a particular manufacturer. + * + * @see BluetoothLeAdvertiser + */ +public final class BluetoothLeAdvertiseScanData { + private static final String TAG = "BluetoothLeAdvertiseScanData"; + + /** + * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising + * packet. + */ + public static final int ADVERTISING_DATA = 0; + /** + * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising + * packet. + *

        + * TODO: unhide when stack supports setting scan response data. + * + * @hide + */ + public static final int SCAN_RESPONSE_DATA = 1; + /** + * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of + * advertising data and scan response data. + */ + public static final int PARSED_SCAN_RECORD = 2; + + /** + * Base data type which contains the common fields for {@link AdvertisementData} and + * {@link ScanRecord}. + */ + public abstract static class AdvertiseBaseData { + + private final int mDataType; + + @Nullable + private final List mServiceUuids; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; + + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; + + private AdvertiseBaseData(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData) { + mDataType = dataType; + mServiceUuids = serviceUuids; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + } + + /** + * Returns the type of data, indicating whether the data is advertising data, scan response + * data or scan record. + */ + public int getDataType() { + return mDataType; + } + + /** + * Returns a list of service uuids within the advertisement that are used to identify the + * bluetooth gatt services. + */ + public List getServiceUuids() { + return mServiceUuids; + } + + /** + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + /** + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. The first 2 bytes of the data contain the company id. + */ + public byte[] getManufacturerSpecificData() { + return mManufacturerSpecificData; + } + + /** + * Returns a 16 bit uuid of the service that the service data is associated with. + */ + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + + /** + * Returns service data. The first two bytes should be a 16 bit service uuid associated with + * the service data. + */ + public byte[] getServiceData() { + return mServiceData; + } + + @Override + public String toString() { + return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids + + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]"; + } + } + + /** + * Advertisement data packet for Bluetooth LE advertising. This represents the data to be + * broadcasted in Bluetooth LE advertising. + *

        + * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to + * be advertised. + * + * @see BluetoothLeAdvertiser + */ + public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable { + + private boolean mIncludeTxPowerLevel; + + /** + * Whether the transmission power level will be included in the advertisement packet. + */ + public boolean getIncludeTxPowerLevel() { + return mIncludeTxPowerLevel; + } + + /** + * Returns a {@link Builder} to build {@link AdvertisementData}. + */ + public static Builder newBuilder() { + return new Builder(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getDataType()); + List uuids = getServiceUuids(); + if (uuids == null) { + dest.writeInt(0); + } else { + dest.writeInt(uuids.size()); + dest.writeList(uuids); + } + + dest.writeInt(getManufacturerId()); + byte[] manufacturerData = getManufacturerSpecificData(); + if (manufacturerData == null) { + dest.writeInt(0); + } else { + dest.writeInt(manufacturerData.length); + dest.writeByteArray(manufacturerData); + } + + ParcelUuid serviceDataUuid = getServiceDataUuid(); + if (serviceDataUuid == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeParcelable(serviceDataUuid, flags); + byte[] serviceData = getServiceData(); + if (serviceData == null) { + dest.writeInt(0); + } else { + dest.writeInt(serviceData.length); + dest.writeByteArray(serviceData); + } + } + dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); + } + + private AdvertisementData(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) { + super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, + manufacturerSpecificData); + this.mIncludeTxPowerLevel = mIncludeTxPowerLevel; + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertisementData[] newArray(int size) { + return new AdvertisementData[size]; + } + + @Override + public AdvertisementData createFromParcel(Parcel in) { + Builder builder = newBuilder(); + int dataType = in.readInt(); + builder.dataType(dataType); + if (in.readInt() > 0) { + List uuids = new ArrayList(); + in.readList(uuids, ParcelUuid.class.getClassLoader()); + builder.serviceUuids(uuids); + } + int manufacturerId = in.readInt(); + int manufacturerDataLength = in.readInt(); + if (manufacturerDataLength > 0) { + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.manufacturerData(manufacturerId, manufacturerData); + } + if (in.readInt() == 1) { + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.serviceData(serviceDataUuid, serviceData); + } + } + builder.includeTxPowerLevel(in.readByte() == 1); + return builder.build(); + } + }; + + /** + * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use + * {@link AdvertisementData#newBuilder()} to get an instance of the Builder. + */ + public static final class Builder { + private static final int MAX_ADVERTISING_DATA_BYTES = 31; + // Each fields need one byte for field length and another byte for field type. + private static final int OVERHEAD_BYTES_PER_FIELD = 2; + // Flags field will be set by system. + private static final int FLAGS_FIELD_BYTES = 3; + + private int mDataType; + @Nullable + private List mServiceUuids; + private boolean mIncludeTxPowerLevel; + private int mManufacturerId; + @Nullable + private byte[] mManufacturerSpecificData; + @Nullable + private ParcelUuid mServiceDataUuid; + @Nullable + private byte[] mServiceData; + + /** + * Set data type. + * + * @param dataType Data type, could only be + * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} or + * {@link BluetoothLeAdvertiseScanData#SCAN_RESPONSE_DATA} + * @throws IllegalArgumentException If the {@code dataType} is invalid. + */ + public Builder dataType(int dataType) { + if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) { + throw new IllegalArgumentException("invalid data type - " + dataType); + } + mDataType = dataType; + return this; + } + + /** + * Set the service uuids. Note the corresponding bluetooth Gatt services need to be + * already added on the device before start BLE advertising. + * + * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or + * 128-bit uuids. + * @throws IllegalArgumentException If the {@code serviceUuids} are null. + */ + public Builder serviceUuids(List serviceUuids) { + if (serviceUuids == null) { + throw new IllegalArgumentException("serivceUuids are null"); + } + mServiceUuids = serviceUuids; + return this; + } + + /** + * Add service data to advertisement. + * + * @param serviceDataUuid A 16 bit uuid of the service data + * @param serviceData Service data - the first two bytes of the service data are the + * service data uuid. + * @throws IllegalArgumentException If the {@code serviceDataUuid} or + * {@code serviceData} is empty. + */ + public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + if (serviceDataUuid == null || serviceData == null) { + throw new IllegalArgumentException( + "serviceDataUuid or serviceDataUuid is null"); + } + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + return this; + } + + /** + * Set manufacturer id and data. See assigned + * manufacturer identifies for the existing company identifiers. + * + * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. + * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of + * the manufacturer specific data are the manufacturer id. + * @throws IllegalArgumentException If the {@code manufacturerId} is negative or + * {@code manufacturerSpecificData} is null. + */ + public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + if (manufacturerId < 0) { + throw new IllegalArgumentException( + "invalid manufacturerId - " + manufacturerId); + } + if (manufacturerSpecificData == null) { + throw new IllegalArgumentException("manufacturerSpecificData is null"); + } + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + return this; + } + + /** + * Whether the transmission power level should be included in the advertising packet. + */ + public Builder includeTxPowerLevel(boolean includeTxPowerLevel) { + mIncludeTxPowerLevel = includeTxPowerLevel; + return this; + } + + /** + * Build the {@link BluetoothLeAdvertiseScanData}. + * + * @throws IllegalArgumentException If the data size is larger than 31 bytes. + */ + public AdvertisementData build() { + if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException( + "advertisement data size is larger than 31 bytes"); + } + return new AdvertisementData(mDataType, + mServiceUuids, + mServiceDataUuid, + mServiceData, mManufacturerId, mManufacturerSpecificData, + mIncludeTxPowerLevel); + } + + // Compute the size of the advertisement data. + private int totalBytes() { + int size = FLAGS_FIELD_BYTES; // flags field is always set. + if (mServiceUuids != null) { + int num16BitUuids = 0; + int num32BitUuids = 0; + int num128BitUuids = 0; + for (ParcelUuid uuid : mServiceUuids) { + if (BluetoothUuid.is16BitUuid(uuid)) { + ++num16BitUuids; + } else if (BluetoothUuid.is32BitUuid(uuid)) { + ++num32BitUuids; + } else { + ++num128BitUuids; + } + } + // 16 bit service uuids are grouped into one field when doing advertising. + if (num16BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + } + // 32 bit service uuids are grouped into one field when doing advertising. + if (num32BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + } + // 128 bit service uuids are grouped into one field when doing advertising. + if (num128BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + } + } + if (mServiceData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; + } + if (mManufacturerSpecificData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; + } + if (mIncludeTxPowerLevel) { + size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. + } + return size; + } + } + + } + + /** + * Represents a scan record from Bluetooth LE scan. + */ + public static final class ScanRecord extends AdvertiseBaseData { + // Flags of the advertising data. + private final int mAdvertiseFlags; + + // Transmission power level(in dB). + private final int mTxPowerLevel; + + // Local name of the Bluetooth LE device. + private final String mLocalName; + + /** + * Returns the advertising flags indicating the discoverable mode and capability of the + * device. Returns -1 if the flag field is not set. + */ + public int getAdvertiseFlags() { + return mAdvertiseFlags; + } + + /** + * Returns the transmission power level of the packet in dBm. Returns + * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate + * the path loss of a received packet using the following equation: + *

        + * pathloss = txPowerLevel - rssi + */ + public int getTxPowerLevel() { + return mTxPowerLevel; + } + + /** + * Returns the local name of the BLE device. The is a UTF-8 encoded string. + */ + @Nullable + public String getLocalName() { + return mLocalName; + } + + ScanRecord(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, + String localName) { + super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, + manufacturerSpecificData); + mLocalName = localName; + mAdvertiseFlags = advertiseFlags; + mTxPowerLevel = txPowerLevel; + } + + /** + * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}. + */ + public static Parser getParser() { + return new Parser(); + } + + /** + * A parser class used to parse a Bluetooth LE scan record to + * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed. + */ + public static final class Parser { + private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser"; + + // The following data type values are assigned by Bluetooth SIG. + // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18. + private static final int DATA_TYPE_FLAGS = 0x01; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; + private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; + private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; + private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; + private static final int DATA_TYPE_SERVICE_DATA = 0x16; + private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; + + // Helper method to extract bytes from byte array. + private static byte[] extractBytes(byte[] scanRecord, int start, int length) { + byte[] bytes = new byte[length]; + System.arraycopy(scanRecord, start, bytes, 0, length); + return bytes; + } + + /** + * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}. + *

        + * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * and 18. + *

        + * All numerical multi-byte entities and values shall use little-endian + * byte order. + * + * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. + */ + public ScanRecord parseFromScanRecord(byte[] scanRecord) { + if (scanRecord == null) { + return null; + } + + int currentPos = 0; + int advertiseFlag = -1; + List serviceUuids = new ArrayList(); + String localName = null; + int txPowerLevel = Integer.MIN_VALUE; + ParcelUuid serviceDataUuid = null; + byte[] serviceData = null; + int manufacturerId = -1; + byte[] manufacturerSpecificData = null; + + try { + while (currentPos < scanRecord.length) { + // length is unsigned int. + int length = scanRecord[currentPos++] & 0xFF; + if (length == 0) { + break; + } + // Note the length includes the length of the field type itself. + int dataLength = length - 1; + // fieldType is unsigned int. + int fieldType = scanRecord[currentPos++] & 0xFF; + switch (fieldType) { + case DATA_TYPE_FLAGS: + advertiseFlag = scanRecord[currentPos] & 0xFF; + break; + case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, + dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); + break; + case DATA_TYPE_LOCAL_NAME_SHORT: + case DATA_TYPE_LOCAL_NAME_COMPLETE: + localName = new String( + extractBytes(scanRecord, currentPos, dataLength)); + break; + case DATA_TYPE_TX_POWER_LEVEL: + txPowerLevel = scanRecord[currentPos]; + break; + case DATA_TYPE_SERVICE_DATA: + serviceData = extractBytes(scanRecord, currentPos, dataLength); + // The first two bytes of the service data are service data uuid. + int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; + byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, + serviceUuidLength); + serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); + break; + case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: + manufacturerSpecificData = extractBytes(scanRecord, currentPos, + dataLength); + // The first two bytes of the manufacturer specific data are + // manufacturer ids in little endian. + manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + + (manufacturerSpecificData[0] & 0xFF); + break; + default: + // Just ignore, we don't handle such data type. + break; + } + currentPos += dataLength; + } + + if (serviceUuids.isEmpty()) { + serviceUuids = null; + } + return new ScanRecord(PARSED_SCAN_RECORD, + serviceUuids, serviceDataUuid, serviceData, + manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, + localName); + } catch (IndexOutOfBoundsException e) { + Log.e(PARSER_TAG, + "unable to parse scan record: " + Arrays.toString(scanRecord)); + return null; + } + } + + // Parse service uuids. + private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, + int uuidLength, List serviceUuids) { + while (dataLength > 0) { + byte[] uuidBytes = extractBytes(scanRecord, currentPos, + uuidLength); + serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); + dataLength -= uuidLength; + currentPos += uuidLength; + } + return currentPos; + } + } + } + +} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl new file mode 100644 index 00000000000..31086105610 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java new file mode 100644 index 00000000000..2a8aa23b28a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop + * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by + * {@link BluetoothLeAdvertiseScanData.AdvertisementData}. + *

        + * To get an instance of {@link BluetoothLeAdvertiser}, call the + * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. + *

        + * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see BluetoothLeAdvertiseScanData.AdvertisementData + */ +public class BluetoothLeAdvertiser { + + private static final String TAG = "BluetoothLeAdvertiser"; + + /** + * The {@link Settings} provide a way to adjust advertising preferences for each individual + * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance. + */ + public static final class Settings implements Parcelable { + /** + * Perform Bluetooth LE advertising in low power mode. This is the default and preferred + * advertising mode as it consumes the least power. + */ + public static final int ADVERTISE_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE advertising in balanced power mode. This is balanced between + * advertising frequency and power consumption. + */ + public static final int ADVERTISE_MODE_BALANCED = 1; + /** + * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest + * power consumption and should not be used for background continuous advertising. + */ + public static final int ADVERTISE_MODE_LOW_LATENCY = 2; + + /** + * Advertise using the lowest transmission(tx) power level. An app can use low transmission + * power to restrict the visibility range of its advertising packet. + */ + public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; + /** + * Advertise using low tx power level. + */ + public static final int ADVERTISE_TX_POWER_LOW = 1; + /** + * Advertise using medium tx power level. + */ + public static final int ADVERTISE_TX_POWER_MEDIUM = 2; + /** + * Advertise using high tx power level. This is corresponding to largest visibility range of + * the advertising packet. + */ + public static final int ADVERTISE_TX_POWER_HIGH = 3; + + /** + * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0 + * vol6, part B, section 4.4.2 - Advertising state. + */ + public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; + /** + * Scannable undirected advertise type, as defined in same spec mentioned above. This event + * type allows a scanner to send a scan request asking additional information about the + * advertiser. + */ + public static final int ADVERTISE_TYPE_SCANNABLE = 1; + /** + * Connectable undirected advertising type, as defined in same spec mentioned above. This + * event type allows a scanner to send scan request asking additional information about the + * advertiser. It also allows an initiator to send a connect request for connection. + */ + public static final int ADVERTISE_TYPE_CONNECTABLE = 2; + + private final int mAdvertiseMode; + private final int mAdvertiseTxPowerLevel; + private final int mAdvertiseEventType; + + private Settings(int advertiseMode, int advertiseTxPowerLevel, + int advertiseEventType) { + mAdvertiseMode = advertiseMode; + mAdvertiseTxPowerLevel = advertiseTxPowerLevel; + mAdvertiseEventType = advertiseEventType; + } + + private Settings(Parcel in) { + mAdvertiseMode = in.readInt(); + mAdvertiseTxPowerLevel = in.readInt(); + mAdvertiseEventType = in.readInt(); + } + + /** + * Creates a {@link Builder} to construct a {@link Settings} object. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Returns the advertise mode. + */ + public int getMode() { + return mAdvertiseMode; + } + + /** + * Returns the tx power level for advertising. + */ + public int getTxPowerLevel() { + return mAdvertiseTxPowerLevel; + } + + /** + * Returns the advertise event type. + */ + public int getType() { + return mAdvertiseEventType; + } + + @Override + public String toString() { + return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAdvertiseMode); + dest.writeInt(mAdvertiseTxPowerLevel); + dest.writeInt(mAdvertiseEventType); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public Settings[] newArray(int size) { + return new Settings[size]; + } + + @Override + public Settings createFromParcel(Parcel in) { + return new Settings(in); + } + }; + + /** + * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use + * {@link Settings#newBuilder()} to get an instance of the builder. + */ + public static final class Builder { + private int mMode = ADVERTISE_MODE_LOW_POWER; + private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; + private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; + + // Private constructor, use Settings.newBuilder() get an instance of BUILDER. + private Builder() { + } + + /** + * Set advertise mode to control the advertising power and latency. + * + * @param advertiseMode Bluetooth LE Advertising mode, can only be one of + * {@link Settings#ADVERTISE_MODE_LOW_POWER}, + * {@link Settings#ADVERTISE_MODE_BALANCED}, or + * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the advertiseMode is invalid. + */ + public Builder advertiseMode(int advertiseMode) { + if (advertiseMode < ADVERTISE_MODE_LOW_POWER + || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("unknown mode " + advertiseMode); + } + mMode = advertiseMode; + return this; + } + + /** + * Set advertise tx power level to control the transmission power level for the + * advertising. + * + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one + * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW}, + * {@link Settings#ADVERTISE_TX_POWER_LOW}, + * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or + * {@link Settings#ADVERTISE_TX_POWER_HIGH}. + * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. + */ + public Builder txPowerLevel(int txPowerLevel) { + if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW + || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { + throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); + } + mTxPowerLevel = txPowerLevel; + return this; + } + + /** + * Set advertise type to control the event type of advertising. + * + * @param type Bluetooth LE Advertising type, can be either + * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE}, + * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or + * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}. + * @throws IllegalArgumentException If the {@code type} is invalid. + */ + public Builder type(int type) { + if (type < ADVERTISE_TYPE_NON_CONNECTABLE + || type > ADVERTISE_TYPE_CONNECTABLE) { + throw new IllegalArgumentException("unknown advertise type " + type); + } + mType = type; + return this; + } + + /** + * Build the {@link Settings} object. + */ + public Settings build() { + return new Settings(mMode, mTxPowerLevel, mType); + } + } + } + + /** + * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and + * stop advertising. + */ + public interface AdvertiseCallback { + + /** + * The operation is success. + * + * @hide + */ + public static final int SUCCESS = 0; + /** + * Fails to start advertising as the advertisement data contains services that are not added + * to the local bluetooth Gatt server. + */ + public static final int ADVERTISING_SERVICE_UNKNOWN = 1; + /** + * Fails to start advertising as system runs out of quota for advertisers. + */ + public static final int TOO_MANY_ADVERTISERS = 2; + + /** + * Fails to start advertising as the advertising is already started. + */ + public static final int ADVERTISING_ALREADY_STARTED = 3; + /** + * Fails to stop advertising as the advertising is not started. + */ + public static final int ADVERISING_NOT_STARTED = 4; + + /** + * Operation fails due to bluetooth controller failure. + */ + public static final int CONTROLLER_FAILURE = 5; + + /** + * Callback when advertising operation succeeds. + * + * @param settingsInEffect The actual settings used for advertising, which may be different + * from what the app asks. + */ + public void onSuccess(Settings settingsInEffect); + + /** + * Callback when advertising operation fails. + * + * @param errorCode Error code for failures. + */ + public void onFailure(int errorCode); + } + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map + mLeAdvertisers = new HashMap(); + + // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead. + BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + } + + /** + * Bluetooth GATT interface callbacks for advertising. + */ + private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; + private final AdvertiseCallback mAdvertiseCallback; + private final AdvertisementData mAdvertisement; + private final Settings mSettings; + private final IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // >0: registered and scan started + private int mLeHandle; + private boolean isAdvertising = false; + + public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, + AdvertisementData advertiseData, Settings settings, + IBluetoothGatt bluetoothGatt) { + mAdvertiseCallback = advertiseCallback; + mAdvertisement = advertiseData; + mSettings = settings; + mBluetoothGatt = bluetoothGatt; + mLeHandle = 0; + } + + public boolean advertiseStarted() { + boolean started = false; + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + started = (mLeHandle > 0 && isAdvertising); + } + return started; + } + + public boolean advertiseStopped() { + synchronized (this) { + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + return !isAdvertising; + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + synchronized (this) { + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, mSettings); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le advertise: " + e); + mLeHandle = -1; + notifyAll(); + } catch (Exception e) { + Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); + } + } else { + // registration failed + mLeHandle = -1; + notifyAll(); + } + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + // no op + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + synchronized (this) { + if (status == 0) { + isAdvertising = !isAdvertising; + if (!isAdvertising) { + try { + mBluetoothGatt.unregisterClient(mLeHandle); + mLeHandle = -1; + } catch (RemoteException e) { + Log.e(TAG, "remote exception when unregistering", e); + } + } + mAdvertiseCallback.onSuccess(null); + } else { + mAdvertiseCallback.onFailure(status); + } + notifyAll(); + } + + } + + /** + * Callback reporting LE ATT MTU. + * + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + /** + * Start Bluetooth LE Advertising. + * + * @param settings {@link Settings} for Bluetooth LE advertising. + * @param advertiseData {@link AdvertisementData} to be advertised. + * @param callback {@link AdvertiseCallback} for advertising status. + */ + public void startAdvertising(Settings settings, + AdvertisementData advertiseData, final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + if (mLeAdvertisers.containsKey(settings)) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED); + return; + } + AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, + settings, + mBluetoothGatt); + UUID uuid = UUID.randomUUID(); + try { + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.advertiseStarted()) { + mLeAdvertisers.put(settings, wrapper); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + /** + * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered + * through the {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param settings {@link Settings} used to start Bluetooth LE advertising. + * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. + */ + public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings); + if (wrapper == null) { + postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED); + return; + } + try { + mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); + if (wrapper.advertiseStopped()) { + mLeAdvertisers.remove(settings); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + private void postCallbackFailure(final AdvertiseCallback callback, final int error) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onFailure(error); + } + }); + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl b/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl new file mode 100644 index 00000000000..86ee06d3942 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeScanFilter; diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.java b/framework/java/android/bluetooth/BluetoothLeScanFilter.java new file mode 100644 index 00000000000..2ed85ba0a70 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanFilter.java @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord; +import android.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement + * packet fields. + *

        + * Current filtering on the following fields are supported: + *

      • Service UUIDs which identify the bluetooth gatt services running on the device. + *
      • Name of remote Bluetooth LE device. + *
      • Mac address of the remote device. + *
      • Rssi which indicates the received power level. + *
      • Service data which is the data associated with a service. + *
      • Manufacturer specific data which is the data associated with a particular manufacturer. + * + * @see BluetoothLeAdvertiseScanData.ScanRecord + * @see BluetoothLeScanner + */ +public final class BluetoothLeScanFilter implements Parcelable { + + @Nullable + private final String mLocalName; + + @Nullable + private final String mMacAddress; + + @Nullable + private final ParcelUuid mServiceUuid; + @Nullable + private final ParcelUuid mServiceUuidMask; + + @Nullable + private final byte[] mServiceData; + @Nullable + private final byte[] mServiceDataMask; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerData; + @Nullable + private final byte[] mManufacturerDataMask; + + private final int mMinRssi; + private final int mMaxRssi; + + private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid, + ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, + int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, + int minRssi, int maxRssi) { + mLocalName = name; + mServiceUuid = uuid; + mServiceUuidMask = uuidMask; + mMacAddress = macAddress; + mServiceData = serviceData; + mServiceDataMask = serviceDataMask; + mManufacturerId = manufacturerId; + mManufacturerData = manufacturerData; + mManufacturerDataMask = manufacturerDataMask; + mMinRssi = minRssi; + mMaxRssi = maxRssi; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mLocalName == null ? 0 : 1); + if (mLocalName != null) { + dest.writeString(mLocalName); + } + dest.writeInt(mMacAddress == null ? 0 : 1); + if (mMacAddress != null) { + dest.writeString(mMacAddress); + } + dest.writeInt(mServiceUuid == null ? 0 : 1); + if (mServiceUuid != null) { + dest.writeParcelable(mServiceUuid, flags); + } + dest.writeInt(mServiceUuidMask == null ? 0 : 1); + if (mServiceUuidMask != null) { + dest.writeParcelable(mServiceUuidMask, flags); + } + dest.writeInt(mServiceData == null ? 0 : mServiceData.length); + if (mServiceData != null) { + dest.writeByteArray(mServiceData); + } + dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); + if (mServiceDataMask != null) { + dest.writeByteArray(mServiceDataMask); + } + dest.writeInt(mManufacturerId); + dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length); + if (mManufacturerData != null) { + dest.writeByteArray(mManufacturerData); + } + dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); + if (mManufacturerDataMask != null) { + dest.writeByteArray(mManufacturerDataMask); + } + dest.writeInt(mMinRssi); + dest.writeInt(mMaxRssi); + } + + /** + * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel. + */ + public static final Creator + CREATOR = new Creator() { + + @Override + public BluetoothLeScanFilter[] newArray(int size) { + return new BluetoothLeScanFilter[size]; + } + + @Override + public BluetoothLeScanFilter createFromParcel(Parcel in) { + Builder builder = newBuilder(); + if (in.readInt() == 1) { + builder.name(in.readString()); + } + if (in.readInt() == 1) { + builder.macAddress(in.readString()); + } + if (in.readInt() == 1) { + ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); + builder.serviceUuid(uuid); + } + if (in.readInt() == 1) { + ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader()); + builder.serviceUuidMask(uuidMask); + } + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.serviceData(serviceData); + } + int serviceDataMaskLength = in.readInt(); + if (serviceDataMaskLength > 0) { + byte[] serviceDataMask = new byte[serviceDataMaskLength]; + in.readByteArray(serviceDataMask); + builder.serviceDataMask(serviceDataMask); + } + int manufacturerId = in.readInt(); + int manufacturerDataLength = in.readInt(); + if (manufacturerDataLength > 0) { + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.manufacturerData(manufacturerId, manufacturerData); + } + int manufacturerDataMaskLength = in.readInt(); + if (manufacturerDataMaskLength > 0) { + byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; + in.readByteArray(manufacturerDataMask); + builder.manufacturerDataMask(manufacturerDataMask); + } + int minRssi = in.readInt(); + int maxRssi = in.readInt(); + builder.rssiRange(minRssi, maxRssi); + return builder.build(); + } + }; + + /** + * Returns the filter set the local name field of Bluetooth advertisement data. + */ + @Nullable + public String getLocalName() { + return mLocalName; + } + + @Nullable /** + * Returns the filter set on the service uuid. + */ + public ParcelUuid getServiceUuid() { + return mServiceUuid; + } + + @Nullable + public ParcelUuid getServiceUuidMask() { + return mServiceUuidMask; + } + + @Nullable + public String getDeviceAddress() { + return mMacAddress; + } + + @Nullable + public byte[] getServiceData() { + return mServiceData; + } + + @Nullable + public byte[] getServiceDataMask() { + return mServiceDataMask; + } + + /** + * Returns the manufacturer id. -1 if the manufacturer filter is not set. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + @Nullable + public byte[] getManufacturerData() { + return mManufacturerData; + } + + @Nullable + public byte[] getManufacturerDataMask() { + return mManufacturerDataMask; + } + + /** + * Returns minimum value of rssi for the scan filter. {@link Integer#MIN_VALUE} if not set. + */ + public int getMinRssi() { + return mMinRssi; + } + + /** + * Returns maximum value of the rssi for the scan filter. {@link Integer#MAX_VALUE} if not set. + */ + public int getMaxRssi() { + return mMaxRssi; + } + + /** + * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match + * if it matches all the field filters. + */ + public boolean matches(ScanResult scanResult) { + if (scanResult == null) { + return false; + } + BluetoothDevice device = scanResult.getDevice(); + // Device match. + if (mMacAddress != null && (device == null || !mMacAddress.equals(device.getAddress()))) { + return false; + } + + int rssi = scanResult.getRssi(); + if (rssi < mMinRssi || rssi > mMaxRssi) { + return false; + } + + byte[] scanRecordBytes = scanResult.getScanRecord(); + ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes); + + // Scan record is null but there exist filters on it. + if (scanRecord == null + && (mLocalName != null || mServiceUuid != null || mManufacturerData != null + || mServiceData != null)) { + return false; + } + + // Local name match. + if (mLocalName != null && !mLocalName.equals(scanRecord.getLocalName())) { + return false; + } + + // UUID match. + if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, + scanRecord.getServiceUuids())) { + return false; + } + + // Service data match + if (mServiceData != null && + !matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData())) { + return false; + } + + // Manufacturer data match. + if (mManufacturerData != null && !matchesPartialData(mManufacturerData, + mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { + return false; + } + // All filters match. + return true; + } + + // Check if the uuid pattern is contained in a list of parcel uuids. + private boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, + List uuids) { + if (uuid == null) { + return true; + } + if (uuids == null) { + return false; + } + + for (ParcelUuid parcelUuid : uuids) { + UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); + if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { + return true; + } + } + return false; + } + + // Check if the uuid pattern matches the particular service uuid. + private boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { + if (mask == null) { + return uuid.equals(data); + } + if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != + (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { + return false; + } + return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == + (data.getMostSignificantBits() & mask.getMostSignificantBits())); + } + + // Check whether the data pattern matches the parsed data. + private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { + if (dataMask == null) { + return Arrays.equals(data, parsedData); + } + if (parsedData == null) { + return false; + } + for (int i = 0; i < data.length; ++i) { + if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "BluetoothLeScanFilter [mLocalName=" + mLocalName + ", mMacAddress=" + mMacAddress + + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" + + Arrays.toString(mServiceData) + ", mServiceDataMask=" + + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + + ", mManufacturerData=" + Arrays.toString(mManufacturerData) + + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + + ", mMinRssi=" + mMinRssi + ", mMaxRssi=" + mMaxRssi + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mLocalName, mMacAddress, mManufacturerId, mManufacturerData, + mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask, + mServiceUuid, mServiceUuidMask); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj; + return Objects.equals(mLocalName, other.mLocalName) && + Objects.equals(mMacAddress, other.mMacAddress) && + mManufacturerId == other.mManufacturerId && + Objects.deepEquals(mManufacturerData, other.mManufacturerData) && + Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && + mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && + Objects.deepEquals(mServiceData, other.mServiceData) && + Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && + Objects.equals(mServiceUuid, other.mServiceUuid) && + Objects.equals(mServiceUuidMask, other.mServiceUuidMask); + } + + /** + * Returns the {@link Builder} for {@link BluetoothLeScanFilter}. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder class for {@link BluetoothLeScanFilter}. Use + * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}. + */ + public static class Builder { + + private String mLocalName; + private String mMacAddress; + + private ParcelUuid mServiceUuid; + private ParcelUuid mUuidMask; + + private byte[] mServiceData; + private byte[] mServiceDataMask; + + private int mManufacturerId = -1; + private byte[] mManufacturerData; + private byte[] mManufacturerDataMask; + + private int mMinRssi = Integer.MIN_VALUE; + private int mMaxRssi = Integer.MAX_VALUE; + + // Private constructor, use BluetoothLeScanFilter.newBuilder instead. + private Builder() { + } + + /** + * Set filtering on local name. + */ + public Builder name(String localName) { + mLocalName = localName; + return this; + } + + /** + * Set filtering on device mac address. + * + * @param macAddress The device mac address for the filter. It needs to be in the format of + * "01:02:03:AB:CD:EF". The mac address can be validated using + * {@link BluetoothAdapter#checkBluetoothAddress}. + * @throws IllegalArgumentException If the {@code macAddress} is invalid. + */ + public Builder macAddress(String macAddress) { + if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) { + throw new IllegalArgumentException("invalid mac address " + macAddress); + } + mMacAddress = macAddress; + return this; + } + + /** + * Set filtering on service uuid. + */ + public Builder serviceUuid(ParcelUuid serviceUuid) { + mServiceUuid = serviceUuid; + return this; + } + + /** + * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set + * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate + * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit. + *

        + * The length of {@code uuidMask} must be the same as {@code serviceUuid}. + */ + public Builder serviceUuidMask(ParcelUuid uuidMask) { + mUuidMask = uuidMask; + return this; + } + + /** + * Set service data filter. + */ + public Builder serviceData(byte[] serviceData) { + mServiceData = serviceData; + return this; + } + + /** + * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it + * needs to match the one in service data, otherwise set it to 0 to ignore that bit. + *

        + * The {@code serviceDataMask} must have the same length of the {@code serviceData} set + * through {@link #serviceData(byte[])}. + */ + public Builder serviceDataMask(byte[] serviceDataMask) { + mServiceDataMask = serviceDataMask; + return this; + } + + /** + * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as + * invalid id. + *

        + * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. + */ + public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) { + if (manufacturerData != null && manufacturerId < 0) { + throw new IllegalArgumentException("invalid manufacture id"); + } + mManufacturerId = manufacturerId; + mManufacturerData = manufacturerData; + return this; + } + + /** + * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it + * needs to match the one in manufacturer data, otherwise set it to 0. + *

        + * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData} + * set through {@link #manufacturerData(int, byte[])}. + */ + public Builder manufacturerDataMask(byte[] manufacturerDataMask) { + mManufacturerDataMask = manufacturerDataMask; + return this; + } + + /** + * Set the desired rssi range for the filter. A scan result with rssi in the range of + * [minRssi, maxRssi] will be consider as a match. + */ + public Builder rssiRange(int minRssi, int maxRssi) { + mMinRssi = minRssi; + mMaxRssi = maxRssi; + return this; + } + + /** + * Build {@link BluetoothLeScanFilter}. + * + * @throws IllegalArgumentException If the filter cannot be built. + */ + public BluetoothLeScanFilter build() { + if (mUuidMask != null && mServiceUuid == null) { + throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); + } + + if (mServiceDataMask != null) { + if (mServiceData == null) { + throw new IllegalArgumentException( + "serviceData is null while serviceDataMask is not null"); + } + // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two + // byte array need to be the same. + if (mServiceData.length != mServiceDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for service data and service data mask"); + } + } + + if (mManufacturerDataMask != null) { + if (mManufacturerData == null) { + throw new IllegalArgumentException( + "manufacturerData is null while manufacturerDataMask is not null"); + } + // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths + // of the two byte array need to be the same. + if (mManufacturerData.length != mManufacturerDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for manufacturerData and manufacturerDataMask"); + } + } + return new BluetoothLeScanFilter(mLocalName, mMacAddress, + mServiceUuid, mUuidMask, + mServiceData, mServiceDataMask, + mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.aidl b/framework/java/android/bluetooth/BluetoothLeScanner.aidl new file mode 100644 index 00000000000..8cecdd7aac0 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanner.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeScanner.ScanResult; +parcelable BluetoothLeScanner.Settings; diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.java b/framework/java/android/bluetooth/BluetoothLeScanner.java new file mode 100644 index 00000000000..ed3188b1c16 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanner.java @@ -0,0 +1,759 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * This class provides methods to perform scan related operations for Bluetooth LE devices. An + * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It + * can also request different types of callbacks for delivering the result. + *

        + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of + * {@link BluetoothLeScanner}. + *

        + * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see BluetoothLeScanFilter + */ +public class BluetoothLeScanner { + + private static final String TAG = "BluetoothLeScanner"; + private static final boolean DBG = true; + + /** + * Settings for Bluetooth LE scan. + */ + public static final class Settings implements Parcelable { + /** + * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes + * the least power. + */ + public static final int SCAN_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE scan in balanced power mode. + */ + public static final int SCAN_MODE_BALANCED = 1; + /** + * Scan using highest duty cycle. It's recommended only using this mode when the application + * is running in foreground. + */ + public static final int SCAN_MODE_LOW_LATENCY = 2; + + /** + * Callback each time when a bluetooth advertisement is found. + */ + public static final int CALLBACK_TYPE_ON_UPDATE = 0; + /** + * Callback when a bluetooth advertisement is found for the first time. + */ + public static final int CALLBACK_TYPE_ON_FOUND = 1; + /** + * Callback when a bluetooth advertisement is found for the first time, then lost. + */ + public static final int CALLBACK_TYPE_ON_LOST = 2; + + /** + * Full scan result which contains device mac address, rssi, advertising and scan response + * and scan timestamp. + */ + public static final int SCAN_RESULT_TYPE_FULL = 0; + /** + * Truncated scan result which contains device mac address, rssi and scan timestamp. Note + * it's possible for an app to get more scan results that it asks if there are multiple apps + * using this type. TODO: decide whether we could unhide this setting. + * + * @hide + */ + public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; + + // Bluetooth LE scan mode. + private int mScanMode; + + // Bluetooth LE scan callback type + private int mCallbackType; + + // Bluetooth LE scan result type + private int mScanResultType; + + // Time of delay for reporting the scan result + private long mReportDelayMicros; + + public int getScanMode() { + return mScanMode; + } + + public int getCallbackType() { + return mCallbackType; + } + + public int getScanResultType() { + return mScanResultType; + } + + /** + * Returns report delay timestamp based on the device clock. + */ + public long getReportDelayMicros() { + return mReportDelayMicros; + } + + /** + * Creates a new {@link Builder} to build {@link Settings} object. + */ + public static Builder newBuilder() { + return new Builder(); + } + + private Settings(int scanMode, int callbackType, int scanResultType, + long reportDelayMicros) { + mScanMode = scanMode; + mCallbackType = callbackType; + mScanResultType = scanResultType; + mReportDelayMicros = reportDelayMicros; + } + + private Settings(Parcel in) { + mScanMode = in.readInt(); + mCallbackType = in.readInt(); + mScanResultType = in.readInt(); + mReportDelayMicros = in.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mScanMode); + dest.writeInt(mCallbackType); + dest.writeInt(mScanResultType); + dest.writeLong(mReportDelayMicros); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public Settings[] newArray(int size) { + return new Settings[size]; + } + + @Override + public Settings createFromParcel(Parcel in) { + return new Settings(in); + } + }; + + /** + * Builder for {@link BluetoothLeScanner.Settings}. + */ + public static class Builder { + private int mScanMode = SCAN_MODE_LOW_POWER; + private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; + private int mScanResultType = SCAN_RESULT_TYPE_FULL; + private long mReportDelayMicros = 0; + + // Hidden constructor. + private Builder() { + } + + /** + * Set scan mode for Bluetooth LE scan. + * + * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER}, + * {@link Settings#SCAN_MODE_BALANCED} or + * {@link Settings#SCAN_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the {@code scanMode} is invalid. + */ + public Builder scanMode(int scanMode) { + if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("invalid scan mode " + scanMode); + } + mScanMode = scanMode; + return this; + } + + /** + * Set callback type for Bluetooth LE scan. + * + * @param callbackType The callback type for the scan. Can be either one of + * {@link Settings#CALLBACK_TYPE_ON_UPDATE}, + * {@link Settings#CALLBACK_TYPE_ON_FOUND} or + * {@link Settings#CALLBACK_TYPE_ON_LOST}. + * @throws IllegalArgumentException If the {@code callbackType} is invalid. + */ + public Builder callbackType(int callbackType) { + if (callbackType < CALLBACK_TYPE_ON_UPDATE + || callbackType > CALLBACK_TYPE_ON_LOST) { + throw new IllegalArgumentException("invalid callback type - " + callbackType); + } + mCallbackType = callbackType; + return this; + } + + /** + * Set scan result type for Bluetooth LE scan. + * + * @param scanResultType Type for scan result, could be either + * {@link Settings#SCAN_RESULT_TYPE_FULL} or + * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}. + * @throws IllegalArgumentException If the {@code scanResultType} is invalid. + * @hide + */ + public Builder scanResultType(int scanResultType) { + if (scanResultType < SCAN_RESULT_TYPE_FULL + || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { + throw new IllegalArgumentException( + "invalid scanResultType - " + scanResultType); + } + mScanResultType = scanResultType; + return this; + } + + /** + * Set report delay timestamp for Bluetooth LE scan. + */ + public Builder reportDelayMicros(long reportDelayMicros) { + mReportDelayMicros = reportDelayMicros; + return this; + } + + /** + * Build {@link Settings}. + */ + public Settings build() { + return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros); + } + } + } + + /** + * ScanResult for Bluetooth LE scan. + */ + public static final class ScanResult implements Parcelable { + // Remote bluetooth device. + private BluetoothDevice mDevice; + + // Scan record, including advertising data and scan response data. + private byte[] mScanRecord; + + // Received signal strength. + private int mRssi; + + // Device timestamp when the result was last seen. + private long mTimestampMicros; + + // Constructor of scan result. + public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) { + mDevice = device; + mScanRecord = scanRecord; + mRssi = rssi; + mTimestampMicros = timestampMicros; + } + + private ScanResult(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mDevice != null) { + dest.writeInt(1); + mDevice.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mScanRecord != null) { + dest.writeInt(1); + dest.writeByteArray(mScanRecord); + } else { + dest.writeInt(0); + } + dest.writeInt(mRssi); + dest.writeLong(mTimestampMicros); + } + + private void readFromParcel(Parcel in) { + if (in.readInt() == 1) { + mDevice = BluetoothDevice.CREATOR.createFromParcel(in); + } + if (in.readInt() == 1) { + mScanRecord = in.createByteArray(); + } + mRssi = in.readInt(); + mTimestampMicros = in.readLong(); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the remote bluetooth device identified by the bluetooth device address. + */ + @Nullable + public BluetoothDevice getDevice() { + return mDevice; + } + + @Nullable /** + * Returns the scan record, which can be a combination of advertisement and scan response. + */ + public byte[] getScanRecord() { + return mScanRecord; + } + + /** + * Returns the received signal strength in dBm. The valid range is [-127, 127]. + */ + public int getRssi() { + return mRssi; + } + + /** + * Returns timestamp since boot when the scan record was observed. + */ + public long getTimestampMicros() { + return mTimestampMicros; + } + + @Override + public int hashCode() { + return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ScanResult other = (ScanResult) obj; + return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && + Objects.deepEquals(mScanRecord, other.mScanRecord) + && (mTimestampMicros == other.mTimestampMicros); + } + + @Override + public String toString() { + return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" + + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros=" + + mTimestampMicros + '}'; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public ScanResult createFromParcel(Parcel source) { + return new ScanResult(source); + } + + @Override + public ScanResult[] newArray(int size) { + return new ScanResult[size]; + } + }; + + } + + /** + * Callback of Bluetooth LE scans. The results of the scans will be delivered through the + * callbacks. + */ + public interface ScanCallback { + /** + * Callback when any BLE beacon is found. + * + * @param result A Bluetooth LE scan result. + */ + public void onDeviceUpdate(ScanResult result); + + /** + * Callback when the BLE beacon is found for the first time. + * + * @param result The Bluetooth LE scan result when the onFound event is triggered. + */ + public void onDeviceFound(ScanResult result); + + /** + * Callback when the BLE device was lost. Note a device has to be "found" before it's lost. + * + * @param device The Bluetooth device that is lost. + */ + public void onDeviceLost(BluetoothDevice device); + + /** + * Callback when batch results are delivered. + * + * @param results List of scan results that are previously scanned. + */ + public void onBatchScanResults(List results); + + /** + * Fails to start scan as BLE scan with the same settings is already started by the app. + */ + public static final int SCAN_ALREADY_STARTED = 1; + /** + * Fails to start scan as app cannot be registered. + */ + public static final int APPLICATION_REGISTRATION_FAILED = 2; + /** + * Fails to start scan due to gatt service failure. + */ + public static final int GATT_SERVICE_FAILURE = 3; + /** + * Fails to start scan due to controller failure. + */ + public static final int CONTROLLER_FAILURE = 4; + + /** + * Callback when scan failed. + */ + public void onScanFailed(int errorCode); + } + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map mLeScanClients; + + BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + mLeScanClients = new HashMap(); + } + + /** + * Bluetooth GATT interface callbacks + */ + private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; + + private final ScanCallback mScanCallback; + private final List mFilters; + private Settings mSettings; + private IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // > 0: registered and scan started + private int mLeHandle; + + public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, + List filters, Settings settings, ScanCallback scanCallback) { + mBluetoothGatt = bluetoothGatt; + mFilters = filters; + mSettings = settings; + mScanCallback = scanCallback; + mLeHandle = 0; + } + + public boolean scanStarted() { + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + } + return mLeHandle > 0; + } + + public void stopLeScan() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + try { + mBluetoothGatt.stopScan(mLeHandle, false); + mBluetoothGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop scan and unregister" + e); + } + mLeHandle = -1; + notifyAll(); + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + + synchronized (this) { + if (mLeHandle == -1) { + if (DBG) + Log.d(TAG, "onClientRegistered LE scan canceled"); + } + + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le scan: " + e); + mLeHandle = -1; + } + } else { + // registration failed + mLeHandle = -1; + } + notifyAll(); + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + /** + * Callback reporting an LE scan result. + * + * @hide + */ + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) + Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); + + // Check null in case the scan has been stopped + synchronized (this) { + if (mLeHandle <= 0) + return; + } + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + address); + long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos()); + ScanResult result = new ScanResult(device, advData, rssi, + scanMicros); + mScanCallback.onDeviceUpdate(result); + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + // no op + } + + @Override + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + /** + * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}. + * + * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices. + * @param settings Settings for ble scan. + * @param callback Callback when scan results are delivered. + * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. + */ + public void startScan(List filters, Settings settings, + final ScanCallback callback) { + if (settings == null || callback == null) { + throw new IllegalArgumentException("settings or callback is null"); + } + synchronized (mLeScanClients) { + if (mLeScanClients.get(settings) != null) { + postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED); + return; + } + BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, + settings, callback); + try { + UUID uuid = UUID.randomUUID(); + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.scanStarted()) { + mLeScanClients.put(settings, wrapper); + } else { + postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED); + return; + } + } catch (RemoteException e) { + Log.e(TAG, "GATT service exception when starting scan", e); + postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE); + } + } + } + + private void postCallbackError(final ScanCallback callback, final int errorCode) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onScanFailed(errorCode); + } + }); + } + + /** + * Stop Bluetooth LE scan. + * + * @param settings The same settings as used in {@link #startScan}, which is used to identify + * the BLE scan. + */ + public void stopScan(Settings settings) { + synchronized (mLeScanClients) { + BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings); + if (wrapper == null) { + return; + } + wrapper.stopLeScan(); + } + } + + /** + * Returns available storage size for batch scan results. It's recommended not to use batch scan + * if available storage size is small (less than 1k bytes, for instance). + * + * @hide TODO: unhide when batching is supported in stack. + */ + public int getAvailableBatchStorageSizeBytes() { + throw new UnsupportedOperationException("not impelemented"); + } + + /** + * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results + * batched on bluetooth controller. + * + * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one + * used to start scan. + * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will + * get batch scan callback if the batch scan buffer is flushed. + * @return Batch Scan results. + * @hide TODO: unhide when batching is supported in stack. + */ + public List getBatchScanResults(ScanCallback callback, boolean flush) { + throw new UnsupportedOperationException("not impelemented"); + } + +} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 3dd70945138..091b8063c1b 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -17,6 +17,10 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAdvertiseScanData; +import android.bluetooth.BluetoothLeAdvertiser; +import android.bluetooth.BluetoothLeScanFilter; +import android.bluetooth.BluetoothLeScanner; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -33,8 +37,13 @@ interface IBluetoothGatt { void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); void startScanWithUuidsScanParam(in int appIf, in boolean isServer, in ParcelUuid[] ids, int scanWindow, int scanInterval); + void startScanWithFilters(in int appIf, in boolean isServer, + in BluetoothLeScanner.Settings settings, + in List filters); void stopScan(in int appIf, in boolean isServer); - + void startMultiAdvertising(in int appIf, in BluetoothLeAdvertiseScanData.AdvertisementData data, + in BluetoothLeAdvertiser.Settings settings); + void stopMultiAdvertising(in int appIf); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index a78c29b0064..bf9e0a70180 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,5 +64,6 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); + oneway void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java new file mode 100644 index 00000000000..eb6c419bad9 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 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.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +import java.util.Arrays; + +/** + * Unit test cases for {@link BluetoothLeAdvertiseScanData}. + *

        + * To run this test, use adb shell am instrument -e class + * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeAdvertiseScanDataTest extends TestCase { + + @SmallTest + public void testParser() { + byte[] scanRecord = new byte[] { + 0x02, 0x01, 0x1a, // advertising flags + 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids + 0x04, 0x09, 0x50, 0x65, 0x64, // name + 0x02, 0x0A, (byte) 0xec, // tx power level + 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data + 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data + 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble + }; + BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord + .getParser().parseFromScanRecord(scanRecord); + assertEquals(0x1a, data.getAdvertiseFlags()); + ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + assertTrue(data.getServiceUuids().contains(uuid1)); + assertTrue(data.getServiceUuids().contains(uuid2)); + + assertEquals("Ped", data.getLocalName()); + assertEquals(-20, data.getTxPowerLevel()); + + assertEquals(224, data.getManufacturerId()); + assertArrayEquals(new byte[] { + (byte) 0xe0, 0x00, 0x02, 0x15 }, data.getManufacturerSpecificData()); + + assertEquals(uuid2, data.getServiceDataUuid()); + assertArrayEquals(new byte[] { + 0x0b, 0x11, 0x50, 0x64 }, data.getServiceData()); + } + + // Assert two byte arrays are equal. + private static void assertArrayEquals(byte[] expected, byte[] actual) { + if (!Arrays.equals(expected, actual)) { + fail("expected:<" + Arrays.toString(expected) + + "> but was:<" + Arrays.toString(actual) + ">"); + } + + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java new file mode 100644 index 00000000000..ec35d85deb1 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for Bluetooth LE scan filters. + *

        + * To run this test, use adb shell am instrument -e class + * 'android.bluetooth.BluetoothLeScanFilterTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeScanFilterTest extends TestCase { + + private static final String DEVICE_MAC = "01:02:03:04:05:AB"; + private ScanResult mScanResult; + private BluetoothLeScanFilter.Builder mFilterBuilder; + + @Override + protected void setUp() throws Exception { + byte[] scanRecord = new byte[] { + 0x02, 0x01, 0x1a, // advertising flags + 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids + 0x04, 0x09, 0x50, 0x65, 0x64, // name + 0x02, 0x0A, (byte) 0xec, // tx power level + 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data + 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data + 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble + }; + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); + mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L); + mFilterBuilder = BluetoothLeScanFilter.newBuilder(); + } + + @SmallTest + public void testNameFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build(); + assertTrue("name filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.name("Pem").build(); + assertFalse("name filter fails", filter.matches(mScanResult)); + + } + + @SmallTest + public void testDeviceFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build(); + assertTrue("device filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + assertFalse("device filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testServiceUuidFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build(); + assertTrue("uuid filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); + assertFalse("uuid filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder + .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")) + .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) + .build(); + assertTrue("uuid filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testServiceDataFilter() { + byte[] serviceData = new byte[] { + 0x0b, 0x11, 0x50, 0x64 }; + BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build(); + assertTrue("service data filter fails", filter.matches(mScanResult)); + + byte[] nonMatchData = new byte[] { + 0x0b, 0x01, 0x50, 0x64 }; + filter = mFilterBuilder.serviceData(nonMatchData).build(); + assertFalse("service data filter fails", filter.matches(mScanResult)); + + byte[] mask = new byte[] { + (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; + filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build(); + assertTrue("partial service data filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testManufacturerSpecificData() { + byte[] manufacturerData = new byte[] { + (byte) 0xE0, 0x00, 0x02, 0x15 }; + int manufacturerId = 224; + BluetoothLeScanFilter filter = + mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); + assertTrue("manufacturerData filter fails", filter.matches(mScanResult)); + + byte[] nonMatchData = new byte[] { + (byte) 0xF0, 0x00, 0x02, 0x15 }; + filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build(); + assertFalse("manufacturerData filter fails", filter.matches(mScanResult)); + + byte[] mask = new byte[] { + (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData) + .manufacturerDataMask(mask).build(); + assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testReadWriteParcel() { + BluetoothLeScanFilter filter = mFilterBuilder.build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.name("Ped").build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.serviceUuidMask( + ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); + testReadWriteParcelForFilter(filter); + + byte[] serviceData = new byte[] { + 0x0b, 0x11, 0x50, 0x64 }; + + filter = mFilterBuilder.serviceData(serviceData).build(); + testReadWriteParcelForFilter(filter); + + byte[] serviceDataMask = new byte[] { + (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; + filter = mFilterBuilder.serviceDataMask(serviceDataMask).build(); + testReadWriteParcelForFilter(filter); + + byte[] manufacturerData = new byte[] { + (byte) 0xE0, 0x00, 0x02, 0x15 }; + int manufacturerId = 224; + filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); + testReadWriteParcelForFilter(filter); + + byte[] manufacturerDataMask = new byte[] { + (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build(); + testReadWriteParcelForFilter(filter); + } + + private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) { + Parcel parcel = Parcel.obtain(); + filter.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + BluetoothLeScanFilter filterFromParcel = + BluetoothLeScanFilter.CREATOR.createFromParcel(parcel); + System.out.println(filterFromParcel); + assertEquals(filter, filterFromParcel); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java new file mode 100644 index 00000000000..8064ba8ac3b --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for Bluetooth LE scans. + *

        + * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest' + * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeScannerTest extends TestCase { + + /** + * Test read and write parcel of ScanResult + */ + @SmallTest + public void testScanResultParceling() { + BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06"); + byte[] scanRecord = new byte[] { + 1, 2, 3 }; + int rssi = -10; + long timestampMicros = 10000L; + + ScanResult result = new ScanResult(device, scanRecord, rssi, timestampMicros); + Parcel parcel = Parcel.obtain(); + result.writeToParcel(parcel, 0); + // Need to reset parcel data position to the beginning. + parcel.setDataPosition(0); + ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel); + assertEquals(result, resultFromParcel); + } + +} -- GitLab From e9c26fa05d5bd6452e0cf2d8c3733062569fb273 Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Mon, 19 May 2014 21:34:25 -0700 Subject: [PATCH 0343/1408] add a command to list all bonded devices Bug: 14601515 Change-Id: I3cc5bb664485ef768bb81f1f8870ed9bf62f8329 --- .../bluetooth/BluetoothInstrumentation.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 67203b261b4..22dce393f67 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -20,6 +20,8 @@ import android.app.Instrumentation; import android.content.Context; import android.os.Bundle; +import java.util.Set; + public class BluetoothInstrumentation extends Instrumentation { private BluetoothTestUtils mUtils = null; @@ -66,6 +68,8 @@ public class BluetoothInstrumentation extends Instrumentation { getName(); } else if ("getAddress".equals(command)) { getAddress(); + } else if ("getBondedDevices".equals(command)) { + getBondedDevices(); } else { finish(null); } @@ -98,6 +102,16 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } + public void getBondedDevices() { + Set devices = getBluetoothAdapter().getBondedDevices(); + int i = 0; + for (BluetoothDevice device : devices) { + mSuccessResult.putString(String.format("device-%02d", i), device.getAddress()); + i++; + } + finish(mSuccessResult); + } + public void finish(Bundle result) { if (result == null) { result = new Bundle(); -- GitLab From 39eb6801bb37a327a7460d9c830571448de19319 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 20 May 2014 04:52:35 +0000 Subject: [PATCH 0344/1408] Revert "APIs for BLE scan, scan filter, batch scan, onFound/onLost and multiple advertising." This reverts commit 2c4e68a86b7a9b9f760a8907b93ff40ccad56c80. Change-Id: I98c91343d886ebe22d0bf75a710fa0b0abf738b6 --- .../android/bluetooth/BluetoothAdapter.java | 32 - .../java/android/bluetooth/BluetoothGatt.java | 10 +- .../BluetoothLeAdvertiseScanData.aidl | 19 - .../BluetoothLeAdvertiseScanData.java | 649 --------------- .../bluetooth/BluetoothLeAdvertiser.aidl | 19 - .../bluetooth/BluetoothLeAdvertiser.java | 600 -------------- .../bluetooth/BluetoothLeScanFilter.aidl | 19 - .../bluetooth/BluetoothLeScanFilter.java | 577 ------------- .../android/bluetooth/BluetoothLeScanner.aidl | 20 - .../android/bluetooth/BluetoothLeScanner.java | 759 ------------------ .../android/bluetooth/IBluetoothGatt.aidl | 11 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 - .../BluetoothLeAdvertiseScanDataTest.java | 74 -- .../bluetooth/BluetoothLeScanFilterTest.java | 185 ----- .../bluetooth/BluetoothLeScannerTest.java | 53 -- 15 files changed, 2 insertions(+), 3026 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.java delete mode 100644 framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java delete mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java delete mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9e1c995bbb5..e79deeccbdc 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -497,34 +497,6 @@ public final class BluetoothAdapter { } } - /** - * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. - */ - public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - // TODO: Return null if this feature is not supported by hardware. - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - return new BluetoothLeAdvertiser(iGatt); - } catch (RemoteException e) { - Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e); - return null; - } - } - - /** - * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. - */ - public BluetoothLeScanner getBluetoothLeScanner() { - // TODO: Return null if BLE scan is not supported by hardware. - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - return new BluetoothLeScanner(iGatt); - } catch (RemoteException e) { - Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e); - return null; - } - } - /** * Interface for BLE advertising callback. * @@ -2052,10 +2024,6 @@ public final class BluetoothAdapter { } } - @Override - public void onMultiAdvertiseCallback(int status) { - // no op - } /** * Callback reporting LE ATT MTU. * @hide diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index c9df9c0eede..601d9eee873 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -581,15 +581,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); - } - - /** - * @hide - */ - @Override - public void onMultiAdvertiseCallback(int status) { - // no op. - } + } /** * Callback invoked when the MTU for a given connection changes diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl deleted file mode 100644 index 4aa8881422d..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java deleted file mode 100644 index 3d85810a00e..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java +++ /dev/null @@ -1,649 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.Nullable; -import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement - * data to be advertised, or the scan record obtained from BLE scans. - *

        - * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth - * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth - * Core Specification Version 4. Currently the following fields are allowed to be set: - *

      • Service UUIDs which identify the bluetooth gatt services running on the device. - *
      • Tx power level which is the transmission power level. - *
      • Service data which is the data associated with a service. - *
      • Manufacturer specific data which is the data associated with a particular manufacturer. - * - * @see BluetoothLeAdvertiser - */ -public final class BluetoothLeAdvertiseScanData { - private static final String TAG = "BluetoothLeAdvertiseScanData"; - - /** - * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising - * packet. - */ - public static final int ADVERTISING_DATA = 0; - /** - * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising - * packet. - *

        - * TODO: unhide when stack supports setting scan response data. - * - * @hide - */ - public static final int SCAN_RESPONSE_DATA = 1; - /** - * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of - * advertising data and scan response data. - */ - public static final int PARSED_SCAN_RECORD = 2; - - /** - * Base data type which contains the common fields for {@link AdvertisementData} and - * {@link ScanRecord}. - */ - public abstract static class AdvertiseBaseData { - - private final int mDataType; - - @Nullable - private final List mServiceUuids; - - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; - - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; - - private AdvertiseBaseData(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData) { - mDataType = dataType; - mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - } - - /** - * Returns the type of data, indicating whether the data is advertising data, scan response - * data or scan record. - */ - public int getDataType() { - return mDataType; - } - - /** - * Returns a list of service uuids within the advertisement that are used to identify the - * bluetooth gatt services. - */ - public List getServiceUuids() { - return mServiceUuids; - } - - /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. - */ - public int getManufacturerId() { - return mManufacturerId; - } - - /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. The first 2 bytes of the data contain the company id. - */ - public byte[] getManufacturerSpecificData() { - return mManufacturerSpecificData; - } - - /** - * Returns a 16 bit uuid of the service that the service data is associated with. - */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** - * Returns service data. The first two bytes should be a 16 bit service uuid associated with - * the service data. - */ - public byte[] getServiceData() { - return mServiceData; - } - - @Override - public String toString() { - return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]"; - } - } - - /** - * Advertisement data packet for Bluetooth LE advertising. This represents the data to be - * broadcasted in Bluetooth LE advertising. - *

        - * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to - * be advertised. - * - * @see BluetoothLeAdvertiser - */ - public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable { - - private boolean mIncludeTxPowerLevel; - - /** - * Whether the transmission power level will be included in the advertisement packet. - */ - public boolean getIncludeTxPowerLevel() { - return mIncludeTxPowerLevel; - } - - /** - * Returns a {@link Builder} to build {@link AdvertisementData}. - */ - public static Builder newBuilder() { - return new Builder(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(getDataType()); - List uuids = getServiceUuids(); - if (uuids == null) { - dest.writeInt(0); - } else { - dest.writeInt(uuids.size()); - dest.writeList(uuids); - } - - dest.writeInt(getManufacturerId()); - byte[] manufacturerData = getManufacturerSpecificData(); - if (manufacturerData == null) { - dest.writeInt(0); - } else { - dest.writeInt(manufacturerData.length); - dest.writeByteArray(manufacturerData); - } - - ParcelUuid serviceDataUuid = getServiceDataUuid(); - if (serviceDataUuid == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeParcelable(serviceDataUuid, flags); - byte[] serviceData = getServiceData(); - if (serviceData == null) { - dest.writeInt(0); - } else { - dest.writeInt(serviceData.length); - dest.writeByteArray(serviceData); - } - } - dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); - } - - private AdvertisementData(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) { - super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, - manufacturerSpecificData); - this.mIncludeTxPowerLevel = mIncludeTxPowerLevel; - } - - public static final Parcelable.Creator CREATOR = - new Creator() { - @Override - public AdvertisementData[] newArray(int size) { - return new AdvertisementData[size]; - } - - @Override - public AdvertisementData createFromParcel(Parcel in) { - Builder builder = newBuilder(); - int dataType = in.readInt(); - builder.dataType(dataType); - if (in.readInt() > 0) { - List uuids = new ArrayList(); - in.readList(uuids, ParcelUuid.class.getClassLoader()); - builder.serviceUuids(uuids); - } - int manufacturerId = in.readInt(); - int manufacturerDataLength = in.readInt(); - if (manufacturerDataLength > 0) { - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.manufacturerData(manufacturerId, manufacturerData); - } - if (in.readInt() == 1) { - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.serviceData(serviceDataUuid, serviceData); - } - } - builder.includeTxPowerLevel(in.readByte() == 1); - return builder.build(); - } - }; - - /** - * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use - * {@link AdvertisementData#newBuilder()} to get an instance of the Builder. - */ - public static final class Builder { - private static final int MAX_ADVERTISING_DATA_BYTES = 31; - // Each fields need one byte for field length and another byte for field type. - private static final int OVERHEAD_BYTES_PER_FIELD = 2; - // Flags field will be set by system. - private static final int FLAGS_FIELD_BYTES = 3; - - private int mDataType; - @Nullable - private List mServiceUuids; - private boolean mIncludeTxPowerLevel; - private int mManufacturerId; - @Nullable - private byte[] mManufacturerSpecificData; - @Nullable - private ParcelUuid mServiceDataUuid; - @Nullable - private byte[] mServiceData; - - /** - * Set data type. - * - * @param dataType Data type, could only be - * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} or - * {@link BluetoothLeAdvertiseScanData#SCAN_RESPONSE_DATA} - * @throws IllegalArgumentException If the {@code dataType} is invalid. - */ - public Builder dataType(int dataType) { - if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) { - throw new IllegalArgumentException("invalid data type - " + dataType); - } - mDataType = dataType; - return this; - } - - /** - * Set the service uuids. Note the corresponding bluetooth Gatt services need to be - * already added on the device before start BLE advertising. - * - * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or - * 128-bit uuids. - * @throws IllegalArgumentException If the {@code serviceUuids} are null. - */ - public Builder serviceUuids(List serviceUuids) { - if (serviceUuids == null) { - throw new IllegalArgumentException("serivceUuids are null"); - } - mServiceUuids = serviceUuids; - return this; - } - - /** - * Add service data to advertisement. - * - * @param serviceDataUuid A 16 bit uuid of the service data - * @param serviceData Service data - the first two bytes of the service data are the - * service data uuid. - * @throws IllegalArgumentException If the {@code serviceDataUuid} or - * {@code serviceData} is empty. - */ - public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) { - if (serviceDataUuid == null || serviceData == null) { - throw new IllegalArgumentException( - "serviceDataUuid or serviceDataUuid is null"); - } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - return this; - } - - /** - * Set manufacturer id and data. See assigned - * manufacturer identifies for the existing company identifiers. - * - * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. - * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of - * the manufacturer specific data are the manufacturer id. - * @throws IllegalArgumentException If the {@code manufacturerId} is negative or - * {@code manufacturerSpecificData} is null. - */ - public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { - if (manufacturerId < 0) { - throw new IllegalArgumentException( - "invalid manufacturerId - " + manufacturerId); - } - if (manufacturerSpecificData == null) { - throw new IllegalArgumentException("manufacturerSpecificData is null"); - } - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - return this; - } - - /** - * Whether the transmission power level should be included in the advertising packet. - */ - public Builder includeTxPowerLevel(boolean includeTxPowerLevel) { - mIncludeTxPowerLevel = includeTxPowerLevel; - return this; - } - - /** - * Build the {@link BluetoothLeAdvertiseScanData}. - * - * @throws IllegalArgumentException If the data size is larger than 31 bytes. - */ - public AdvertisementData build() { - if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { - throw new IllegalArgumentException( - "advertisement data size is larger than 31 bytes"); - } - return new AdvertisementData(mDataType, - mServiceUuids, - mServiceDataUuid, - mServiceData, mManufacturerId, mManufacturerSpecificData, - mIncludeTxPowerLevel); - } - - // Compute the size of the advertisement data. - private int totalBytes() { - int size = FLAGS_FIELD_BYTES; // flags field is always set. - if (mServiceUuids != null) { - int num16BitUuids = 0; - int num32BitUuids = 0; - int num128BitUuids = 0; - for (ParcelUuid uuid : mServiceUuids) { - if (BluetoothUuid.is16BitUuid(uuid)) { - ++num16BitUuids; - } else if (BluetoothUuid.is32BitUuid(uuid)) { - ++num32BitUuids; - } else { - ++num128BitUuids; - } - } - // 16 bit service uuids are grouped into one field when doing advertising. - if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; - } - // 32 bit service uuids are grouped into one field when doing advertising. - if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; - } - // 128 bit service uuids are grouped into one field when doing advertising. - if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; - } - } - if (mServiceData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; - } - if (mManufacturerSpecificData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; - } - if (mIncludeTxPowerLevel) { - size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. - } - return size; - } - } - - } - - /** - * Represents a scan record from Bluetooth LE scan. - */ - public static final class ScanRecord extends AdvertiseBaseData { - // Flags of the advertising data. - private final int mAdvertiseFlags; - - // Transmission power level(in dB). - private final int mTxPowerLevel; - - // Local name of the Bluetooth LE device. - private final String mLocalName; - - /** - * Returns the advertising flags indicating the discoverable mode and capability of the - * device. Returns -1 if the flag field is not set. - */ - public int getAdvertiseFlags() { - return mAdvertiseFlags; - } - - /** - * Returns the transmission power level of the packet in dBm. Returns - * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate - * the path loss of a received packet using the following equation: - *

        - * pathloss = txPowerLevel - rssi - */ - public int getTxPowerLevel() { - return mTxPowerLevel; - } - - /** - * Returns the local name of the BLE device. The is a UTF-8 encoded string. - */ - @Nullable - public String getLocalName() { - return mLocalName; - } - - ScanRecord(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, - String localName) { - super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, - manufacturerSpecificData); - mLocalName = localName; - mAdvertiseFlags = advertiseFlags; - mTxPowerLevel = txPowerLevel; - } - - /** - * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}. - */ - public static Parser getParser() { - return new Parser(); - } - - /** - * A parser class used to parse a Bluetooth LE scan record to - * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed. - */ - public static final class Parser { - private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser"; - - // The following data type values are assigned by Bluetooth SIG. - // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18. - private static final int DATA_TYPE_FLAGS = 0x01; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; - private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; - private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; - private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA = 0x16; - private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; - - // Helper method to extract bytes from byte array. - private static byte[] extractBytes(byte[] scanRecord, int start, int length) { - byte[] bytes = new byte[length]; - System.arraycopy(scanRecord, start, bytes, 0, length); - return bytes; - } - - /** - * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}. - *

        - * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 - * and 18. - *

        - * All numerical multi-byte entities and values shall use little-endian - * byte order. - * - * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - */ - public ScanRecord parseFromScanRecord(byte[] scanRecord) { - if (scanRecord == null) { - return null; - } - - int currentPos = 0; - int advertiseFlag = -1; - List serviceUuids = new ArrayList(); - String localName = null; - int txPowerLevel = Integer.MIN_VALUE; - ParcelUuid serviceDataUuid = null; - byte[] serviceData = null; - int manufacturerId = -1; - byte[] manufacturerSpecificData = null; - - try { - while (currentPos < scanRecord.length) { - // length is unsigned int. - int length = scanRecord[currentPos++] & 0xFF; - if (length == 0) { - break; - } - // Note the length includes the length of the field type itself. - int dataLength = length - 1; - // fieldType is unsigned int. - int fieldType = scanRecord[currentPos++] & 0xFF; - switch (fieldType) { - case DATA_TYPE_FLAGS: - advertiseFlag = scanRecord[currentPos] & 0xFF; - break; - case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, - dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); - break; - case DATA_TYPE_LOCAL_NAME_SHORT: - case DATA_TYPE_LOCAL_NAME_COMPLETE: - localName = new String( - extractBytes(scanRecord, currentPos, dataLength)); - break; - case DATA_TYPE_TX_POWER_LEVEL: - txPowerLevel = scanRecord[currentPos]; - break; - case DATA_TYPE_SERVICE_DATA: - serviceData = extractBytes(scanRecord, currentPos, dataLength); - // The first two bytes of the service data are service data uuid. - int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; - byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, - serviceUuidLength); - serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); - break; - case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: - manufacturerSpecificData = extractBytes(scanRecord, currentPos, - dataLength); - // The first two bytes of the manufacturer specific data are - // manufacturer ids in little endian. - manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + - (manufacturerSpecificData[0] & 0xFF); - break; - default: - // Just ignore, we don't handle such data type. - break; - } - currentPos += dataLength; - } - - if (serviceUuids.isEmpty()) { - serviceUuids = null; - } - return new ScanRecord(PARSED_SCAN_RECORD, - serviceUuids, serviceDataUuid, serviceData, - manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, - localName); - } catch (IndexOutOfBoundsException e) { - Log.e(PARSER_TAG, - "unable to parse scan record: " + Arrays.toString(scanRecord)); - return null; - } - } - - // Parse service uuids. - private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, - int uuidLength, List serviceUuids) { - while (dataLength > 0) { - byte[] uuidBytes = extractBytes(scanRecord, currentPos, - uuidLength); - serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); - dataLength -= uuidLength; - currentPos += uuidLength; - } - return currentPos; - } - } - } - -} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl deleted file mode 100644 index 31086105610..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java deleted file mode 100644 index 2a8aa23b28a..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; -import android.os.Handler; -import android.os.Looper; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop - * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by - * {@link BluetoothLeAdvertiseScanData.AdvertisementData}. - *

        - * To get an instance of {@link BluetoothLeAdvertiser}, call the - * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. - *

        - * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @see BluetoothLeAdvertiseScanData.AdvertisementData - */ -public class BluetoothLeAdvertiser { - - private static final String TAG = "BluetoothLeAdvertiser"; - - /** - * The {@link Settings} provide a way to adjust advertising preferences for each individual - * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance. - */ - public static final class Settings implements Parcelable { - /** - * Perform Bluetooth LE advertising in low power mode. This is the default and preferred - * advertising mode as it consumes the least power. - */ - public static final int ADVERTISE_MODE_LOW_POWER = 0; - /** - * Perform Bluetooth LE advertising in balanced power mode. This is balanced between - * advertising frequency and power consumption. - */ - public static final int ADVERTISE_MODE_BALANCED = 1; - /** - * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest - * power consumption and should not be used for background continuous advertising. - */ - public static final int ADVERTISE_MODE_LOW_LATENCY = 2; - - /** - * Advertise using the lowest transmission(tx) power level. An app can use low transmission - * power to restrict the visibility range of its advertising packet. - */ - public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; - /** - * Advertise using low tx power level. - */ - public static final int ADVERTISE_TX_POWER_LOW = 1; - /** - * Advertise using medium tx power level. - */ - public static final int ADVERTISE_TX_POWER_MEDIUM = 2; - /** - * Advertise using high tx power level. This is corresponding to largest visibility range of - * the advertising packet. - */ - public static final int ADVERTISE_TX_POWER_HIGH = 3; - - /** - * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0 - * vol6, part B, section 4.4.2 - Advertising state. - */ - public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; - /** - * Scannable undirected advertise type, as defined in same spec mentioned above. This event - * type allows a scanner to send a scan request asking additional information about the - * advertiser. - */ - public static final int ADVERTISE_TYPE_SCANNABLE = 1; - /** - * Connectable undirected advertising type, as defined in same spec mentioned above. This - * event type allows a scanner to send scan request asking additional information about the - * advertiser. It also allows an initiator to send a connect request for connection. - */ - public static final int ADVERTISE_TYPE_CONNECTABLE = 2; - - private final int mAdvertiseMode; - private final int mAdvertiseTxPowerLevel; - private final int mAdvertiseEventType; - - private Settings(int advertiseMode, int advertiseTxPowerLevel, - int advertiseEventType) { - mAdvertiseMode = advertiseMode; - mAdvertiseTxPowerLevel = advertiseTxPowerLevel; - mAdvertiseEventType = advertiseEventType; - } - - private Settings(Parcel in) { - mAdvertiseMode = in.readInt(); - mAdvertiseTxPowerLevel = in.readInt(); - mAdvertiseEventType = in.readInt(); - } - - /** - * Creates a {@link Builder} to construct a {@link Settings} object. - */ - public static Builder newBuilder() { - return new Builder(); - } - - /** - * Returns the advertise mode. - */ - public int getMode() { - return mAdvertiseMode; - } - - /** - * Returns the tx power level for advertising. - */ - public int getTxPowerLevel() { - return mAdvertiseTxPowerLevel; - } - - /** - * Returns the advertise event type. - */ - public int getType() { - return mAdvertiseEventType; - } - - @Override - public String toString() { - return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" - + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mAdvertiseMode); - dest.writeInt(mAdvertiseTxPowerLevel); - dest.writeInt(mAdvertiseEventType); - } - - public static final Parcelable.Creator CREATOR = - new Creator() { - @Override - public Settings[] newArray(int size) { - return new Settings[size]; - } - - @Override - public Settings createFromParcel(Parcel in) { - return new Settings(in); - } - }; - - /** - * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use - * {@link Settings#newBuilder()} to get an instance of the builder. - */ - public static final class Builder { - private int mMode = ADVERTISE_MODE_LOW_POWER; - private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; - private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; - - // Private constructor, use Settings.newBuilder() get an instance of BUILDER. - private Builder() { - } - - /** - * Set advertise mode to control the advertising power and latency. - * - * @param advertiseMode Bluetooth LE Advertising mode, can only be one of - * {@link Settings#ADVERTISE_MODE_LOW_POWER}, - * {@link Settings#ADVERTISE_MODE_BALANCED}, or - * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the advertiseMode is invalid. - */ - public Builder advertiseMode(int advertiseMode) { - if (advertiseMode < ADVERTISE_MODE_LOW_POWER - || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("unknown mode " + advertiseMode); - } - mMode = advertiseMode; - return this; - } - - /** - * Set advertise tx power level to control the transmission power level for the - * advertising. - * - * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one - * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW}, - * {@link Settings#ADVERTISE_TX_POWER_LOW}, - * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or - * {@link Settings#ADVERTISE_TX_POWER_HIGH}. - * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. - */ - public Builder txPowerLevel(int txPowerLevel) { - if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW - || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { - throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); - } - mTxPowerLevel = txPowerLevel; - return this; - } - - /** - * Set advertise type to control the event type of advertising. - * - * @param type Bluetooth LE Advertising type, can be either - * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE}, - * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or - * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}. - * @throws IllegalArgumentException If the {@code type} is invalid. - */ - public Builder type(int type) { - if (type < ADVERTISE_TYPE_NON_CONNECTABLE - || type > ADVERTISE_TYPE_CONNECTABLE) { - throw new IllegalArgumentException("unknown advertise type " + type); - } - mType = type; - return this; - } - - /** - * Build the {@link Settings} object. - */ - public Settings build() { - return new Settings(mMode, mTxPowerLevel, mType); - } - } - } - - /** - * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and - * stop advertising. - */ - public interface AdvertiseCallback { - - /** - * The operation is success. - * - * @hide - */ - public static final int SUCCESS = 0; - /** - * Fails to start advertising as the advertisement data contains services that are not added - * to the local bluetooth Gatt server. - */ - public static final int ADVERTISING_SERVICE_UNKNOWN = 1; - /** - * Fails to start advertising as system runs out of quota for advertisers. - */ - public static final int TOO_MANY_ADVERTISERS = 2; - - /** - * Fails to start advertising as the advertising is already started. - */ - public static final int ADVERTISING_ALREADY_STARTED = 3; - /** - * Fails to stop advertising as the advertising is not started. - */ - public static final int ADVERISING_NOT_STARTED = 4; - - /** - * Operation fails due to bluetooth controller failure. - */ - public static final int CONTROLLER_FAILURE = 5; - - /** - * Callback when advertising operation succeeds. - * - * @param settingsInEffect The actual settings used for advertising, which may be different - * from what the app asks. - */ - public void onSuccess(Settings settingsInEffect); - - /** - * Callback when advertising operation fails. - * - * @param errorCode Error code for failures. - */ - public void onFailure(int errorCode); - } - - private final IBluetoothGatt mBluetoothGatt; - private final Handler mHandler; - private final Map - mLeAdvertisers = new HashMap(); - - // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead. - BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; - mHandler = new Handler(Looper.getMainLooper()); - } - - /** - * Bluetooth GATT interface callbacks for advertising. - */ - private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { - private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; - private final AdvertiseCallback mAdvertiseCallback; - private final AdvertisementData mAdvertisement; - private final Settings mSettings; - private final IBluetoothGatt mBluetoothGatt; - - // mLeHandle 0: not registered - // -1: scan stopped - // >0: registered and scan started - private int mLeHandle; - private boolean isAdvertising = false; - - public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, - AdvertisementData advertiseData, Settings settings, - IBluetoothGatt bluetoothGatt) { - mAdvertiseCallback = advertiseCallback; - mAdvertisement = advertiseData; - mSettings = settings; - mBluetoothGatt = bluetoothGatt; - mLeHandle = 0; - } - - public boolean advertiseStarted() { - boolean started = false; - synchronized (this) { - if (mLeHandle == -1) { - return false; - } - try { - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - started = (mLeHandle > 0 && isAdvertising); - } - return started; - } - - public boolean advertiseStopped() { - synchronized (this) { - try { - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - return !isAdvertising; - } - } - - /** - * Application interface registered - app is ready to go - */ - @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); - synchronized (this) { - if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; - try { - mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, mSettings); - } catch (RemoteException e) { - Log.e(TAG, "fail to start le advertise: " + e); - mLeHandle = -1; - notifyAll(); - } catch (Exception e) { - Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); - } - } else { - // registration failed - mLeHandle = -1; - notifyAll(); - } - } - } - - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - - @Override - public void onScanResult(String address, int rssi, byte[] advData) { - // no op - } - - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - synchronized (this) { - if (status == 0) { - isAdvertising = !isAdvertising; - if (!isAdvertising) { - try { - mBluetoothGatt.unregisterClient(mLeHandle); - mLeHandle = -1; - } catch (RemoteException e) { - Log.e(TAG, "remote exception when unregistering", e); - } - } - mAdvertiseCallback.onSuccess(null); - } else { - mAdvertiseCallback.onFailure(status); - } - notifyAll(); - } - - } - - /** - * Callback reporting LE ATT MTU. - * - * @hide - */ - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - } - - /** - * Start Bluetooth LE Advertising. - * - * @param settings {@link Settings} for Bluetooth LE advertising. - * @param advertiseData {@link AdvertisementData} to be advertised. - * @param callback {@link AdvertiseCallback} for advertising status. - */ - public void startAdvertising(Settings settings, - AdvertisementData advertiseData, final AdvertiseCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - if (mLeAdvertisers.containsKey(settings)) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED); - return; - } - AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - settings, - mBluetoothGatt); - UUID uuid = UUID.randomUUID(); - try { - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.advertiseStarted()) { - mLeAdvertisers.put(settings, wrapper); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); - } - } - - /** - * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered - * through the {@code callback}. - *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * - * @param settings {@link Settings} used to start Bluetooth LE advertising. - * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. - */ - public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings); - if (wrapper == null) { - postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED); - return; - } - try { - mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); - if (wrapper.advertiseStopped()) { - mLeAdvertisers.remove(settings); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); - } - } - - private void postCallbackFailure(final AdvertiseCallback callback, final int error) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onFailure(error); - } - }); - } -} diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl b/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl deleted file mode 100644 index 86ee06d3942..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothLeScanFilter; diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.java b/framework/java/android/bluetooth/BluetoothLeScanFilter.java deleted file mode 100644 index 2ed85ba0a70..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeScanFilter.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.Nullable; -import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord; -import android.bluetooth.BluetoothLeScanner.ScanResult; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -/** - * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement - * packet fields. - *

        - * Current filtering on the following fields are supported: - *

      • Service UUIDs which identify the bluetooth gatt services running on the device. - *
      • Name of remote Bluetooth LE device. - *
      • Mac address of the remote device. - *
      • Rssi which indicates the received power level. - *
      • Service data which is the data associated with a service. - *
      • Manufacturer specific data which is the data associated with a particular manufacturer. - * - * @see BluetoothLeAdvertiseScanData.ScanRecord - * @see BluetoothLeScanner - */ -public final class BluetoothLeScanFilter implements Parcelable { - - @Nullable - private final String mLocalName; - - @Nullable - private final String mMacAddress; - - @Nullable - private final ParcelUuid mServiceUuid; - @Nullable - private final ParcelUuid mServiceUuidMask; - - @Nullable - private final byte[] mServiceData; - @Nullable - private final byte[] mServiceDataMask; - - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerData; - @Nullable - private final byte[] mManufacturerDataMask; - - private final int mMinRssi; - private final int mMaxRssi; - - private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid, - ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, - int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, - int minRssi, int maxRssi) { - mLocalName = name; - mServiceUuid = uuid; - mServiceUuidMask = uuidMask; - mMacAddress = macAddress; - mServiceData = serviceData; - mServiceDataMask = serviceDataMask; - mManufacturerId = manufacturerId; - mManufacturerData = manufacturerData; - mManufacturerDataMask = manufacturerDataMask; - mMinRssi = minRssi; - mMaxRssi = maxRssi; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mLocalName == null ? 0 : 1); - if (mLocalName != null) { - dest.writeString(mLocalName); - } - dest.writeInt(mMacAddress == null ? 0 : 1); - if (mMacAddress != null) { - dest.writeString(mMacAddress); - } - dest.writeInt(mServiceUuid == null ? 0 : 1); - if (mServiceUuid != null) { - dest.writeParcelable(mServiceUuid, flags); - } - dest.writeInt(mServiceUuidMask == null ? 0 : 1); - if (mServiceUuidMask != null) { - dest.writeParcelable(mServiceUuidMask, flags); - } - dest.writeInt(mServiceData == null ? 0 : mServiceData.length); - if (mServiceData != null) { - dest.writeByteArray(mServiceData); - } - dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); - if (mServiceDataMask != null) { - dest.writeByteArray(mServiceDataMask); - } - dest.writeInt(mManufacturerId); - dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length); - if (mManufacturerData != null) { - dest.writeByteArray(mManufacturerData); - } - dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); - if (mManufacturerDataMask != null) { - dest.writeByteArray(mManufacturerDataMask); - } - dest.writeInt(mMinRssi); - dest.writeInt(mMaxRssi); - } - - /** - * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel. - */ - public static final Creator - CREATOR = new Creator() { - - @Override - public BluetoothLeScanFilter[] newArray(int size) { - return new BluetoothLeScanFilter[size]; - } - - @Override - public BluetoothLeScanFilter createFromParcel(Parcel in) { - Builder builder = newBuilder(); - if (in.readInt() == 1) { - builder.name(in.readString()); - } - if (in.readInt() == 1) { - builder.macAddress(in.readString()); - } - if (in.readInt() == 1) { - ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.serviceUuid(uuid); - } - if (in.readInt() == 1) { - ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.serviceUuidMask(uuidMask); - } - int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.serviceData(serviceData); - } - int serviceDataMaskLength = in.readInt(); - if (serviceDataMaskLength > 0) { - byte[] serviceDataMask = new byte[serviceDataMaskLength]; - in.readByteArray(serviceDataMask); - builder.serviceDataMask(serviceDataMask); - } - int manufacturerId = in.readInt(); - int manufacturerDataLength = in.readInt(); - if (manufacturerDataLength > 0) { - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.manufacturerData(manufacturerId, manufacturerData); - } - int manufacturerDataMaskLength = in.readInt(); - if (manufacturerDataMaskLength > 0) { - byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; - in.readByteArray(manufacturerDataMask); - builder.manufacturerDataMask(manufacturerDataMask); - } - int minRssi = in.readInt(); - int maxRssi = in.readInt(); - builder.rssiRange(minRssi, maxRssi); - return builder.build(); - } - }; - - /** - * Returns the filter set the local name field of Bluetooth advertisement data. - */ - @Nullable - public String getLocalName() { - return mLocalName; - } - - @Nullable /** - * Returns the filter set on the service uuid. - */ - public ParcelUuid getServiceUuid() { - return mServiceUuid; - } - - @Nullable - public ParcelUuid getServiceUuidMask() { - return mServiceUuidMask; - } - - @Nullable - public String getDeviceAddress() { - return mMacAddress; - } - - @Nullable - public byte[] getServiceData() { - return mServiceData; - } - - @Nullable - public byte[] getServiceDataMask() { - return mServiceDataMask; - } - - /** - * Returns the manufacturer id. -1 if the manufacturer filter is not set. - */ - public int getManufacturerId() { - return mManufacturerId; - } - - @Nullable - public byte[] getManufacturerData() { - return mManufacturerData; - } - - @Nullable - public byte[] getManufacturerDataMask() { - return mManufacturerDataMask; - } - - /** - * Returns minimum value of rssi for the scan filter. {@link Integer#MIN_VALUE} if not set. - */ - public int getMinRssi() { - return mMinRssi; - } - - /** - * Returns maximum value of the rssi for the scan filter. {@link Integer#MAX_VALUE} if not set. - */ - public int getMaxRssi() { - return mMaxRssi; - } - - /** - * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match - * if it matches all the field filters. - */ - public boolean matches(ScanResult scanResult) { - if (scanResult == null) { - return false; - } - BluetoothDevice device = scanResult.getDevice(); - // Device match. - if (mMacAddress != null && (device == null || !mMacAddress.equals(device.getAddress()))) { - return false; - } - - int rssi = scanResult.getRssi(); - if (rssi < mMinRssi || rssi > mMaxRssi) { - return false; - } - - byte[] scanRecordBytes = scanResult.getScanRecord(); - ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes); - - // Scan record is null but there exist filters on it. - if (scanRecord == null - && (mLocalName != null || mServiceUuid != null || mManufacturerData != null - || mServiceData != null)) { - return false; - } - - // Local name match. - if (mLocalName != null && !mLocalName.equals(scanRecord.getLocalName())) { - return false; - } - - // UUID match. - if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, - scanRecord.getServiceUuids())) { - return false; - } - - // Service data match - if (mServiceData != null && - !matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData())) { - return false; - } - - // Manufacturer data match. - if (mManufacturerData != null && !matchesPartialData(mManufacturerData, - mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { - return false; - } - // All filters match. - return true; - } - - // Check if the uuid pattern is contained in a list of parcel uuids. - private boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, - List uuids) { - if (uuid == null) { - return true; - } - if (uuids == null) { - return false; - } - - for (ParcelUuid parcelUuid : uuids) { - UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); - if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { - return true; - } - } - return false; - } - - // Check if the uuid pattern matches the particular service uuid. - private boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - if (mask == null) { - return uuid.equals(data); - } - if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != - (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { - return false; - } - return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == - (data.getMostSignificantBits() & mask.getMostSignificantBits())); - } - - // Check whether the data pattern matches the parsed data. - private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { - if (dataMask == null) { - return Arrays.equals(data, parsedData); - } - if (parsedData == null) { - return false; - } - for (int i = 0; i < data.length; ++i) { - if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { - return false; - } - } - return true; - } - - @Override - public String toString() { - return "BluetoothLeScanFilter [mLocalName=" + mLocalName + ", mMacAddress=" + mMacAddress - + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" - + Arrays.toString(mServiceData) + ", mServiceDataMask=" - + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId - + ", mManufacturerData=" + Arrays.toString(mManufacturerData) - + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) - + ", mMinRssi=" + mMinRssi + ", mMaxRssi=" + mMaxRssi + "]"; - } - - @Override - public int hashCode() { - return Objects.hash(mLocalName, mMacAddress, mManufacturerId, mManufacturerData, - mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask, - mServiceUuid, mServiceUuidMask); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj; - return Objects.equals(mLocalName, other.mLocalName) && - Objects.equals(mMacAddress, other.mMacAddress) && - mManufacturerId == other.mManufacturerId && - Objects.deepEquals(mManufacturerData, other.mManufacturerData) && - Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && - Objects.deepEquals(mServiceData, other.mServiceData) && - Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && - Objects.equals(mServiceUuid, other.mServiceUuid) && - Objects.equals(mServiceUuidMask, other.mServiceUuidMask); - } - - /** - * Returns the {@link Builder} for {@link BluetoothLeScanFilter}. - */ - public static Builder newBuilder() { - return new Builder(); - } - - /** - * Builder class for {@link BluetoothLeScanFilter}. Use - * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}. - */ - public static class Builder { - - private String mLocalName; - private String mMacAddress; - - private ParcelUuid mServiceUuid; - private ParcelUuid mUuidMask; - - private byte[] mServiceData; - private byte[] mServiceDataMask; - - private int mManufacturerId = -1; - private byte[] mManufacturerData; - private byte[] mManufacturerDataMask; - - private int mMinRssi = Integer.MIN_VALUE; - private int mMaxRssi = Integer.MAX_VALUE; - - // Private constructor, use BluetoothLeScanFilter.newBuilder instead. - private Builder() { - } - - /** - * Set filtering on local name. - */ - public Builder name(String localName) { - mLocalName = localName; - return this; - } - - /** - * Set filtering on device mac address. - * - * @param macAddress The device mac address for the filter. It needs to be in the format of - * "01:02:03:AB:CD:EF". The mac address can be validated using - * {@link BluetoothAdapter#checkBluetoothAddress}. - * @throws IllegalArgumentException If the {@code macAddress} is invalid. - */ - public Builder macAddress(String macAddress) { - if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) { - throw new IllegalArgumentException("invalid mac address " + macAddress); - } - mMacAddress = macAddress; - return this; - } - - /** - * Set filtering on service uuid. - */ - public Builder serviceUuid(ParcelUuid serviceUuid) { - mServiceUuid = serviceUuid; - return this; - } - - /** - * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set - * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate - * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit. - *

        - * The length of {@code uuidMask} must be the same as {@code serviceUuid}. - */ - public Builder serviceUuidMask(ParcelUuid uuidMask) { - mUuidMask = uuidMask; - return this; - } - - /** - * Set service data filter. - */ - public Builder serviceData(byte[] serviceData) { - mServiceData = serviceData; - return this; - } - - /** - * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it - * needs to match the one in service data, otherwise set it to 0 to ignore that bit. - *

        - * The {@code serviceDataMask} must have the same length of the {@code serviceData} set - * through {@link #serviceData(byte[])}. - */ - public Builder serviceDataMask(byte[] serviceDataMask) { - mServiceDataMask = serviceDataMask; - return this; - } - - /** - * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as - * invalid id. - *

        - * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. - */ - public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) { - if (manufacturerData != null && manufacturerId < 0) { - throw new IllegalArgumentException("invalid manufacture id"); - } - mManufacturerId = manufacturerId; - mManufacturerData = manufacturerData; - return this; - } - - /** - * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it - * needs to match the one in manufacturer data, otherwise set it to 0. - *

        - * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData} - * set through {@link #manufacturerData(int, byte[])}. - */ - public Builder manufacturerDataMask(byte[] manufacturerDataMask) { - mManufacturerDataMask = manufacturerDataMask; - return this; - } - - /** - * Set the desired rssi range for the filter. A scan result with rssi in the range of - * [minRssi, maxRssi] will be consider as a match. - */ - public Builder rssiRange(int minRssi, int maxRssi) { - mMinRssi = minRssi; - mMaxRssi = maxRssi; - return this; - } - - /** - * Build {@link BluetoothLeScanFilter}. - * - * @throws IllegalArgumentException If the filter cannot be built. - */ - public BluetoothLeScanFilter build() { - if (mUuidMask != null && mServiceUuid == null) { - throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); - } - - if (mServiceDataMask != null) { - if (mServiceData == null) { - throw new IllegalArgumentException( - "serviceData is null while serviceDataMask is not null"); - } - // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two - // byte array need to be the same. - if (mServiceData.length != mServiceDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for service data and service data mask"); - } - } - - if (mManufacturerDataMask != null) { - if (mManufacturerData == null) { - throw new IllegalArgumentException( - "manufacturerData is null while manufacturerDataMask is not null"); - } - // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths - // of the two byte array need to be the same. - if (mManufacturerData.length != mManufacturerDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for manufacturerData and manufacturerDataMask"); - } - } - return new BluetoothLeScanFilter(mLocalName, mMacAddress, - mServiceUuid, mUuidMask, - mServiceData, mServiceDataMask, - mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); - } - } -} diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.aidl b/framework/java/android/bluetooth/BluetoothLeScanner.aidl deleted file mode 100644 index 8cecdd7aac0..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeScanner.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothLeScanner.ScanResult; -parcelable BluetoothLeScanner.Settings; diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.java b/framework/java/android/bluetooth/BluetoothLeScanner.java deleted file mode 100644 index ed3188b1c16..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeScanner.java +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.Nullable; -import android.os.Handler; -import android.os.Looper; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.SystemClock; -import android.util.Log; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It - * can also request different types of callbacks for delivering the result. - *

        - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of - * {@link BluetoothLeScanner}. - *

        - * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @see BluetoothLeScanFilter - */ -public class BluetoothLeScanner { - - private static final String TAG = "BluetoothLeScanner"; - private static final boolean DBG = true; - - /** - * Settings for Bluetooth LE scan. - */ - public static final class Settings implements Parcelable { - /** - * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes - * the least power. - */ - public static final int SCAN_MODE_LOW_POWER = 0; - /** - * Perform Bluetooth LE scan in balanced power mode. - */ - public static final int SCAN_MODE_BALANCED = 1; - /** - * Scan using highest duty cycle. It's recommended only using this mode when the application - * is running in foreground. - */ - public static final int SCAN_MODE_LOW_LATENCY = 2; - - /** - * Callback each time when a bluetooth advertisement is found. - */ - public static final int CALLBACK_TYPE_ON_UPDATE = 0; - /** - * Callback when a bluetooth advertisement is found for the first time. - */ - public static final int CALLBACK_TYPE_ON_FOUND = 1; - /** - * Callback when a bluetooth advertisement is found for the first time, then lost. - */ - public static final int CALLBACK_TYPE_ON_LOST = 2; - - /** - * Full scan result which contains device mac address, rssi, advertising and scan response - * and scan timestamp. - */ - public static final int SCAN_RESULT_TYPE_FULL = 0; - /** - * Truncated scan result which contains device mac address, rssi and scan timestamp. Note - * it's possible for an app to get more scan results that it asks if there are multiple apps - * using this type. TODO: decide whether we could unhide this setting. - * - * @hide - */ - public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; - - // Bluetooth LE scan mode. - private int mScanMode; - - // Bluetooth LE scan callback type - private int mCallbackType; - - // Bluetooth LE scan result type - private int mScanResultType; - - // Time of delay for reporting the scan result - private long mReportDelayMicros; - - public int getScanMode() { - return mScanMode; - } - - public int getCallbackType() { - return mCallbackType; - } - - public int getScanResultType() { - return mScanResultType; - } - - /** - * Returns report delay timestamp based on the device clock. - */ - public long getReportDelayMicros() { - return mReportDelayMicros; - } - - /** - * Creates a new {@link Builder} to build {@link Settings} object. - */ - public static Builder newBuilder() { - return new Builder(); - } - - private Settings(int scanMode, int callbackType, int scanResultType, - long reportDelayMicros) { - mScanMode = scanMode; - mCallbackType = callbackType; - mScanResultType = scanResultType; - mReportDelayMicros = reportDelayMicros; - } - - private Settings(Parcel in) { - mScanMode = in.readInt(); - mCallbackType = in.readInt(); - mScanResultType = in.readInt(); - mReportDelayMicros = in.readLong(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mScanMode); - dest.writeInt(mCallbackType); - dest.writeInt(mScanResultType); - dest.writeLong(mReportDelayMicros); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public Settings[] newArray(int size) { - return new Settings[size]; - } - - @Override - public Settings createFromParcel(Parcel in) { - return new Settings(in); - } - }; - - /** - * Builder for {@link BluetoothLeScanner.Settings}. - */ - public static class Builder { - private int mScanMode = SCAN_MODE_LOW_POWER; - private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; - private int mScanResultType = SCAN_RESULT_TYPE_FULL; - private long mReportDelayMicros = 0; - - // Hidden constructor. - private Builder() { - } - - /** - * Set scan mode for Bluetooth LE scan. - * - * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER}, - * {@link Settings#SCAN_MODE_BALANCED} or - * {@link Settings#SCAN_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the {@code scanMode} is invalid. - */ - public Builder scanMode(int scanMode) { - if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("invalid scan mode " + scanMode); - } - mScanMode = scanMode; - return this; - } - - /** - * Set callback type for Bluetooth LE scan. - * - * @param callbackType The callback type for the scan. Can be either one of - * {@link Settings#CALLBACK_TYPE_ON_UPDATE}, - * {@link Settings#CALLBACK_TYPE_ON_FOUND} or - * {@link Settings#CALLBACK_TYPE_ON_LOST}. - * @throws IllegalArgumentException If the {@code callbackType} is invalid. - */ - public Builder callbackType(int callbackType) { - if (callbackType < CALLBACK_TYPE_ON_UPDATE - || callbackType > CALLBACK_TYPE_ON_LOST) { - throw new IllegalArgumentException("invalid callback type - " + callbackType); - } - mCallbackType = callbackType; - return this; - } - - /** - * Set scan result type for Bluetooth LE scan. - * - * @param scanResultType Type for scan result, could be either - * {@link Settings#SCAN_RESULT_TYPE_FULL} or - * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}. - * @throws IllegalArgumentException If the {@code scanResultType} is invalid. - * @hide - */ - public Builder scanResultType(int scanResultType) { - if (scanResultType < SCAN_RESULT_TYPE_FULL - || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { - throw new IllegalArgumentException( - "invalid scanResultType - " + scanResultType); - } - mScanResultType = scanResultType; - return this; - } - - /** - * Set report delay timestamp for Bluetooth LE scan. - */ - public Builder reportDelayMicros(long reportDelayMicros) { - mReportDelayMicros = reportDelayMicros; - return this; - } - - /** - * Build {@link Settings}. - */ - public Settings build() { - return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros); - } - } - } - - /** - * ScanResult for Bluetooth LE scan. - */ - public static final class ScanResult implements Parcelable { - // Remote bluetooth device. - private BluetoothDevice mDevice; - - // Scan record, including advertising data and scan response data. - private byte[] mScanRecord; - - // Received signal strength. - private int mRssi; - - // Device timestamp when the result was last seen. - private long mTimestampMicros; - - // Constructor of scan result. - public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) { - mDevice = device; - mScanRecord = scanRecord; - mRssi = rssi; - mTimestampMicros = timestampMicros; - } - - private ScanResult(Parcel in) { - readFromParcel(in); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - if (mDevice != null) { - dest.writeInt(1); - mDevice.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mScanRecord != null) { - dest.writeInt(1); - dest.writeByteArray(mScanRecord); - } else { - dest.writeInt(0); - } - dest.writeInt(mRssi); - dest.writeLong(mTimestampMicros); - } - - private void readFromParcel(Parcel in) { - if (in.readInt() == 1) { - mDevice = BluetoothDevice.CREATOR.createFromParcel(in); - } - if (in.readInt() == 1) { - mScanRecord = in.createByteArray(); - } - mRssi = in.readInt(); - mTimestampMicros = in.readLong(); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Returns the remote bluetooth device identified by the bluetooth device address. - */ - @Nullable - public BluetoothDevice getDevice() { - return mDevice; - } - - @Nullable /** - * Returns the scan record, which can be a combination of advertisement and scan response. - */ - public byte[] getScanRecord() { - return mScanRecord; - } - - /** - * Returns the received signal strength in dBm. The valid range is [-127, 127]. - */ - public int getRssi() { - return mRssi; - } - - /** - * Returns timestamp since boot when the scan record was observed. - */ - public long getTimestampMicros() { - return mTimestampMicros; - } - - @Override - public int hashCode() { - return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ScanResult other = (ScanResult) obj; - return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.deepEquals(mScanRecord, other.mScanRecord) - && (mTimestampMicros == other.mTimestampMicros); - } - - @Override - public String toString() { - return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros=" - + mTimestampMicros + '}'; - } - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public ScanResult createFromParcel(Parcel source) { - return new ScanResult(source); - } - - @Override - public ScanResult[] newArray(int size) { - return new ScanResult[size]; - } - }; - - } - - /** - * Callback of Bluetooth LE scans. The results of the scans will be delivered through the - * callbacks. - */ - public interface ScanCallback { - /** - * Callback when any BLE beacon is found. - * - * @param result A Bluetooth LE scan result. - */ - public void onDeviceUpdate(ScanResult result); - - /** - * Callback when the BLE beacon is found for the first time. - * - * @param result The Bluetooth LE scan result when the onFound event is triggered. - */ - public void onDeviceFound(ScanResult result); - - /** - * Callback when the BLE device was lost. Note a device has to be "found" before it's lost. - * - * @param device The Bluetooth device that is lost. - */ - public void onDeviceLost(BluetoothDevice device); - - /** - * Callback when batch results are delivered. - * - * @param results List of scan results that are previously scanned. - */ - public void onBatchScanResults(List results); - - /** - * Fails to start scan as BLE scan with the same settings is already started by the app. - */ - public static final int SCAN_ALREADY_STARTED = 1; - /** - * Fails to start scan as app cannot be registered. - */ - public static final int APPLICATION_REGISTRATION_FAILED = 2; - /** - * Fails to start scan due to gatt service failure. - */ - public static final int GATT_SERVICE_FAILURE = 3; - /** - * Fails to start scan due to controller failure. - */ - public static final int CONTROLLER_FAILURE = 4; - - /** - * Callback when scan failed. - */ - public void onScanFailed(int errorCode); - } - - private final IBluetoothGatt mBluetoothGatt; - private final Handler mHandler; - private final Map mLeScanClients; - - BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; - mHandler = new Handler(Looper.getMainLooper()); - mLeScanClients = new HashMap(); - } - - /** - * Bluetooth GATT interface callbacks - */ - private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { - private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; - - private final ScanCallback mScanCallback; - private final List mFilters; - private Settings mSettings; - private IBluetoothGatt mBluetoothGatt; - - // mLeHandle 0: not registered - // -1: scan stopped - // > 0: registered and scan started - private int mLeHandle; - - public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, - List filters, Settings settings, ScanCallback scanCallback) { - mBluetoothGatt = bluetoothGatt; - mFilters = filters; - mSettings = settings; - mScanCallback = scanCallback; - mLeHandle = 0; - } - - public boolean scanStarted() { - synchronized (this) { - if (mLeHandle == -1) { - return false; - } - try { - wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - } - return mLeHandle > 0; - } - - public void stopLeScan() { - synchronized (this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); - return; - } - try { - mBluetoothGatt.stopScan(mLeHandle, false); - mBluetoothGatt.unregisterClient(mLeHandle); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop scan and unregister" + e); - } - mLeHandle = -1; - notifyAll(); - } - } - - /** - * Application interface registered - app is ready to go - */ - @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + - " clientIf=" + clientIf); - - synchronized (this) { - if (mLeHandle == -1) { - if (DBG) - Log.d(TAG, "onClientRegistered LE scan canceled"); - } - - if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; - try { - mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); - } catch (RemoteException e) { - Log.e(TAG, "fail to start le scan: " + e); - mLeHandle = -1; - } - } else { - // registration failed - mLeHandle = -1; - } - notifyAll(); - } - } - - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - - /** - * Callback reporting an LE scan result. - * - * @hide - */ - @Override - public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) - Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); - - // Check null in case the scan has been stopped - synchronized (this) { - if (mLeHandle <= 0) - return; - } - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - address); - long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos()); - ScanResult result = new ScanResult(device, advData, rssi, - scanMicros); - mScanCallback.onDeviceUpdate(result); - } - - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - // no op - } - - @Override - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - } - - /** - * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}. - * - * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices. - * @param settings Settings for ble scan. - * @param callback Callback when scan results are delivered. - * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. - */ - public void startScan(List filters, Settings settings, - final ScanCallback callback) { - if (settings == null || callback == null) { - throw new IllegalArgumentException("settings or callback is null"); - } - synchronized (mLeScanClients) { - if (mLeScanClients.get(settings) != null) { - postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED); - return; - } - BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, - settings, callback); - try { - UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.scanStarted()) { - mLeScanClients.put(settings, wrapper); - } else { - postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED); - return; - } - } catch (RemoteException e) { - Log.e(TAG, "GATT service exception when starting scan", e); - postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE); - } - } - } - - private void postCallbackError(final ScanCallback callback, final int errorCode) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onScanFailed(errorCode); - } - }); - } - - /** - * Stop Bluetooth LE scan. - * - * @param settings The same settings as used in {@link #startScan}, which is used to identify - * the BLE scan. - */ - public void stopScan(Settings settings) { - synchronized (mLeScanClients) { - BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings); - if (wrapper == null) { - return; - } - wrapper.stopLeScan(); - } - } - - /** - * Returns available storage size for batch scan results. It's recommended not to use batch scan - * if available storage size is small (less than 1k bytes, for instance). - * - * @hide TODO: unhide when batching is supported in stack. - */ - public int getAvailableBatchStorageSizeBytes() { - throw new UnsupportedOperationException("not impelemented"); - } - - /** - * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results - * batched on bluetooth controller. - * - * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one - * used to start scan. - * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will - * get batch scan callback if the batch scan buffer is flushed. - * @return Batch Scan results. - * @hide TODO: unhide when batching is supported in stack. - */ - public List getBatchScanResults(ScanCallback callback, boolean flush) { - throw new UnsupportedOperationException("not impelemented"); - } - -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 091b8063c1b..3dd70945138 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -17,10 +17,6 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothLeAdvertiseScanData; -import android.bluetooth.BluetoothLeAdvertiser; -import android.bluetooth.BluetoothLeScanFilter; -import android.bluetooth.BluetoothLeScanner; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -37,13 +33,8 @@ interface IBluetoothGatt { void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); void startScanWithUuidsScanParam(in int appIf, in boolean isServer, in ParcelUuid[] ids, int scanWindow, int scanInterval); - void startScanWithFilters(in int appIf, in boolean isServer, - in BluetoothLeScanner.Settings settings, - in List filters); void stopScan(in int appIf, in boolean isServer); - void startMultiAdvertising(in int appIf, in BluetoothLeAdvertiseScanData.AdvertisementData data, - in BluetoothLeAdvertiser.Settings settings); - void stopMultiAdvertising(in int appIf); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index bf9e0a70180..a78c29b0064 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,6 +64,5 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); - oneway void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java deleted file mode 100644 index eb6c419bad9..00000000000 --- a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2014 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.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -import java.util.Arrays; - -/** - * Unit test cases for {@link BluetoothLeAdvertiseScanData}. - *

        - * To run this test, use adb shell am instrument -e class - * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class BluetoothLeAdvertiseScanDataTest extends TestCase { - - @SmallTest - public void testParser() { - byte[] scanRecord = new byte[] { - 0x02, 0x01, 0x1a, // advertising flags - 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids - 0x04, 0x09, 0x50, 0x65, 0x64, // name - 0x02, 0x0A, (byte) 0xec, // tx power level - 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data - 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data - 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble - }; - BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord - .getParser().parseFromScanRecord(scanRecord); - assertEquals(0x1a, data.getAdvertiseFlags()); - ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - assertTrue(data.getServiceUuids().contains(uuid1)); - assertTrue(data.getServiceUuids().contains(uuid2)); - - assertEquals("Ped", data.getLocalName()); - assertEquals(-20, data.getTxPowerLevel()); - - assertEquals(224, data.getManufacturerId()); - assertArrayEquals(new byte[] { - (byte) 0xe0, 0x00, 0x02, 0x15 }, data.getManufacturerSpecificData()); - - assertEquals(uuid2, data.getServiceDataUuid()); - assertArrayEquals(new byte[] { - 0x0b, 0x11, 0x50, 0x64 }, data.getServiceData()); - } - - // Assert two byte arrays are equal. - private static void assertArrayEquals(byte[] expected, byte[] actual) { - if (!Arrays.equals(expected, actual)) { - fail("expected:<" + Arrays.toString(expected) + - "> but was:<" + Arrays.toString(actual) + ">"); - } - - } -} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java deleted file mode 100644 index ec35d85deb1..00000000000 --- a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for Bluetooth LE scan filters. - *

        - * To run this test, use adb shell am instrument -e class - * 'android.bluetooth.BluetoothLeScanFilterTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class BluetoothLeScanFilterTest extends TestCase { - - private static final String DEVICE_MAC = "01:02:03:04:05:AB"; - private ScanResult mScanResult; - private BluetoothLeScanFilter.Builder mFilterBuilder; - - @Override - protected void setUp() throws Exception { - byte[] scanRecord = new byte[] { - 0x02, 0x01, 0x1a, // advertising flags - 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids - 0x04, 0x09, 0x50, 0x65, 0x64, // name - 0x02, 0x0A, (byte) 0xec, // tx power level - 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data - 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data - 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble - }; - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); - mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L); - mFilterBuilder = BluetoothLeScanFilter.newBuilder(); - } - - @SmallTest - public void testNameFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build(); - assertTrue("name filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.name("Pem").build(); - assertFalse("name filter fails", filter.matches(mScanResult)); - - } - - @SmallTest - public void testDeviceFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build(); - assertTrue("device filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); - assertFalse("device filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testServiceUuidFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid( - ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build(); - assertTrue("uuid filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.serviceUuid( - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); - assertFalse("uuid filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder - .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")) - .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) - .build(); - assertTrue("uuid filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testServiceDataFilter() { - byte[] serviceData = new byte[] { - 0x0b, 0x11, 0x50, 0x64 }; - BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build(); - assertTrue("service data filter fails", filter.matches(mScanResult)); - - byte[] nonMatchData = new byte[] { - 0x0b, 0x01, 0x50, 0x64 }; - filter = mFilterBuilder.serviceData(nonMatchData).build(); - assertFalse("service data filter fails", filter.matches(mScanResult)); - - byte[] mask = new byte[] { - (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build(); - assertTrue("partial service data filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testManufacturerSpecificData() { - byte[] manufacturerData = new byte[] { - (byte) 0xE0, 0x00, 0x02, 0x15 }; - int manufacturerId = 224; - BluetoothLeScanFilter filter = - mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); - assertTrue("manufacturerData filter fails", filter.matches(mScanResult)); - - byte[] nonMatchData = new byte[] { - (byte) 0xF0, 0x00, 0x02, 0x15 }; - filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build(); - assertFalse("manufacturerData filter fails", filter.matches(mScanResult)); - - byte[] mask = new byte[] { - (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF - }; - filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData) - .manufacturerDataMask(mask).build(); - assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testReadWriteParcel() { - BluetoothLeScanFilter filter = mFilterBuilder.build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.name("Ped").build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.serviceUuid( - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.serviceUuidMask( - ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); - testReadWriteParcelForFilter(filter); - - byte[] serviceData = new byte[] { - 0x0b, 0x11, 0x50, 0x64 }; - - filter = mFilterBuilder.serviceData(serviceData).build(); - testReadWriteParcelForFilter(filter); - - byte[] serviceDataMask = new byte[] { - (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.serviceDataMask(serviceDataMask).build(); - testReadWriteParcelForFilter(filter); - - byte[] manufacturerData = new byte[] { - (byte) 0xE0, 0x00, 0x02, 0x15 }; - int manufacturerId = 224; - filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); - testReadWriteParcelForFilter(filter); - - byte[] manufacturerDataMask = new byte[] { - (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF - }; - filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build(); - testReadWriteParcelForFilter(filter); - } - - private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) { - Parcel parcel = Parcel.obtain(); - filter.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - BluetoothLeScanFilter filterFromParcel = - BluetoothLeScanFilter.CREATOR.createFromParcel(parcel); - System.out.println(filterFromParcel); - assertEquals(filter, filterFromParcel); - } -} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java deleted file mode 100644 index 8064ba8ac3b..00000000000 --- a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for Bluetooth LE scans. - *

        - * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest' - * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class BluetoothLeScannerTest extends TestCase { - - /** - * Test read and write parcel of ScanResult - */ - @SmallTest - public void testScanResultParceling() { - BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06"); - byte[] scanRecord = new byte[] { - 1, 2, 3 }; - int rssi = -10; - long timestampMicros = 10000L; - - ScanResult result = new ScanResult(device, scanRecord, rssi, timestampMicros); - Parcel parcel = Parcel.obtain(); - result.writeToParcel(parcel, 0); - // Need to reset parcel data position to the beginning. - parcel.setDataPosition(0); - ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel); - assertEquals(result, resultFromParcel); - } - -} -- GitLab From b464cfd7ac9eb108f89356a3764a6dfe247ce3b6 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 20 May 2014 06:30:20 +0000 Subject: [PATCH 0345/1408] Revert "Revert "APIs for BLE scan, scan filter, batch scan, onFound/onLost and multiple advertising."" This reverts commit b1d9fbc0f8dea0c77ed810190b325bfdaaf21789. Change-Id: Ic8dec9385a7c763170ebeb1bcddd221c72f46e88 --- .../android/bluetooth/BluetoothAdapter.java | 32 + .../java/android/bluetooth/BluetoothGatt.java | 10 +- .../BluetoothLeAdvertiseScanData.aidl | 19 + .../BluetoothLeAdvertiseScanData.java | 649 +++++++++++++++ .../bluetooth/BluetoothLeAdvertiser.aidl | 19 + .../bluetooth/BluetoothLeAdvertiser.java | 600 ++++++++++++++ .../bluetooth/BluetoothLeScanFilter.aidl | 19 + .../bluetooth/BluetoothLeScanFilter.java | 577 +++++++++++++ .../android/bluetooth/BluetoothLeScanner.aidl | 20 + .../android/bluetooth/BluetoothLeScanner.java | 759 ++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 11 +- .../bluetooth/IBluetoothGattCallback.aidl | 1 + .../BluetoothLeAdvertiseScanDataTest.java | 74 ++ .../bluetooth/BluetoothLeScanFilterTest.java | 185 +++++ .../bluetooth/BluetoothLeScannerTest.java | 53 ++ 15 files changed, 3026 insertions(+), 2 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.java create mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeScanFilter.java create mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.aidl create mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e79deeccbdc..9e1c995bbb5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -497,6 +497,34 @@ public final class BluetoothAdapter { } } + /** + * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. + */ + public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { + // TODO: Return null if this feature is not supported by hardware. + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return new BluetoothLeAdvertiser(iGatt); + } catch (RemoteException e) { + Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e); + return null; + } + } + + /** + * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. + */ + public BluetoothLeScanner getBluetoothLeScanner() { + // TODO: Return null if BLE scan is not supported by hardware. + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return new BluetoothLeScanner(iGatt); + } catch (RemoteException e) { + Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e); + return null; + } + } + /** * Interface for BLE advertising callback. * @@ -2024,6 +2052,10 @@ public final class BluetoothAdapter { } } + @Override + public void onMultiAdvertiseCallback(int status) { + // no op + } /** * Callback reporting LE ATT MTU. * @hide diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 601d9eee873..c9df9c0eede 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -581,7 +581,15 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); - } + } + + /** + * @hide + */ + @Override + public void onMultiAdvertiseCallback(int status) { + // no op. + } /** * Callback invoked when the MTU for a given connection changes diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl new file mode 100644 index 00000000000..4aa8881422d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java new file mode 100644 index 00000000000..3d85810a00e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement + * data to be advertised, or the scan record obtained from BLE scans. + *

        + * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth + * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth + * Core Specification Version 4. Currently the following fields are allowed to be set: + *

      • Service UUIDs which identify the bluetooth gatt services running on the device. + *
      • Tx power level which is the transmission power level. + *
      • Service data which is the data associated with a service. + *
      • Manufacturer specific data which is the data associated with a particular manufacturer. + * + * @see BluetoothLeAdvertiser + */ +public final class BluetoothLeAdvertiseScanData { + private static final String TAG = "BluetoothLeAdvertiseScanData"; + + /** + * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising + * packet. + */ + public static final int ADVERTISING_DATA = 0; + /** + * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising + * packet. + *

        + * TODO: unhide when stack supports setting scan response data. + * + * @hide + */ + public static final int SCAN_RESPONSE_DATA = 1; + /** + * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of + * advertising data and scan response data. + */ + public static final int PARSED_SCAN_RECORD = 2; + + /** + * Base data type which contains the common fields for {@link AdvertisementData} and + * {@link ScanRecord}. + */ + public abstract static class AdvertiseBaseData { + + private final int mDataType; + + @Nullable + private final List mServiceUuids; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; + + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; + + private AdvertiseBaseData(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData) { + mDataType = dataType; + mServiceUuids = serviceUuids; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + } + + /** + * Returns the type of data, indicating whether the data is advertising data, scan response + * data or scan record. + */ + public int getDataType() { + return mDataType; + } + + /** + * Returns a list of service uuids within the advertisement that are used to identify the + * bluetooth gatt services. + */ + public List getServiceUuids() { + return mServiceUuids; + } + + /** + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + /** + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. The first 2 bytes of the data contain the company id. + */ + public byte[] getManufacturerSpecificData() { + return mManufacturerSpecificData; + } + + /** + * Returns a 16 bit uuid of the service that the service data is associated with. + */ + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + + /** + * Returns service data. The first two bytes should be a 16 bit service uuid associated with + * the service data. + */ + public byte[] getServiceData() { + return mServiceData; + } + + @Override + public String toString() { + return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids + + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]"; + } + } + + /** + * Advertisement data packet for Bluetooth LE advertising. This represents the data to be + * broadcasted in Bluetooth LE advertising. + *

        + * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to + * be advertised. + * + * @see BluetoothLeAdvertiser + */ + public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable { + + private boolean mIncludeTxPowerLevel; + + /** + * Whether the transmission power level will be included in the advertisement packet. + */ + public boolean getIncludeTxPowerLevel() { + return mIncludeTxPowerLevel; + } + + /** + * Returns a {@link Builder} to build {@link AdvertisementData}. + */ + public static Builder newBuilder() { + return new Builder(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getDataType()); + List uuids = getServiceUuids(); + if (uuids == null) { + dest.writeInt(0); + } else { + dest.writeInt(uuids.size()); + dest.writeList(uuids); + } + + dest.writeInt(getManufacturerId()); + byte[] manufacturerData = getManufacturerSpecificData(); + if (manufacturerData == null) { + dest.writeInt(0); + } else { + dest.writeInt(manufacturerData.length); + dest.writeByteArray(manufacturerData); + } + + ParcelUuid serviceDataUuid = getServiceDataUuid(); + if (serviceDataUuid == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeParcelable(serviceDataUuid, flags); + byte[] serviceData = getServiceData(); + if (serviceData == null) { + dest.writeInt(0); + } else { + dest.writeInt(serviceData.length); + dest.writeByteArray(serviceData); + } + } + dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); + } + + private AdvertisementData(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) { + super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, + manufacturerSpecificData); + this.mIncludeTxPowerLevel = mIncludeTxPowerLevel; + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertisementData[] newArray(int size) { + return new AdvertisementData[size]; + } + + @Override + public AdvertisementData createFromParcel(Parcel in) { + Builder builder = newBuilder(); + int dataType = in.readInt(); + builder.dataType(dataType); + if (in.readInt() > 0) { + List uuids = new ArrayList(); + in.readList(uuids, ParcelUuid.class.getClassLoader()); + builder.serviceUuids(uuids); + } + int manufacturerId = in.readInt(); + int manufacturerDataLength = in.readInt(); + if (manufacturerDataLength > 0) { + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.manufacturerData(manufacturerId, manufacturerData); + } + if (in.readInt() == 1) { + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.serviceData(serviceDataUuid, serviceData); + } + } + builder.includeTxPowerLevel(in.readByte() == 1); + return builder.build(); + } + }; + + /** + * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use + * {@link AdvertisementData#newBuilder()} to get an instance of the Builder. + */ + public static final class Builder { + private static final int MAX_ADVERTISING_DATA_BYTES = 31; + // Each fields need one byte for field length and another byte for field type. + private static final int OVERHEAD_BYTES_PER_FIELD = 2; + // Flags field will be set by system. + private static final int FLAGS_FIELD_BYTES = 3; + + private int mDataType; + @Nullable + private List mServiceUuids; + private boolean mIncludeTxPowerLevel; + private int mManufacturerId; + @Nullable + private byte[] mManufacturerSpecificData; + @Nullable + private ParcelUuid mServiceDataUuid; + @Nullable + private byte[] mServiceData; + + /** + * Set data type. + * + * @param dataType Data type, could only be + * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} or + * {@link BluetoothLeAdvertiseScanData#SCAN_RESPONSE_DATA} + * @throws IllegalArgumentException If the {@code dataType} is invalid. + */ + public Builder dataType(int dataType) { + if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) { + throw new IllegalArgumentException("invalid data type - " + dataType); + } + mDataType = dataType; + return this; + } + + /** + * Set the service uuids. Note the corresponding bluetooth Gatt services need to be + * already added on the device before start BLE advertising. + * + * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or + * 128-bit uuids. + * @throws IllegalArgumentException If the {@code serviceUuids} are null. + */ + public Builder serviceUuids(List serviceUuids) { + if (serviceUuids == null) { + throw new IllegalArgumentException("serivceUuids are null"); + } + mServiceUuids = serviceUuids; + return this; + } + + /** + * Add service data to advertisement. + * + * @param serviceDataUuid A 16 bit uuid of the service data + * @param serviceData Service data - the first two bytes of the service data are the + * service data uuid. + * @throws IllegalArgumentException If the {@code serviceDataUuid} or + * {@code serviceData} is empty. + */ + public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + if (serviceDataUuid == null || serviceData == null) { + throw new IllegalArgumentException( + "serviceDataUuid or serviceDataUuid is null"); + } + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + return this; + } + + /** + * Set manufacturer id and data. See assigned + * manufacturer identifies for the existing company identifiers. + * + * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. + * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of + * the manufacturer specific data are the manufacturer id. + * @throws IllegalArgumentException If the {@code manufacturerId} is negative or + * {@code manufacturerSpecificData} is null. + */ + public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + if (manufacturerId < 0) { + throw new IllegalArgumentException( + "invalid manufacturerId - " + manufacturerId); + } + if (manufacturerSpecificData == null) { + throw new IllegalArgumentException("manufacturerSpecificData is null"); + } + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + return this; + } + + /** + * Whether the transmission power level should be included in the advertising packet. + */ + public Builder includeTxPowerLevel(boolean includeTxPowerLevel) { + mIncludeTxPowerLevel = includeTxPowerLevel; + return this; + } + + /** + * Build the {@link BluetoothLeAdvertiseScanData}. + * + * @throws IllegalArgumentException If the data size is larger than 31 bytes. + */ + public AdvertisementData build() { + if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException( + "advertisement data size is larger than 31 bytes"); + } + return new AdvertisementData(mDataType, + mServiceUuids, + mServiceDataUuid, + mServiceData, mManufacturerId, mManufacturerSpecificData, + mIncludeTxPowerLevel); + } + + // Compute the size of the advertisement data. + private int totalBytes() { + int size = FLAGS_FIELD_BYTES; // flags field is always set. + if (mServiceUuids != null) { + int num16BitUuids = 0; + int num32BitUuids = 0; + int num128BitUuids = 0; + for (ParcelUuid uuid : mServiceUuids) { + if (BluetoothUuid.is16BitUuid(uuid)) { + ++num16BitUuids; + } else if (BluetoothUuid.is32BitUuid(uuid)) { + ++num32BitUuids; + } else { + ++num128BitUuids; + } + } + // 16 bit service uuids are grouped into one field when doing advertising. + if (num16BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + } + // 32 bit service uuids are grouped into one field when doing advertising. + if (num32BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + } + // 128 bit service uuids are grouped into one field when doing advertising. + if (num128BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + } + } + if (mServiceData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; + } + if (mManufacturerSpecificData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; + } + if (mIncludeTxPowerLevel) { + size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. + } + return size; + } + } + + } + + /** + * Represents a scan record from Bluetooth LE scan. + */ + public static final class ScanRecord extends AdvertiseBaseData { + // Flags of the advertising data. + private final int mAdvertiseFlags; + + // Transmission power level(in dB). + private final int mTxPowerLevel; + + // Local name of the Bluetooth LE device. + private final String mLocalName; + + /** + * Returns the advertising flags indicating the discoverable mode and capability of the + * device. Returns -1 if the flag field is not set. + */ + public int getAdvertiseFlags() { + return mAdvertiseFlags; + } + + /** + * Returns the transmission power level of the packet in dBm. Returns + * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate + * the path loss of a received packet using the following equation: + *

        + * pathloss = txPowerLevel - rssi + */ + public int getTxPowerLevel() { + return mTxPowerLevel; + } + + /** + * Returns the local name of the BLE device. The is a UTF-8 encoded string. + */ + @Nullable + public String getLocalName() { + return mLocalName; + } + + ScanRecord(int dataType, + List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, + String localName) { + super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, + manufacturerSpecificData); + mLocalName = localName; + mAdvertiseFlags = advertiseFlags; + mTxPowerLevel = txPowerLevel; + } + + /** + * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}. + */ + public static Parser getParser() { + return new Parser(); + } + + /** + * A parser class used to parse a Bluetooth LE scan record to + * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed. + */ + public static final class Parser { + private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser"; + + // The following data type values are assigned by Bluetooth SIG. + // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18. + private static final int DATA_TYPE_FLAGS = 0x01; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; + private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; + private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; + private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; + private static final int DATA_TYPE_SERVICE_DATA = 0x16; + private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; + + // Helper method to extract bytes from byte array. + private static byte[] extractBytes(byte[] scanRecord, int start, int length) { + byte[] bytes = new byte[length]; + System.arraycopy(scanRecord, start, bytes, 0, length); + return bytes; + } + + /** + * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}. + *

        + * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * and 18. + *

        + * All numerical multi-byte entities and values shall use little-endian + * byte order. + * + * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. + */ + public ScanRecord parseFromScanRecord(byte[] scanRecord) { + if (scanRecord == null) { + return null; + } + + int currentPos = 0; + int advertiseFlag = -1; + List serviceUuids = new ArrayList(); + String localName = null; + int txPowerLevel = Integer.MIN_VALUE; + ParcelUuid serviceDataUuid = null; + byte[] serviceData = null; + int manufacturerId = -1; + byte[] manufacturerSpecificData = null; + + try { + while (currentPos < scanRecord.length) { + // length is unsigned int. + int length = scanRecord[currentPos++] & 0xFF; + if (length == 0) { + break; + } + // Note the length includes the length of the field type itself. + int dataLength = length - 1; + // fieldType is unsigned int. + int fieldType = scanRecord[currentPos++] & 0xFF; + switch (fieldType) { + case DATA_TYPE_FLAGS: + advertiseFlag = scanRecord[currentPos] & 0xFF; + break; + case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, + dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); + break; + case DATA_TYPE_LOCAL_NAME_SHORT: + case DATA_TYPE_LOCAL_NAME_COMPLETE: + localName = new String( + extractBytes(scanRecord, currentPos, dataLength)); + break; + case DATA_TYPE_TX_POWER_LEVEL: + txPowerLevel = scanRecord[currentPos]; + break; + case DATA_TYPE_SERVICE_DATA: + serviceData = extractBytes(scanRecord, currentPos, dataLength); + // The first two bytes of the service data are service data uuid. + int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; + byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, + serviceUuidLength); + serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); + break; + case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: + manufacturerSpecificData = extractBytes(scanRecord, currentPos, + dataLength); + // The first two bytes of the manufacturer specific data are + // manufacturer ids in little endian. + manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + + (manufacturerSpecificData[0] & 0xFF); + break; + default: + // Just ignore, we don't handle such data type. + break; + } + currentPos += dataLength; + } + + if (serviceUuids.isEmpty()) { + serviceUuids = null; + } + return new ScanRecord(PARSED_SCAN_RECORD, + serviceUuids, serviceDataUuid, serviceData, + manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, + localName); + } catch (IndexOutOfBoundsException e) { + Log.e(PARSER_TAG, + "unable to parse scan record: " + Arrays.toString(scanRecord)); + return null; + } + } + + // Parse service uuids. + private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, + int uuidLength, List serviceUuids) { + while (dataLength > 0) { + byte[] uuidBytes = extractBytes(scanRecord, currentPos, + uuidLength); + serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); + dataLength -= uuidLength; + currentPos += uuidLength; + } + return currentPos; + } + } + } + +} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl new file mode 100644 index 00000000000..31086105610 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java new file mode 100644 index 00000000000..2a8aa23b28a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop + * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by + * {@link BluetoothLeAdvertiseScanData.AdvertisementData}. + *

        + * To get an instance of {@link BluetoothLeAdvertiser}, call the + * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. + *

        + * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see BluetoothLeAdvertiseScanData.AdvertisementData + */ +public class BluetoothLeAdvertiser { + + private static final String TAG = "BluetoothLeAdvertiser"; + + /** + * The {@link Settings} provide a way to adjust advertising preferences for each individual + * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance. + */ + public static final class Settings implements Parcelable { + /** + * Perform Bluetooth LE advertising in low power mode. This is the default and preferred + * advertising mode as it consumes the least power. + */ + public static final int ADVERTISE_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE advertising in balanced power mode. This is balanced between + * advertising frequency and power consumption. + */ + public static final int ADVERTISE_MODE_BALANCED = 1; + /** + * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest + * power consumption and should not be used for background continuous advertising. + */ + public static final int ADVERTISE_MODE_LOW_LATENCY = 2; + + /** + * Advertise using the lowest transmission(tx) power level. An app can use low transmission + * power to restrict the visibility range of its advertising packet. + */ + public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; + /** + * Advertise using low tx power level. + */ + public static final int ADVERTISE_TX_POWER_LOW = 1; + /** + * Advertise using medium tx power level. + */ + public static final int ADVERTISE_TX_POWER_MEDIUM = 2; + /** + * Advertise using high tx power level. This is corresponding to largest visibility range of + * the advertising packet. + */ + public static final int ADVERTISE_TX_POWER_HIGH = 3; + + /** + * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0 + * vol6, part B, section 4.4.2 - Advertising state. + */ + public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; + /** + * Scannable undirected advertise type, as defined in same spec mentioned above. This event + * type allows a scanner to send a scan request asking additional information about the + * advertiser. + */ + public static final int ADVERTISE_TYPE_SCANNABLE = 1; + /** + * Connectable undirected advertising type, as defined in same spec mentioned above. This + * event type allows a scanner to send scan request asking additional information about the + * advertiser. It also allows an initiator to send a connect request for connection. + */ + public static final int ADVERTISE_TYPE_CONNECTABLE = 2; + + private final int mAdvertiseMode; + private final int mAdvertiseTxPowerLevel; + private final int mAdvertiseEventType; + + private Settings(int advertiseMode, int advertiseTxPowerLevel, + int advertiseEventType) { + mAdvertiseMode = advertiseMode; + mAdvertiseTxPowerLevel = advertiseTxPowerLevel; + mAdvertiseEventType = advertiseEventType; + } + + private Settings(Parcel in) { + mAdvertiseMode = in.readInt(); + mAdvertiseTxPowerLevel = in.readInt(); + mAdvertiseEventType = in.readInt(); + } + + /** + * Creates a {@link Builder} to construct a {@link Settings} object. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Returns the advertise mode. + */ + public int getMode() { + return mAdvertiseMode; + } + + /** + * Returns the tx power level for advertising. + */ + public int getTxPowerLevel() { + return mAdvertiseTxPowerLevel; + } + + /** + * Returns the advertise event type. + */ + public int getType() { + return mAdvertiseEventType; + } + + @Override + public String toString() { + return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAdvertiseMode); + dest.writeInt(mAdvertiseTxPowerLevel); + dest.writeInt(mAdvertiseEventType); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public Settings[] newArray(int size) { + return new Settings[size]; + } + + @Override + public Settings createFromParcel(Parcel in) { + return new Settings(in); + } + }; + + /** + * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use + * {@link Settings#newBuilder()} to get an instance of the builder. + */ + public static final class Builder { + private int mMode = ADVERTISE_MODE_LOW_POWER; + private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; + private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; + + // Private constructor, use Settings.newBuilder() get an instance of BUILDER. + private Builder() { + } + + /** + * Set advertise mode to control the advertising power and latency. + * + * @param advertiseMode Bluetooth LE Advertising mode, can only be one of + * {@link Settings#ADVERTISE_MODE_LOW_POWER}, + * {@link Settings#ADVERTISE_MODE_BALANCED}, or + * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the advertiseMode is invalid. + */ + public Builder advertiseMode(int advertiseMode) { + if (advertiseMode < ADVERTISE_MODE_LOW_POWER + || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("unknown mode " + advertiseMode); + } + mMode = advertiseMode; + return this; + } + + /** + * Set advertise tx power level to control the transmission power level for the + * advertising. + * + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one + * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW}, + * {@link Settings#ADVERTISE_TX_POWER_LOW}, + * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or + * {@link Settings#ADVERTISE_TX_POWER_HIGH}. + * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. + */ + public Builder txPowerLevel(int txPowerLevel) { + if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW + || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { + throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); + } + mTxPowerLevel = txPowerLevel; + return this; + } + + /** + * Set advertise type to control the event type of advertising. + * + * @param type Bluetooth LE Advertising type, can be either + * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE}, + * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or + * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}. + * @throws IllegalArgumentException If the {@code type} is invalid. + */ + public Builder type(int type) { + if (type < ADVERTISE_TYPE_NON_CONNECTABLE + || type > ADVERTISE_TYPE_CONNECTABLE) { + throw new IllegalArgumentException("unknown advertise type " + type); + } + mType = type; + return this; + } + + /** + * Build the {@link Settings} object. + */ + public Settings build() { + return new Settings(mMode, mTxPowerLevel, mType); + } + } + } + + /** + * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and + * stop advertising. + */ + public interface AdvertiseCallback { + + /** + * The operation is success. + * + * @hide + */ + public static final int SUCCESS = 0; + /** + * Fails to start advertising as the advertisement data contains services that are not added + * to the local bluetooth Gatt server. + */ + public static final int ADVERTISING_SERVICE_UNKNOWN = 1; + /** + * Fails to start advertising as system runs out of quota for advertisers. + */ + public static final int TOO_MANY_ADVERTISERS = 2; + + /** + * Fails to start advertising as the advertising is already started. + */ + public static final int ADVERTISING_ALREADY_STARTED = 3; + /** + * Fails to stop advertising as the advertising is not started. + */ + public static final int ADVERISING_NOT_STARTED = 4; + + /** + * Operation fails due to bluetooth controller failure. + */ + public static final int CONTROLLER_FAILURE = 5; + + /** + * Callback when advertising operation succeeds. + * + * @param settingsInEffect The actual settings used for advertising, which may be different + * from what the app asks. + */ + public void onSuccess(Settings settingsInEffect); + + /** + * Callback when advertising operation fails. + * + * @param errorCode Error code for failures. + */ + public void onFailure(int errorCode); + } + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map + mLeAdvertisers = new HashMap(); + + // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead. + BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + } + + /** + * Bluetooth GATT interface callbacks for advertising. + */ + private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; + private final AdvertiseCallback mAdvertiseCallback; + private final AdvertisementData mAdvertisement; + private final Settings mSettings; + private final IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // >0: registered and scan started + private int mLeHandle; + private boolean isAdvertising = false; + + public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, + AdvertisementData advertiseData, Settings settings, + IBluetoothGatt bluetoothGatt) { + mAdvertiseCallback = advertiseCallback; + mAdvertisement = advertiseData; + mSettings = settings; + mBluetoothGatt = bluetoothGatt; + mLeHandle = 0; + } + + public boolean advertiseStarted() { + boolean started = false; + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + started = (mLeHandle > 0 && isAdvertising); + } + return started; + } + + public boolean advertiseStopped() { + synchronized (this) { + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + return !isAdvertising; + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + synchronized (this) { + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, mSettings); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le advertise: " + e); + mLeHandle = -1; + notifyAll(); + } catch (Exception e) { + Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); + } + } else { + // registration failed + mLeHandle = -1; + notifyAll(); + } + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + // no op + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + synchronized (this) { + if (status == 0) { + isAdvertising = !isAdvertising; + if (!isAdvertising) { + try { + mBluetoothGatt.unregisterClient(mLeHandle); + mLeHandle = -1; + } catch (RemoteException e) { + Log.e(TAG, "remote exception when unregistering", e); + } + } + mAdvertiseCallback.onSuccess(null); + } else { + mAdvertiseCallback.onFailure(status); + } + notifyAll(); + } + + } + + /** + * Callback reporting LE ATT MTU. + * + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + /** + * Start Bluetooth LE Advertising. + * + * @param settings {@link Settings} for Bluetooth LE advertising. + * @param advertiseData {@link AdvertisementData} to be advertised. + * @param callback {@link AdvertiseCallback} for advertising status. + */ + public void startAdvertising(Settings settings, + AdvertisementData advertiseData, final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + if (mLeAdvertisers.containsKey(settings)) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED); + return; + } + AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, + settings, + mBluetoothGatt); + UUID uuid = UUID.randomUUID(); + try { + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.advertiseStarted()) { + mLeAdvertisers.put(settings, wrapper); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + /** + * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered + * through the {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param settings {@link Settings} used to start Bluetooth LE advertising. + * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. + */ + public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings); + if (wrapper == null) { + postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED); + return; + } + try { + mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); + if (wrapper.advertiseStopped()) { + mLeAdvertisers.remove(settings); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + private void postCallbackFailure(final AdvertiseCallback callback, final int error) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onFailure(error); + } + }); + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl b/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl new file mode 100644 index 00000000000..86ee06d3942 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeScanFilter; diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.java b/framework/java/android/bluetooth/BluetoothLeScanFilter.java new file mode 100644 index 00000000000..2ed85ba0a70 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanFilter.java @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord; +import android.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement + * packet fields. + *

        + * Current filtering on the following fields are supported: + *

      • Service UUIDs which identify the bluetooth gatt services running on the device. + *
      • Name of remote Bluetooth LE device. + *
      • Mac address of the remote device. + *
      • Rssi which indicates the received power level. + *
      • Service data which is the data associated with a service. + *
      • Manufacturer specific data which is the data associated with a particular manufacturer. + * + * @see BluetoothLeAdvertiseScanData.ScanRecord + * @see BluetoothLeScanner + */ +public final class BluetoothLeScanFilter implements Parcelable { + + @Nullable + private final String mLocalName; + + @Nullable + private final String mMacAddress; + + @Nullable + private final ParcelUuid mServiceUuid; + @Nullable + private final ParcelUuid mServiceUuidMask; + + @Nullable + private final byte[] mServiceData; + @Nullable + private final byte[] mServiceDataMask; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerData; + @Nullable + private final byte[] mManufacturerDataMask; + + private final int mMinRssi; + private final int mMaxRssi; + + private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid, + ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, + int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, + int minRssi, int maxRssi) { + mLocalName = name; + mServiceUuid = uuid; + mServiceUuidMask = uuidMask; + mMacAddress = macAddress; + mServiceData = serviceData; + mServiceDataMask = serviceDataMask; + mManufacturerId = manufacturerId; + mManufacturerData = manufacturerData; + mManufacturerDataMask = manufacturerDataMask; + mMinRssi = minRssi; + mMaxRssi = maxRssi; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mLocalName == null ? 0 : 1); + if (mLocalName != null) { + dest.writeString(mLocalName); + } + dest.writeInt(mMacAddress == null ? 0 : 1); + if (mMacAddress != null) { + dest.writeString(mMacAddress); + } + dest.writeInt(mServiceUuid == null ? 0 : 1); + if (mServiceUuid != null) { + dest.writeParcelable(mServiceUuid, flags); + } + dest.writeInt(mServiceUuidMask == null ? 0 : 1); + if (mServiceUuidMask != null) { + dest.writeParcelable(mServiceUuidMask, flags); + } + dest.writeInt(mServiceData == null ? 0 : mServiceData.length); + if (mServiceData != null) { + dest.writeByteArray(mServiceData); + } + dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); + if (mServiceDataMask != null) { + dest.writeByteArray(mServiceDataMask); + } + dest.writeInt(mManufacturerId); + dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length); + if (mManufacturerData != null) { + dest.writeByteArray(mManufacturerData); + } + dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); + if (mManufacturerDataMask != null) { + dest.writeByteArray(mManufacturerDataMask); + } + dest.writeInt(mMinRssi); + dest.writeInt(mMaxRssi); + } + + /** + * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel. + */ + public static final Creator + CREATOR = new Creator() { + + @Override + public BluetoothLeScanFilter[] newArray(int size) { + return new BluetoothLeScanFilter[size]; + } + + @Override + public BluetoothLeScanFilter createFromParcel(Parcel in) { + Builder builder = newBuilder(); + if (in.readInt() == 1) { + builder.name(in.readString()); + } + if (in.readInt() == 1) { + builder.macAddress(in.readString()); + } + if (in.readInt() == 1) { + ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); + builder.serviceUuid(uuid); + } + if (in.readInt() == 1) { + ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader()); + builder.serviceUuidMask(uuidMask); + } + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.serviceData(serviceData); + } + int serviceDataMaskLength = in.readInt(); + if (serviceDataMaskLength > 0) { + byte[] serviceDataMask = new byte[serviceDataMaskLength]; + in.readByteArray(serviceDataMask); + builder.serviceDataMask(serviceDataMask); + } + int manufacturerId = in.readInt(); + int manufacturerDataLength = in.readInt(); + if (manufacturerDataLength > 0) { + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.manufacturerData(manufacturerId, manufacturerData); + } + int manufacturerDataMaskLength = in.readInt(); + if (manufacturerDataMaskLength > 0) { + byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; + in.readByteArray(manufacturerDataMask); + builder.manufacturerDataMask(manufacturerDataMask); + } + int minRssi = in.readInt(); + int maxRssi = in.readInt(); + builder.rssiRange(minRssi, maxRssi); + return builder.build(); + } + }; + + /** + * Returns the filter set the local name field of Bluetooth advertisement data. + */ + @Nullable + public String getLocalName() { + return mLocalName; + } + + @Nullable /** + * Returns the filter set on the service uuid. + */ + public ParcelUuid getServiceUuid() { + return mServiceUuid; + } + + @Nullable + public ParcelUuid getServiceUuidMask() { + return mServiceUuidMask; + } + + @Nullable + public String getDeviceAddress() { + return mMacAddress; + } + + @Nullable + public byte[] getServiceData() { + return mServiceData; + } + + @Nullable + public byte[] getServiceDataMask() { + return mServiceDataMask; + } + + /** + * Returns the manufacturer id. -1 if the manufacturer filter is not set. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + @Nullable + public byte[] getManufacturerData() { + return mManufacturerData; + } + + @Nullable + public byte[] getManufacturerDataMask() { + return mManufacturerDataMask; + } + + /** + * Returns minimum value of rssi for the scan filter. {@link Integer#MIN_VALUE} if not set. + */ + public int getMinRssi() { + return mMinRssi; + } + + /** + * Returns maximum value of the rssi for the scan filter. {@link Integer#MAX_VALUE} if not set. + */ + public int getMaxRssi() { + return mMaxRssi; + } + + /** + * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match + * if it matches all the field filters. + */ + public boolean matches(ScanResult scanResult) { + if (scanResult == null) { + return false; + } + BluetoothDevice device = scanResult.getDevice(); + // Device match. + if (mMacAddress != null && (device == null || !mMacAddress.equals(device.getAddress()))) { + return false; + } + + int rssi = scanResult.getRssi(); + if (rssi < mMinRssi || rssi > mMaxRssi) { + return false; + } + + byte[] scanRecordBytes = scanResult.getScanRecord(); + ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes); + + // Scan record is null but there exist filters on it. + if (scanRecord == null + && (mLocalName != null || mServiceUuid != null || mManufacturerData != null + || mServiceData != null)) { + return false; + } + + // Local name match. + if (mLocalName != null && !mLocalName.equals(scanRecord.getLocalName())) { + return false; + } + + // UUID match. + if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, + scanRecord.getServiceUuids())) { + return false; + } + + // Service data match + if (mServiceData != null && + !matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData())) { + return false; + } + + // Manufacturer data match. + if (mManufacturerData != null && !matchesPartialData(mManufacturerData, + mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { + return false; + } + // All filters match. + return true; + } + + // Check if the uuid pattern is contained in a list of parcel uuids. + private boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, + List uuids) { + if (uuid == null) { + return true; + } + if (uuids == null) { + return false; + } + + for (ParcelUuid parcelUuid : uuids) { + UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); + if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { + return true; + } + } + return false; + } + + // Check if the uuid pattern matches the particular service uuid. + private boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { + if (mask == null) { + return uuid.equals(data); + } + if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != + (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { + return false; + } + return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == + (data.getMostSignificantBits() & mask.getMostSignificantBits())); + } + + // Check whether the data pattern matches the parsed data. + private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { + if (dataMask == null) { + return Arrays.equals(data, parsedData); + } + if (parsedData == null) { + return false; + } + for (int i = 0; i < data.length; ++i) { + if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "BluetoothLeScanFilter [mLocalName=" + mLocalName + ", mMacAddress=" + mMacAddress + + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" + + Arrays.toString(mServiceData) + ", mServiceDataMask=" + + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + + ", mManufacturerData=" + Arrays.toString(mManufacturerData) + + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + + ", mMinRssi=" + mMinRssi + ", mMaxRssi=" + mMaxRssi + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mLocalName, mMacAddress, mManufacturerId, mManufacturerData, + mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask, + mServiceUuid, mServiceUuidMask); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj; + return Objects.equals(mLocalName, other.mLocalName) && + Objects.equals(mMacAddress, other.mMacAddress) && + mManufacturerId == other.mManufacturerId && + Objects.deepEquals(mManufacturerData, other.mManufacturerData) && + Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && + mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && + Objects.deepEquals(mServiceData, other.mServiceData) && + Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && + Objects.equals(mServiceUuid, other.mServiceUuid) && + Objects.equals(mServiceUuidMask, other.mServiceUuidMask); + } + + /** + * Returns the {@link Builder} for {@link BluetoothLeScanFilter}. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder class for {@link BluetoothLeScanFilter}. Use + * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}. + */ + public static class Builder { + + private String mLocalName; + private String mMacAddress; + + private ParcelUuid mServiceUuid; + private ParcelUuid mUuidMask; + + private byte[] mServiceData; + private byte[] mServiceDataMask; + + private int mManufacturerId = -1; + private byte[] mManufacturerData; + private byte[] mManufacturerDataMask; + + private int mMinRssi = Integer.MIN_VALUE; + private int mMaxRssi = Integer.MAX_VALUE; + + // Private constructor, use BluetoothLeScanFilter.newBuilder instead. + private Builder() { + } + + /** + * Set filtering on local name. + */ + public Builder name(String localName) { + mLocalName = localName; + return this; + } + + /** + * Set filtering on device mac address. + * + * @param macAddress The device mac address for the filter. It needs to be in the format of + * "01:02:03:AB:CD:EF". The mac address can be validated using + * {@link BluetoothAdapter#checkBluetoothAddress}. + * @throws IllegalArgumentException If the {@code macAddress} is invalid. + */ + public Builder macAddress(String macAddress) { + if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) { + throw new IllegalArgumentException("invalid mac address " + macAddress); + } + mMacAddress = macAddress; + return this; + } + + /** + * Set filtering on service uuid. + */ + public Builder serviceUuid(ParcelUuid serviceUuid) { + mServiceUuid = serviceUuid; + return this; + } + + /** + * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set + * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate + * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit. + *

        + * The length of {@code uuidMask} must be the same as {@code serviceUuid}. + */ + public Builder serviceUuidMask(ParcelUuid uuidMask) { + mUuidMask = uuidMask; + return this; + } + + /** + * Set service data filter. + */ + public Builder serviceData(byte[] serviceData) { + mServiceData = serviceData; + return this; + } + + /** + * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it + * needs to match the one in service data, otherwise set it to 0 to ignore that bit. + *

        + * The {@code serviceDataMask} must have the same length of the {@code serviceData} set + * through {@link #serviceData(byte[])}. + */ + public Builder serviceDataMask(byte[] serviceDataMask) { + mServiceDataMask = serviceDataMask; + return this; + } + + /** + * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as + * invalid id. + *

        + * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. + */ + public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) { + if (manufacturerData != null && manufacturerId < 0) { + throw new IllegalArgumentException("invalid manufacture id"); + } + mManufacturerId = manufacturerId; + mManufacturerData = manufacturerData; + return this; + } + + /** + * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it + * needs to match the one in manufacturer data, otherwise set it to 0. + *

        + * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData} + * set through {@link #manufacturerData(int, byte[])}. + */ + public Builder manufacturerDataMask(byte[] manufacturerDataMask) { + mManufacturerDataMask = manufacturerDataMask; + return this; + } + + /** + * Set the desired rssi range for the filter. A scan result with rssi in the range of + * [minRssi, maxRssi] will be consider as a match. + */ + public Builder rssiRange(int minRssi, int maxRssi) { + mMinRssi = minRssi; + mMaxRssi = maxRssi; + return this; + } + + /** + * Build {@link BluetoothLeScanFilter}. + * + * @throws IllegalArgumentException If the filter cannot be built. + */ + public BluetoothLeScanFilter build() { + if (mUuidMask != null && mServiceUuid == null) { + throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); + } + + if (mServiceDataMask != null) { + if (mServiceData == null) { + throw new IllegalArgumentException( + "serviceData is null while serviceDataMask is not null"); + } + // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two + // byte array need to be the same. + if (mServiceData.length != mServiceDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for service data and service data mask"); + } + } + + if (mManufacturerDataMask != null) { + if (mManufacturerData == null) { + throw new IllegalArgumentException( + "manufacturerData is null while manufacturerDataMask is not null"); + } + // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths + // of the two byte array need to be the same. + if (mManufacturerData.length != mManufacturerDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for manufacturerData and manufacturerDataMask"); + } + } + return new BluetoothLeScanFilter(mLocalName, mMacAddress, + mServiceUuid, mUuidMask, + mServiceData, mServiceDataMask, + mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.aidl b/framework/java/android/bluetooth/BluetoothLeScanner.aidl new file mode 100644 index 00000000000..8cecdd7aac0 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanner.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothLeScanner.ScanResult; +parcelable BluetoothLeScanner.Settings; diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.java b/framework/java/android/bluetooth/BluetoothLeScanner.java new file mode 100644 index 00000000000..ed3188b1c16 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeScanner.java @@ -0,0 +1,759 @@ +/* + * Copyright (C) 2014 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.annotation.Nullable; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * This class provides methods to perform scan related operations for Bluetooth LE devices. An + * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It + * can also request different types of callbacks for delivering the result. + *

        + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of + * {@link BluetoothLeScanner}. + *

        + * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see BluetoothLeScanFilter + */ +public class BluetoothLeScanner { + + private static final String TAG = "BluetoothLeScanner"; + private static final boolean DBG = true; + + /** + * Settings for Bluetooth LE scan. + */ + public static final class Settings implements Parcelable { + /** + * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes + * the least power. + */ + public static final int SCAN_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE scan in balanced power mode. + */ + public static final int SCAN_MODE_BALANCED = 1; + /** + * Scan using highest duty cycle. It's recommended only using this mode when the application + * is running in foreground. + */ + public static final int SCAN_MODE_LOW_LATENCY = 2; + + /** + * Callback each time when a bluetooth advertisement is found. + */ + public static final int CALLBACK_TYPE_ON_UPDATE = 0; + /** + * Callback when a bluetooth advertisement is found for the first time. + */ + public static final int CALLBACK_TYPE_ON_FOUND = 1; + /** + * Callback when a bluetooth advertisement is found for the first time, then lost. + */ + public static final int CALLBACK_TYPE_ON_LOST = 2; + + /** + * Full scan result which contains device mac address, rssi, advertising and scan response + * and scan timestamp. + */ + public static final int SCAN_RESULT_TYPE_FULL = 0; + /** + * Truncated scan result which contains device mac address, rssi and scan timestamp. Note + * it's possible for an app to get more scan results that it asks if there are multiple apps + * using this type. TODO: decide whether we could unhide this setting. + * + * @hide + */ + public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; + + // Bluetooth LE scan mode. + private int mScanMode; + + // Bluetooth LE scan callback type + private int mCallbackType; + + // Bluetooth LE scan result type + private int mScanResultType; + + // Time of delay for reporting the scan result + private long mReportDelayMicros; + + public int getScanMode() { + return mScanMode; + } + + public int getCallbackType() { + return mCallbackType; + } + + public int getScanResultType() { + return mScanResultType; + } + + /** + * Returns report delay timestamp based on the device clock. + */ + public long getReportDelayMicros() { + return mReportDelayMicros; + } + + /** + * Creates a new {@link Builder} to build {@link Settings} object. + */ + public static Builder newBuilder() { + return new Builder(); + } + + private Settings(int scanMode, int callbackType, int scanResultType, + long reportDelayMicros) { + mScanMode = scanMode; + mCallbackType = callbackType; + mScanResultType = scanResultType; + mReportDelayMicros = reportDelayMicros; + } + + private Settings(Parcel in) { + mScanMode = in.readInt(); + mCallbackType = in.readInt(); + mScanResultType = in.readInt(); + mReportDelayMicros = in.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mScanMode); + dest.writeInt(mCallbackType); + dest.writeInt(mScanResultType); + dest.writeLong(mReportDelayMicros); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public Settings[] newArray(int size) { + return new Settings[size]; + } + + @Override + public Settings createFromParcel(Parcel in) { + return new Settings(in); + } + }; + + /** + * Builder for {@link BluetoothLeScanner.Settings}. + */ + public static class Builder { + private int mScanMode = SCAN_MODE_LOW_POWER; + private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; + private int mScanResultType = SCAN_RESULT_TYPE_FULL; + private long mReportDelayMicros = 0; + + // Hidden constructor. + private Builder() { + } + + /** + * Set scan mode for Bluetooth LE scan. + * + * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER}, + * {@link Settings#SCAN_MODE_BALANCED} or + * {@link Settings#SCAN_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the {@code scanMode} is invalid. + */ + public Builder scanMode(int scanMode) { + if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("invalid scan mode " + scanMode); + } + mScanMode = scanMode; + return this; + } + + /** + * Set callback type for Bluetooth LE scan. + * + * @param callbackType The callback type for the scan. Can be either one of + * {@link Settings#CALLBACK_TYPE_ON_UPDATE}, + * {@link Settings#CALLBACK_TYPE_ON_FOUND} or + * {@link Settings#CALLBACK_TYPE_ON_LOST}. + * @throws IllegalArgumentException If the {@code callbackType} is invalid. + */ + public Builder callbackType(int callbackType) { + if (callbackType < CALLBACK_TYPE_ON_UPDATE + || callbackType > CALLBACK_TYPE_ON_LOST) { + throw new IllegalArgumentException("invalid callback type - " + callbackType); + } + mCallbackType = callbackType; + return this; + } + + /** + * Set scan result type for Bluetooth LE scan. + * + * @param scanResultType Type for scan result, could be either + * {@link Settings#SCAN_RESULT_TYPE_FULL} or + * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}. + * @throws IllegalArgumentException If the {@code scanResultType} is invalid. + * @hide + */ + public Builder scanResultType(int scanResultType) { + if (scanResultType < SCAN_RESULT_TYPE_FULL + || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { + throw new IllegalArgumentException( + "invalid scanResultType - " + scanResultType); + } + mScanResultType = scanResultType; + return this; + } + + /** + * Set report delay timestamp for Bluetooth LE scan. + */ + public Builder reportDelayMicros(long reportDelayMicros) { + mReportDelayMicros = reportDelayMicros; + return this; + } + + /** + * Build {@link Settings}. + */ + public Settings build() { + return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros); + } + } + } + + /** + * ScanResult for Bluetooth LE scan. + */ + public static final class ScanResult implements Parcelable { + // Remote bluetooth device. + private BluetoothDevice mDevice; + + // Scan record, including advertising data and scan response data. + private byte[] mScanRecord; + + // Received signal strength. + private int mRssi; + + // Device timestamp when the result was last seen. + private long mTimestampMicros; + + // Constructor of scan result. + public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) { + mDevice = device; + mScanRecord = scanRecord; + mRssi = rssi; + mTimestampMicros = timestampMicros; + } + + private ScanResult(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mDevice != null) { + dest.writeInt(1); + mDevice.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mScanRecord != null) { + dest.writeInt(1); + dest.writeByteArray(mScanRecord); + } else { + dest.writeInt(0); + } + dest.writeInt(mRssi); + dest.writeLong(mTimestampMicros); + } + + private void readFromParcel(Parcel in) { + if (in.readInt() == 1) { + mDevice = BluetoothDevice.CREATOR.createFromParcel(in); + } + if (in.readInt() == 1) { + mScanRecord = in.createByteArray(); + } + mRssi = in.readInt(); + mTimestampMicros = in.readLong(); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the remote bluetooth device identified by the bluetooth device address. + */ + @Nullable + public BluetoothDevice getDevice() { + return mDevice; + } + + @Nullable /** + * Returns the scan record, which can be a combination of advertisement and scan response. + */ + public byte[] getScanRecord() { + return mScanRecord; + } + + /** + * Returns the received signal strength in dBm. The valid range is [-127, 127]. + */ + public int getRssi() { + return mRssi; + } + + /** + * Returns timestamp since boot when the scan record was observed. + */ + public long getTimestampMicros() { + return mTimestampMicros; + } + + @Override + public int hashCode() { + return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ScanResult other = (ScanResult) obj; + return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && + Objects.deepEquals(mScanRecord, other.mScanRecord) + && (mTimestampMicros == other.mTimestampMicros); + } + + @Override + public String toString() { + return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" + + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros=" + + mTimestampMicros + '}'; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public ScanResult createFromParcel(Parcel source) { + return new ScanResult(source); + } + + @Override + public ScanResult[] newArray(int size) { + return new ScanResult[size]; + } + }; + + } + + /** + * Callback of Bluetooth LE scans. The results of the scans will be delivered through the + * callbacks. + */ + public interface ScanCallback { + /** + * Callback when any BLE beacon is found. + * + * @param result A Bluetooth LE scan result. + */ + public void onDeviceUpdate(ScanResult result); + + /** + * Callback when the BLE beacon is found for the first time. + * + * @param result The Bluetooth LE scan result when the onFound event is triggered. + */ + public void onDeviceFound(ScanResult result); + + /** + * Callback when the BLE device was lost. Note a device has to be "found" before it's lost. + * + * @param device The Bluetooth device that is lost. + */ + public void onDeviceLost(BluetoothDevice device); + + /** + * Callback when batch results are delivered. + * + * @param results List of scan results that are previously scanned. + */ + public void onBatchScanResults(List results); + + /** + * Fails to start scan as BLE scan with the same settings is already started by the app. + */ + public static final int SCAN_ALREADY_STARTED = 1; + /** + * Fails to start scan as app cannot be registered. + */ + public static final int APPLICATION_REGISTRATION_FAILED = 2; + /** + * Fails to start scan due to gatt service failure. + */ + public static final int GATT_SERVICE_FAILURE = 3; + /** + * Fails to start scan due to controller failure. + */ + public static final int CONTROLLER_FAILURE = 4; + + /** + * Callback when scan failed. + */ + public void onScanFailed(int errorCode); + } + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map mLeScanClients; + + BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + mLeScanClients = new HashMap(); + } + + /** + * Bluetooth GATT interface callbacks + */ + private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; + + private final ScanCallback mScanCallback; + private final List mFilters; + private Settings mSettings; + private IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // > 0: registered and scan started + private int mLeHandle; + + public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, + List filters, Settings settings, ScanCallback scanCallback) { + mBluetoothGatt = bluetoothGatt; + mFilters = filters; + mSettings = settings; + mScanCallback = scanCallback; + mLeHandle = 0; + } + + public boolean scanStarted() { + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + } + return mLeHandle > 0; + } + + public void stopLeScan() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + try { + mBluetoothGatt.stopScan(mLeHandle, false); + mBluetoothGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop scan and unregister" + e); + } + mLeHandle = -1; + notifyAll(); + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + + synchronized (this) { + if (mLeHandle == -1) { + if (DBG) + Log.d(TAG, "onClientRegistered LE scan canceled"); + } + + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le scan: " + e); + mLeHandle = -1; + } + } else { + // registration failed + mLeHandle = -1; + } + notifyAll(); + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + /** + * Callback reporting an LE scan result. + * + * @hide + */ + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) + Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); + + // Check null in case the scan has been stopped + synchronized (this) { + if (mLeHandle <= 0) + return; + } + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + address); + long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos()); + ScanResult result = new ScanResult(device, advData, rssi, + scanMicros); + mScanCallback.onDeviceUpdate(result); + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + // no op + } + + @Override + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + /** + * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}. + * + * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices. + * @param settings Settings for ble scan. + * @param callback Callback when scan results are delivered. + * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. + */ + public void startScan(List filters, Settings settings, + final ScanCallback callback) { + if (settings == null || callback == null) { + throw new IllegalArgumentException("settings or callback is null"); + } + synchronized (mLeScanClients) { + if (mLeScanClients.get(settings) != null) { + postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED); + return; + } + BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, + settings, callback); + try { + UUID uuid = UUID.randomUUID(); + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.scanStarted()) { + mLeScanClients.put(settings, wrapper); + } else { + postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED); + return; + } + } catch (RemoteException e) { + Log.e(TAG, "GATT service exception when starting scan", e); + postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE); + } + } + } + + private void postCallbackError(final ScanCallback callback, final int errorCode) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onScanFailed(errorCode); + } + }); + } + + /** + * Stop Bluetooth LE scan. + * + * @param settings The same settings as used in {@link #startScan}, which is used to identify + * the BLE scan. + */ + public void stopScan(Settings settings) { + synchronized (mLeScanClients) { + BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings); + if (wrapper == null) { + return; + } + wrapper.stopLeScan(); + } + } + + /** + * Returns available storage size for batch scan results. It's recommended not to use batch scan + * if available storage size is small (less than 1k bytes, for instance). + * + * @hide TODO: unhide when batching is supported in stack. + */ + public int getAvailableBatchStorageSizeBytes() { + throw new UnsupportedOperationException("not impelemented"); + } + + /** + * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results + * batched on bluetooth controller. + * + * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one + * used to start scan. + * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will + * get batch scan callback if the batch scan buffer is flushed. + * @return Batch Scan results. + * @hide TODO: unhide when batching is supported in stack. + */ + public List getBatchScanResults(ScanCallback callback, boolean flush) { + throw new UnsupportedOperationException("not impelemented"); + } + +} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 3dd70945138..091b8063c1b 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -17,6 +17,10 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAdvertiseScanData; +import android.bluetooth.BluetoothLeAdvertiser; +import android.bluetooth.BluetoothLeScanFilter; +import android.bluetooth.BluetoothLeScanner; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -33,8 +37,13 @@ interface IBluetoothGatt { void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); void startScanWithUuidsScanParam(in int appIf, in boolean isServer, in ParcelUuid[] ids, int scanWindow, int scanInterval); + void startScanWithFilters(in int appIf, in boolean isServer, + in BluetoothLeScanner.Settings settings, + in List filters); void stopScan(in int appIf, in boolean isServer); - + void startMultiAdvertising(in int appIf, in BluetoothLeAdvertiseScanData.AdvertisementData data, + in BluetoothLeAdvertiser.Settings settings); + void stopMultiAdvertising(in int appIf); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index a78c29b0064..bf9e0a70180 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,5 +64,6 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); + oneway void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java new file mode 100644 index 00000000000..eb6c419bad9 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 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.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +import java.util.Arrays; + +/** + * Unit test cases for {@link BluetoothLeAdvertiseScanData}. + *

        + * To run this test, use adb shell am instrument -e class + * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeAdvertiseScanDataTest extends TestCase { + + @SmallTest + public void testParser() { + byte[] scanRecord = new byte[] { + 0x02, 0x01, 0x1a, // advertising flags + 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids + 0x04, 0x09, 0x50, 0x65, 0x64, // name + 0x02, 0x0A, (byte) 0xec, // tx power level + 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data + 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data + 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble + }; + BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord + .getParser().parseFromScanRecord(scanRecord); + assertEquals(0x1a, data.getAdvertiseFlags()); + ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + assertTrue(data.getServiceUuids().contains(uuid1)); + assertTrue(data.getServiceUuids().contains(uuid2)); + + assertEquals("Ped", data.getLocalName()); + assertEquals(-20, data.getTxPowerLevel()); + + assertEquals(224, data.getManufacturerId()); + assertArrayEquals(new byte[] { + (byte) 0xe0, 0x00, 0x02, 0x15 }, data.getManufacturerSpecificData()); + + assertEquals(uuid2, data.getServiceDataUuid()); + assertArrayEquals(new byte[] { + 0x0b, 0x11, 0x50, 0x64 }, data.getServiceData()); + } + + // Assert two byte arrays are equal. + private static void assertArrayEquals(byte[] expected, byte[] actual) { + if (!Arrays.equals(expected, actual)) { + fail("expected:<" + Arrays.toString(expected) + + "> but was:<" + Arrays.toString(actual) + ">"); + } + + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java new file mode 100644 index 00000000000..ec35d85deb1 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for Bluetooth LE scan filters. + *

        + * To run this test, use adb shell am instrument -e class + * 'android.bluetooth.BluetoothLeScanFilterTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeScanFilterTest extends TestCase { + + private static final String DEVICE_MAC = "01:02:03:04:05:AB"; + private ScanResult mScanResult; + private BluetoothLeScanFilter.Builder mFilterBuilder; + + @Override + protected void setUp() throws Exception { + byte[] scanRecord = new byte[] { + 0x02, 0x01, 0x1a, // advertising flags + 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids + 0x04, 0x09, 0x50, 0x65, 0x64, // name + 0x02, 0x0A, (byte) 0xec, // tx power level + 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data + 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data + 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble + }; + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); + mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L); + mFilterBuilder = BluetoothLeScanFilter.newBuilder(); + } + + @SmallTest + public void testNameFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build(); + assertTrue("name filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.name("Pem").build(); + assertFalse("name filter fails", filter.matches(mScanResult)); + + } + + @SmallTest + public void testDeviceFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build(); + assertTrue("device filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + assertFalse("device filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testServiceUuidFilter() { + BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build(); + assertTrue("uuid filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); + assertFalse("uuid filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder + .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")) + .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) + .build(); + assertTrue("uuid filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testServiceDataFilter() { + byte[] serviceData = new byte[] { + 0x0b, 0x11, 0x50, 0x64 }; + BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build(); + assertTrue("service data filter fails", filter.matches(mScanResult)); + + byte[] nonMatchData = new byte[] { + 0x0b, 0x01, 0x50, 0x64 }; + filter = mFilterBuilder.serviceData(nonMatchData).build(); + assertFalse("service data filter fails", filter.matches(mScanResult)); + + byte[] mask = new byte[] { + (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; + filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build(); + assertTrue("partial service data filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testManufacturerSpecificData() { + byte[] manufacturerData = new byte[] { + (byte) 0xE0, 0x00, 0x02, 0x15 }; + int manufacturerId = 224; + BluetoothLeScanFilter filter = + mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); + assertTrue("manufacturerData filter fails", filter.matches(mScanResult)); + + byte[] nonMatchData = new byte[] { + (byte) 0xF0, 0x00, 0x02, 0x15 }; + filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build(); + assertFalse("manufacturerData filter fails", filter.matches(mScanResult)); + + byte[] mask = new byte[] { + (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData) + .manufacturerDataMask(mask).build(); + assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult)); + } + + @SmallTest + public void testReadWriteParcel() { + BluetoothLeScanFilter filter = mFilterBuilder.build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.name("Ped").build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.serviceUuid( + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.serviceUuidMask( + ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); + testReadWriteParcelForFilter(filter); + + byte[] serviceData = new byte[] { + 0x0b, 0x11, 0x50, 0x64 }; + + filter = mFilterBuilder.serviceData(serviceData).build(); + testReadWriteParcelForFilter(filter); + + byte[] serviceDataMask = new byte[] { + (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; + filter = mFilterBuilder.serviceDataMask(serviceDataMask).build(); + testReadWriteParcelForFilter(filter); + + byte[] manufacturerData = new byte[] { + (byte) 0xE0, 0x00, 0x02, 0x15 }; + int manufacturerId = 224; + filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); + testReadWriteParcelForFilter(filter); + + byte[] manufacturerDataMask = new byte[] { + (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + }; + filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build(); + testReadWriteParcelForFilter(filter); + } + + private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) { + Parcel parcel = Parcel.obtain(); + filter.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + BluetoothLeScanFilter filterFromParcel = + BluetoothLeScanFilter.CREATOR.createFromParcel(parcel); + System.out.println(filterFromParcel); + assertEquals(filter, filterFromParcel); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java b/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java new file mode 100644 index 00000000000..8064ba8ac3b --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothLeScanner.ScanResult; +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for Bluetooth LE scans. + *

        + * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest' + * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothLeScannerTest extends TestCase { + + /** + * Test read and write parcel of ScanResult + */ + @SmallTest + public void testScanResultParceling() { + BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06"); + byte[] scanRecord = new byte[] { + 1, 2, 3 }; + int rssi = -10; + long timestampMicros = 10000L; + + ScanResult result = new ScanResult(device, scanRecord, rssi, timestampMicros); + Parcel parcel = Parcel.obtain(); + result.writeToParcel(parcel, 0); + // Need to reset parcel data position to the beginning. + parcel.setDataPosition(0); + ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel); + assertEquals(result, resultFromParcel); + } + +} -- GitLab From 0ef4f27eb9cefd2623eeef002c96a6df81a625b1 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 20 May 2014 02:43:02 -0700 Subject: [PATCH 0346/1408] Remove a hide tag link. Change-Id: I6781dcfcaf89c6b00e0641549f946b90e4543218 --- .../java/android/bluetooth/BluetoothLeAdvertiseScanData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java index 3d85810a00e..d12ac6ccefb 100644 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java @@ -298,8 +298,7 @@ public final class BluetoothLeAdvertiseScanData { * Set data type. * * @param dataType Data type, could only be - * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} or - * {@link BluetoothLeAdvertiseScanData#SCAN_RESPONSE_DATA} + * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} * @throws IllegalArgumentException If the {@code dataType} is invalid. */ public Builder dataType(int dataType) { -- GitLab From 42da7130310fc2af0d86df6c981f45dfdd7d3d60 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 20 May 2014 17:59:36 -0700 Subject: [PATCH 0347/1408] Add scan response as an advertising parameter(1/2). Change-Id: I75c500dd7b5f2609d3092f50ba284a5ec4a41987 --- .../BluetoothLeAdvertiseScanData.java | 3 --- .../bluetooth/BluetoothLeAdvertiser.java | 23 +++++++++++++++---- .../android/bluetooth/IBluetoothGatt.aidl | 4 +++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java index d12ac6ccefb..2fa5e4965ed 100644 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java @@ -53,9 +53,6 @@ public final class BluetoothLeAdvertiseScanData { * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising * packet. *

        - * TODO: unhide when stack supports setting scan response data. - * - * @hide */ public static final int SCAN_RESPONSE_DATA = 1; /** diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java index 2a8aa23b28a..30c90c4deab 100644 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java @@ -328,6 +328,7 @@ public class BluetoothLeAdvertiser { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; private final AdvertisementData mAdvertisement; + private final AdvertisementData mScanResponse; private final Settings mSettings; private final IBluetoothGatt mBluetoothGatt; @@ -338,10 +339,11 @@ public class BluetoothLeAdvertiser { private boolean isAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, - AdvertisementData advertiseData, Settings settings, + AdvertisementData advertiseData, AdvertisementData scanResponse, Settings settings, IBluetoothGatt bluetoothGatt) { mAdvertiseCallback = advertiseCallback; mAdvertisement = advertiseData; + mScanResponse = scanResponse; mSettings = settings; mBluetoothGatt = bluetoothGatt; mLeHandle = 0; @@ -384,7 +386,8 @@ public class BluetoothLeAdvertiser { if (status == BluetoothGatt.GATT_SUCCESS) { mLeHandle = clientIf; try { - mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, mSettings); + mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, + mScanResponse, mSettings); } catch (RemoteException e) { Log.e(TAG, "fail to start le advertise: " + e); mLeHandle = -1; @@ -540,6 +543,19 @@ public class BluetoothLeAdvertiser { */ public void startAdvertising(Settings settings, AdvertisementData advertiseData, final AdvertiseCallback callback) { + startAdvertising(settings, advertiseData, null, callback); + } + + /** + * Start Bluetooth LE Advertising. + * @param settings {@link Settings} for Bluetooth LE advertising. + * @param advertiseData {@link AdvertisementData} to be advertised in advertisement packet. + * @param scanResponse {@link AdvertisementData} for scan response. + * @param callback {@link AdvertiseCallback} for advertising status. + */ + public void startAdvertising(Settings settings, + AdvertisementData advertiseData, AdvertisementData scanResponse, + final AdvertiseCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } @@ -548,8 +564,7 @@ public class BluetoothLeAdvertiser { return; } AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - settings, - mBluetoothGatt); + scanResponse, settings, mBluetoothGatt); UUID uuid = UUID.randomUUID(); try { mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 091b8063c1b..ceed52bff89 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -41,7 +41,9 @@ interface IBluetoothGatt { in BluetoothLeScanner.Settings settings, in List filters); void stopScan(in int appIf, in boolean isServer); - void startMultiAdvertising(in int appIf, in BluetoothLeAdvertiseScanData.AdvertisementData data, + void startMultiAdvertising(in int appIf, + in BluetoothLeAdvertiseScanData.AdvertisementData advertiseData, + in BluetoothLeAdvertiseScanData.AdvertisementData scanResponse, in BluetoothLeAdvertiser.Settings settings); void stopMultiAdvertising(in int appIf); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); -- GitLab From a5e51dba01239a6131cf31a4d4788ca4a5081295 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 21 May 2014 10:08:50 -0700 Subject: [PATCH 0348/1408] Add Audio Manager support for Bluetooth A2DP Sink profile Change-Id: Iff6035e85faf52647cc41a59f98ba2924300eb8d --- framework/java/android/bluetooth/BluetoothProfile.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 15740908b0a..d8980602855 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -103,6 +103,12 @@ public interface BluetoothProfile { */ public static final int MAP = 9; + /** + * A2DP Sink Profile + * @hide + */ + public static final int A2DP_SINK = 10; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile -- GitLab From 7df2e527176028029fdf07e85a8fb3377111b4b8 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 22 May 2014 12:10:25 -0700 Subject: [PATCH 0349/1408] Address API review comments. 1. Moved le stuff to it's subpackage. Remove BluetoothLe for all classes except *Scanner, *ScanSetting, *Advertiser and *AdvertiseSettings. 2. Make all callbacks abstract classes instead of interfaces. 3. Moved AdvertisementData and ScanRecord out and removed AdvertiseBaseData 4. Removed newBuild and use new Builder for all builders. 5. Using setxxx in builders. 6. Misc other changes. Fixes b/15140940 Change-Id: I32ae3d24a9491baf96048040b5ac78f6f731e468 NO_SQ: multi-project submit --- .../android/bluetooth/BluetoothAdapter.java | 2 + .../BluetoothLeAdvertiseScanData.java | 645 --------------- .../bluetooth/BluetoothLeAdvertiser.java | 615 -------------- .../android/bluetooth/BluetoothLeScanner.java | 759 ------------------ .../android/bluetooth/IBluetoothGatt.aidl | 17 +- .../bluetooth/le/AdvertiseCallback.java | 68 ++ .../AdvertiseSettings.aidl} | 4 +- .../bluetooth/le/AdvertiseSettings.java | 218 +++++ .../AdvertisementData.aidl} | 4 +- .../bluetooth/le/AdvertisementData.java | 344 ++++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 368 +++++++++ .../bluetooth/le/BluetoothLeScanner.java | 371 +++++++++ .../android/bluetooth/le/ScanCallback.java | 79 ++ .../ScanFilter.aidl} | 4 +- .../ScanFilter.java} | 269 ++++--- .../java/android/bluetooth/le/ScanRecord.java | 278 +++++++ .../ScanResult.aidl} | 5 +- .../java/android/bluetooth/le/ScanResult.java | 162 ++++ .../android/bluetooth/le/ScanSettings.aidl | 19 + .../android/bluetooth/le/ScanSettings.java | 221 +++++ .../ScanFilterTest.java} | 95 +-- .../ScanRecordTest.java} | 12 +- .../ScanResultTest.java} | 14 +- 23 files changed, 2348 insertions(+), 2225 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeAdvertiser.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeScanner.java create mode 100644 framework/java/android/bluetooth/le/AdvertiseCallback.java rename framework/java/android/bluetooth/{BluetoothLeScanFilter.aidl => le/AdvertiseSettings.aidl} (91%) create mode 100644 framework/java/android/bluetooth/le/AdvertiseSettings.java rename framework/java/android/bluetooth/{BluetoothLeAdvertiser.aidl => le/AdvertisementData.aidl} (90%) create mode 100644 framework/java/android/bluetooth/le/AdvertisementData.java create mode 100644 framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java create mode 100644 framework/java/android/bluetooth/le/BluetoothLeScanner.java create mode 100644 framework/java/android/bluetooth/le/ScanCallback.java rename framework/java/android/bluetooth/{BluetoothLeAdvertiseScanData.aidl => le/ScanFilter.aidl} (87%) rename framework/java/android/bluetooth/{BluetoothLeScanFilter.java => le/ScanFilter.java} (71%) create mode 100644 framework/java/android/bluetooth/le/ScanRecord.java rename framework/java/android/bluetooth/{BluetoothLeScanner.aidl => le/ScanResult.aidl} (85%) create mode 100644 framework/java/android/bluetooth/le/ScanResult.java create mode 100644 framework/java/android/bluetooth/le/ScanSettings.aidl create mode 100644 framework/java/android/bluetooth/le/ScanSettings.java rename framework/tests/src/android/bluetooth/{BluetoothLeScanFilterTest.java => le/ScanFilterTest.java} (59%) rename framework/tests/src/android/bluetooth/{BluetoothLeAdvertiseScanDataTest.java => le/ScanRecordTest.java} (87%) rename framework/tests/src/android/bluetooth/{BluetoothLeScannerTest.java => le/ScanResultTest.java} (79%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9e1c995bbb5..42c2aebead5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.bluetooth.le.BluetoothLeScanner; import android.content.Context; import android.os.Handler; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java b/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java deleted file mode 100644 index 2fa5e4965ed..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.java +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.Nullable; -import android.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Represents Bluetooth LE advertise and scan response data. This could be either the advertisement - * data to be advertised, or the scan record obtained from BLE scans. - *

        - * The exact bluetooth advertising and scan response data fields and types are defined in Bluetooth - * 4.0 specification, Volume 3, Part C, Section 11 and 18, as well as Supplement to the Bluetooth - * Core Specification Version 4. Currently the following fields are allowed to be set: - *

      • Service UUIDs which identify the bluetooth gatt services running on the device. - *
      • Tx power level which is the transmission power level. - *
      • Service data which is the data associated with a service. - *
      • Manufacturer specific data which is the data associated with a particular manufacturer. - * - * @see BluetoothLeAdvertiser - */ -public final class BluetoothLeAdvertiseScanData { - private static final String TAG = "BluetoothLeAdvertiseScanData"; - - /** - * Bluetooth LE Advertising Data type, the data will be placed in AdvData field of advertising - * packet. - */ - public static final int ADVERTISING_DATA = 0; - /** - * Bluetooth LE scan response data, the data will be placed in ScanRspData field of advertising - * packet. - *

        - */ - public static final int SCAN_RESPONSE_DATA = 1; - /** - * Scan record parsed from Bluetooth LE scans. The content can contain a concatenation of - * advertising data and scan response data. - */ - public static final int PARSED_SCAN_RECORD = 2; - - /** - * Base data type which contains the common fields for {@link AdvertisementData} and - * {@link ScanRecord}. - */ - public abstract static class AdvertiseBaseData { - - private final int mDataType; - - @Nullable - private final List mServiceUuids; - - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; - - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; - - private AdvertiseBaseData(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData) { - mDataType = dataType; - mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - } - - /** - * Returns the type of data, indicating whether the data is advertising data, scan response - * data or scan record. - */ - public int getDataType() { - return mDataType; - } - - /** - * Returns a list of service uuids within the advertisement that are used to identify the - * bluetooth gatt services. - */ - public List getServiceUuids() { - return mServiceUuids; - } - - /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. - */ - public int getManufacturerId() { - return mManufacturerId; - } - - /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. The first 2 bytes of the data contain the company id. - */ - public byte[] getManufacturerSpecificData() { - return mManufacturerSpecificData; - } - - /** - * Returns a 16 bit uuid of the service that the service data is associated with. - */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** - * Returns service data. The first two bytes should be a 16 bit service uuid associated with - * the service data. - */ - public byte[] getServiceData() { - return mServiceData; - } - - @Override - public String toString() { - return "AdvertiseBaseData [mDataType=" + mDataType + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + "]"; - } - } - - /** - * Advertisement data packet for Bluetooth LE advertising. This represents the data to be - * broadcasted in Bluetooth LE advertising. - *

        - * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to - * be advertised. - * - * @see BluetoothLeAdvertiser - */ - public static final class AdvertisementData extends AdvertiseBaseData implements Parcelable { - - private boolean mIncludeTxPowerLevel; - - /** - * Whether the transmission power level will be included in the advertisement packet. - */ - public boolean getIncludeTxPowerLevel() { - return mIncludeTxPowerLevel; - } - - /** - * Returns a {@link Builder} to build {@link AdvertisementData}. - */ - public static Builder newBuilder() { - return new Builder(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(getDataType()); - List uuids = getServiceUuids(); - if (uuids == null) { - dest.writeInt(0); - } else { - dest.writeInt(uuids.size()); - dest.writeList(uuids); - } - - dest.writeInt(getManufacturerId()); - byte[] manufacturerData = getManufacturerSpecificData(); - if (manufacturerData == null) { - dest.writeInt(0); - } else { - dest.writeInt(manufacturerData.length); - dest.writeByteArray(manufacturerData); - } - - ParcelUuid serviceDataUuid = getServiceDataUuid(); - if (serviceDataUuid == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeParcelable(serviceDataUuid, flags); - byte[] serviceData = getServiceData(); - if (serviceData == null) { - dest.writeInt(0); - } else { - dest.writeInt(serviceData.length); - dest.writeByteArray(serviceData); - } - } - dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); - } - - private AdvertisementData(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, boolean mIncludeTxPowerLevel) { - super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, - manufacturerSpecificData); - this.mIncludeTxPowerLevel = mIncludeTxPowerLevel; - } - - public static final Parcelable.Creator CREATOR = - new Creator() { - @Override - public AdvertisementData[] newArray(int size) { - return new AdvertisementData[size]; - } - - @Override - public AdvertisementData createFromParcel(Parcel in) { - Builder builder = newBuilder(); - int dataType = in.readInt(); - builder.dataType(dataType); - if (in.readInt() > 0) { - List uuids = new ArrayList(); - in.readList(uuids, ParcelUuid.class.getClassLoader()); - builder.serviceUuids(uuids); - } - int manufacturerId = in.readInt(); - int manufacturerDataLength = in.readInt(); - if (manufacturerDataLength > 0) { - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.manufacturerData(manufacturerId, manufacturerData); - } - if (in.readInt() == 1) { - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.serviceData(serviceDataUuid, serviceData); - } - } - builder.includeTxPowerLevel(in.readByte() == 1); - return builder.build(); - } - }; - - /** - * Builder for {@link BluetoothLeAdvertiseScanData.AdvertisementData}. Use - * {@link AdvertisementData#newBuilder()} to get an instance of the Builder. - */ - public static final class Builder { - private static final int MAX_ADVERTISING_DATA_BYTES = 31; - // Each fields need one byte for field length and another byte for field type. - private static final int OVERHEAD_BYTES_PER_FIELD = 2; - // Flags field will be set by system. - private static final int FLAGS_FIELD_BYTES = 3; - - private int mDataType; - @Nullable - private List mServiceUuids; - private boolean mIncludeTxPowerLevel; - private int mManufacturerId; - @Nullable - private byte[] mManufacturerSpecificData; - @Nullable - private ParcelUuid mServiceDataUuid; - @Nullable - private byte[] mServiceData; - - /** - * Set data type. - * - * @param dataType Data type, could only be - * {@link BluetoothLeAdvertiseScanData#ADVERTISING_DATA} - * @throws IllegalArgumentException If the {@code dataType} is invalid. - */ - public Builder dataType(int dataType) { - if (mDataType != ADVERTISING_DATA && mDataType != SCAN_RESPONSE_DATA) { - throw new IllegalArgumentException("invalid data type - " + dataType); - } - mDataType = dataType; - return this; - } - - /** - * Set the service uuids. Note the corresponding bluetooth Gatt services need to be - * already added on the device before start BLE advertising. - * - * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or - * 128-bit uuids. - * @throws IllegalArgumentException If the {@code serviceUuids} are null. - */ - public Builder serviceUuids(List serviceUuids) { - if (serviceUuids == null) { - throw new IllegalArgumentException("serivceUuids are null"); - } - mServiceUuids = serviceUuids; - return this; - } - - /** - * Add service data to advertisement. - * - * @param serviceDataUuid A 16 bit uuid of the service data - * @param serviceData Service data - the first two bytes of the service data are the - * service data uuid. - * @throws IllegalArgumentException If the {@code serviceDataUuid} or - * {@code serviceData} is empty. - */ - public Builder serviceData(ParcelUuid serviceDataUuid, byte[] serviceData) { - if (serviceDataUuid == null || serviceData == null) { - throw new IllegalArgumentException( - "serviceDataUuid or serviceDataUuid is null"); - } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - return this; - } - - /** - * Set manufacturer id and data. See assigned - * manufacturer identifies for the existing company identifiers. - * - * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. - * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of - * the manufacturer specific data are the manufacturer id. - * @throws IllegalArgumentException If the {@code manufacturerId} is negative or - * {@code manufacturerSpecificData} is null. - */ - public Builder manufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { - if (manufacturerId < 0) { - throw new IllegalArgumentException( - "invalid manufacturerId - " + manufacturerId); - } - if (manufacturerSpecificData == null) { - throw new IllegalArgumentException("manufacturerSpecificData is null"); - } - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - return this; - } - - /** - * Whether the transmission power level should be included in the advertising packet. - */ - public Builder includeTxPowerLevel(boolean includeTxPowerLevel) { - mIncludeTxPowerLevel = includeTxPowerLevel; - return this; - } - - /** - * Build the {@link BluetoothLeAdvertiseScanData}. - * - * @throws IllegalArgumentException If the data size is larger than 31 bytes. - */ - public AdvertisementData build() { - if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { - throw new IllegalArgumentException( - "advertisement data size is larger than 31 bytes"); - } - return new AdvertisementData(mDataType, - mServiceUuids, - mServiceDataUuid, - mServiceData, mManufacturerId, mManufacturerSpecificData, - mIncludeTxPowerLevel); - } - - // Compute the size of the advertisement data. - private int totalBytes() { - int size = FLAGS_FIELD_BYTES; // flags field is always set. - if (mServiceUuids != null) { - int num16BitUuids = 0; - int num32BitUuids = 0; - int num128BitUuids = 0; - for (ParcelUuid uuid : mServiceUuids) { - if (BluetoothUuid.is16BitUuid(uuid)) { - ++num16BitUuids; - } else if (BluetoothUuid.is32BitUuid(uuid)) { - ++num32BitUuids; - } else { - ++num128BitUuids; - } - } - // 16 bit service uuids are grouped into one field when doing advertising. - if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; - } - // 32 bit service uuids are grouped into one field when doing advertising. - if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; - } - // 128 bit service uuids are grouped into one field when doing advertising. - if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; - } - } - if (mServiceData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; - } - if (mManufacturerSpecificData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; - } - if (mIncludeTxPowerLevel) { - size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. - } - return size; - } - } - - } - - /** - * Represents a scan record from Bluetooth LE scan. - */ - public static final class ScanRecord extends AdvertiseBaseData { - // Flags of the advertising data. - private final int mAdvertiseFlags; - - // Transmission power level(in dB). - private final int mTxPowerLevel; - - // Local name of the Bluetooth LE device. - private final String mLocalName; - - /** - * Returns the advertising flags indicating the discoverable mode and capability of the - * device. Returns -1 if the flag field is not set. - */ - public int getAdvertiseFlags() { - return mAdvertiseFlags; - } - - /** - * Returns the transmission power level of the packet in dBm. Returns - * {@link Integer#MIN_VALUE} if the field is not set. This value can be used to calculate - * the path loss of a received packet using the following equation: - *

        - * pathloss = txPowerLevel - rssi - */ - public int getTxPowerLevel() { - return mTxPowerLevel; - } - - /** - * Returns the local name of the BLE device. The is a UTF-8 encoded string. - */ - @Nullable - public String getLocalName() { - return mLocalName; - } - - ScanRecord(int dataType, - List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, - String localName) { - super(dataType, serviceUuids, serviceDataUuid, serviceData, manufacturerId, - manufacturerSpecificData); - mLocalName = localName; - mAdvertiseFlags = advertiseFlags; - mTxPowerLevel = txPowerLevel; - } - - /** - * Get a {@link Parser} to parse the scan record byte array into {@link ScanRecord}. - */ - public static Parser getParser() { - return new Parser(); - } - - /** - * A parser class used to parse a Bluetooth LE scan record to - * {@link BluetoothLeAdvertiseScanData}. Note not all field types would be parsed. - */ - public static final class Parser { - private static final String PARSER_TAG = "BluetoothLeAdvertiseDataParser"; - - // The following data type values are assigned by Bluetooth SIG. - // For more details refer to Bluetooth 4.0 specification, Volume 3, Part C, Section 18. - private static final int DATA_TYPE_FLAGS = 0x01; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; - private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; - private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; - private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA = 0x16; - private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; - - // Helper method to extract bytes from byte array. - private static byte[] extractBytes(byte[] scanRecord, int start, int length) { - byte[] bytes = new byte[length]; - System.arraycopy(scanRecord, start, bytes, 0, length); - return bytes; - } - - /** - * Parse scan record to {@link BluetoothLeAdvertiseScanData.ScanRecord}. - *

        - * The format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 - * and 18. - *

        - * All numerical multi-byte entities and values shall use little-endian - * byte order. - * - * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - */ - public ScanRecord parseFromScanRecord(byte[] scanRecord) { - if (scanRecord == null) { - return null; - } - - int currentPos = 0; - int advertiseFlag = -1; - List serviceUuids = new ArrayList(); - String localName = null; - int txPowerLevel = Integer.MIN_VALUE; - ParcelUuid serviceDataUuid = null; - byte[] serviceData = null; - int manufacturerId = -1; - byte[] manufacturerSpecificData = null; - - try { - while (currentPos < scanRecord.length) { - // length is unsigned int. - int length = scanRecord[currentPos++] & 0xFF; - if (length == 0) { - break; - } - // Note the length includes the length of the field type itself. - int dataLength = length - 1; - // fieldType is unsigned int. - int fieldType = scanRecord[currentPos++] & 0xFF; - switch (fieldType) { - case DATA_TYPE_FLAGS: - advertiseFlag = scanRecord[currentPos] & 0xFF; - break; - case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, - dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); - break; - case DATA_TYPE_LOCAL_NAME_SHORT: - case DATA_TYPE_LOCAL_NAME_COMPLETE: - localName = new String( - extractBytes(scanRecord, currentPos, dataLength)); - break; - case DATA_TYPE_TX_POWER_LEVEL: - txPowerLevel = scanRecord[currentPos]; - break; - case DATA_TYPE_SERVICE_DATA: - serviceData = extractBytes(scanRecord, currentPos, dataLength); - // The first two bytes of the service data are service data uuid. - int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; - byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, - serviceUuidLength); - serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); - break; - case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: - manufacturerSpecificData = extractBytes(scanRecord, currentPos, - dataLength); - // The first two bytes of the manufacturer specific data are - // manufacturer ids in little endian. - manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + - (manufacturerSpecificData[0] & 0xFF); - break; - default: - // Just ignore, we don't handle such data type. - break; - } - currentPos += dataLength; - } - - if (serviceUuids.isEmpty()) { - serviceUuids = null; - } - return new ScanRecord(PARSED_SCAN_RECORD, - serviceUuids, serviceDataUuid, serviceData, - manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, - localName); - } catch (IndexOutOfBoundsException e) { - Log.e(PARSER_TAG, - "unable to parse scan record: " + Arrays.toString(scanRecord)); - return null; - } - } - - // Parse service uuids. - private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, - int uuidLength, List serviceUuids) { - while (dataLength > 0) { - byte[] uuidBytes = extractBytes(scanRecord, currentPos, - uuidLength); - serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); - dataLength -= uuidLength; - currentPos += uuidLength; - } - return currentPos; - } - } - } - -} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/BluetoothLeAdvertiser.java deleted file mode 100644 index 30c90c4deab..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiser.java +++ /dev/null @@ -1,615 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothLeAdvertiseScanData.AdvertisementData; -import android.os.Handler; -import android.os.Looper; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop - * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by - * {@link BluetoothLeAdvertiseScanData.AdvertisementData}. - *

        - * To get an instance of {@link BluetoothLeAdvertiser}, call the - * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. - *

        - * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @see BluetoothLeAdvertiseScanData.AdvertisementData - */ -public class BluetoothLeAdvertiser { - - private static final String TAG = "BluetoothLeAdvertiser"; - - /** - * The {@link Settings} provide a way to adjust advertising preferences for each individual - * advertisement. Use {@link Settings.Builder} to create a {@link Settings} instance. - */ - public static final class Settings implements Parcelable { - /** - * Perform Bluetooth LE advertising in low power mode. This is the default and preferred - * advertising mode as it consumes the least power. - */ - public static final int ADVERTISE_MODE_LOW_POWER = 0; - /** - * Perform Bluetooth LE advertising in balanced power mode. This is balanced between - * advertising frequency and power consumption. - */ - public static final int ADVERTISE_MODE_BALANCED = 1; - /** - * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest - * power consumption and should not be used for background continuous advertising. - */ - public static final int ADVERTISE_MODE_LOW_LATENCY = 2; - - /** - * Advertise using the lowest transmission(tx) power level. An app can use low transmission - * power to restrict the visibility range of its advertising packet. - */ - public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; - /** - * Advertise using low tx power level. - */ - public static final int ADVERTISE_TX_POWER_LOW = 1; - /** - * Advertise using medium tx power level. - */ - public static final int ADVERTISE_TX_POWER_MEDIUM = 2; - /** - * Advertise using high tx power level. This is corresponding to largest visibility range of - * the advertising packet. - */ - public static final int ADVERTISE_TX_POWER_HIGH = 3; - - /** - * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.0 - * vol6, part B, section 4.4.2 - Advertising state. - */ - public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; - /** - * Scannable undirected advertise type, as defined in same spec mentioned above. This event - * type allows a scanner to send a scan request asking additional information about the - * advertiser. - */ - public static final int ADVERTISE_TYPE_SCANNABLE = 1; - /** - * Connectable undirected advertising type, as defined in same spec mentioned above. This - * event type allows a scanner to send scan request asking additional information about the - * advertiser. It also allows an initiator to send a connect request for connection. - */ - public static final int ADVERTISE_TYPE_CONNECTABLE = 2; - - private final int mAdvertiseMode; - private final int mAdvertiseTxPowerLevel; - private final int mAdvertiseEventType; - - private Settings(int advertiseMode, int advertiseTxPowerLevel, - int advertiseEventType) { - mAdvertiseMode = advertiseMode; - mAdvertiseTxPowerLevel = advertiseTxPowerLevel; - mAdvertiseEventType = advertiseEventType; - } - - private Settings(Parcel in) { - mAdvertiseMode = in.readInt(); - mAdvertiseTxPowerLevel = in.readInt(); - mAdvertiseEventType = in.readInt(); - } - - /** - * Creates a {@link Builder} to construct a {@link Settings} object. - */ - public static Builder newBuilder() { - return new Builder(); - } - - /** - * Returns the advertise mode. - */ - public int getMode() { - return mAdvertiseMode; - } - - /** - * Returns the tx power level for advertising. - */ - public int getTxPowerLevel() { - return mAdvertiseTxPowerLevel; - } - - /** - * Returns the advertise event type. - */ - public int getType() { - return mAdvertiseEventType; - } - - @Override - public String toString() { - return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" - + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mAdvertiseMode); - dest.writeInt(mAdvertiseTxPowerLevel); - dest.writeInt(mAdvertiseEventType); - } - - public static final Parcelable.Creator CREATOR = - new Creator() { - @Override - public Settings[] newArray(int size) { - return new Settings[size]; - } - - @Override - public Settings createFromParcel(Parcel in) { - return new Settings(in); - } - }; - - /** - * Builder class for {@link BluetoothLeAdvertiser.Settings}. Caller should use - * {@link Settings#newBuilder()} to get an instance of the builder. - */ - public static final class Builder { - private int mMode = ADVERTISE_MODE_LOW_POWER; - private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; - private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; - - // Private constructor, use Settings.newBuilder() get an instance of BUILDER. - private Builder() { - } - - /** - * Set advertise mode to control the advertising power and latency. - * - * @param advertiseMode Bluetooth LE Advertising mode, can only be one of - * {@link Settings#ADVERTISE_MODE_LOW_POWER}, - * {@link Settings#ADVERTISE_MODE_BALANCED}, or - * {@link Settings#ADVERTISE_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the advertiseMode is invalid. - */ - public Builder advertiseMode(int advertiseMode) { - if (advertiseMode < ADVERTISE_MODE_LOW_POWER - || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("unknown mode " + advertiseMode); - } - mMode = advertiseMode; - return this; - } - - /** - * Set advertise tx power level to control the transmission power level for the - * advertising. - * - * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one - * of {@link Settings#ADVERTISE_TX_POWER_ULTRA_LOW}, - * {@link Settings#ADVERTISE_TX_POWER_LOW}, - * {@link Settings#ADVERTISE_TX_POWER_MEDIUM} or - * {@link Settings#ADVERTISE_TX_POWER_HIGH}. - * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. - */ - public Builder txPowerLevel(int txPowerLevel) { - if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW - || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { - throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); - } - mTxPowerLevel = txPowerLevel; - return this; - } - - /** - * Set advertise type to control the event type of advertising. - * - * @param type Bluetooth LE Advertising type, can be either - * {@link Settings#ADVERTISE_TYPE_NON_CONNECTABLE}, - * {@link Settings#ADVERTISE_TYPE_SCANNABLE} or - * {@link Settings#ADVERTISE_TYPE_CONNECTABLE}. - * @throws IllegalArgumentException If the {@code type} is invalid. - */ - public Builder type(int type) { - if (type < ADVERTISE_TYPE_NON_CONNECTABLE - || type > ADVERTISE_TYPE_CONNECTABLE) { - throw new IllegalArgumentException("unknown advertise type " + type); - } - mType = type; - return this; - } - - /** - * Build the {@link Settings} object. - */ - public Settings build() { - return new Settings(mMode, mTxPowerLevel, mType); - } - } - } - - /** - * Callback of Bluetooth LE advertising, which is used to deliver operation status for start and - * stop advertising. - */ - public interface AdvertiseCallback { - - /** - * The operation is success. - * - * @hide - */ - public static final int SUCCESS = 0; - /** - * Fails to start advertising as the advertisement data contains services that are not added - * to the local bluetooth Gatt server. - */ - public static final int ADVERTISING_SERVICE_UNKNOWN = 1; - /** - * Fails to start advertising as system runs out of quota for advertisers. - */ - public static final int TOO_MANY_ADVERTISERS = 2; - - /** - * Fails to start advertising as the advertising is already started. - */ - public static final int ADVERTISING_ALREADY_STARTED = 3; - /** - * Fails to stop advertising as the advertising is not started. - */ - public static final int ADVERISING_NOT_STARTED = 4; - - /** - * Operation fails due to bluetooth controller failure. - */ - public static final int CONTROLLER_FAILURE = 5; - - /** - * Callback when advertising operation succeeds. - * - * @param settingsInEffect The actual settings used for advertising, which may be different - * from what the app asks. - */ - public void onSuccess(Settings settingsInEffect); - - /** - * Callback when advertising operation fails. - * - * @param errorCode Error code for failures. - */ - public void onFailure(int errorCode); - } - - private final IBluetoothGatt mBluetoothGatt; - private final Handler mHandler; - private final Map - mLeAdvertisers = new HashMap(); - - // Package private constructor, use BluetoothAdapter.getLeAdvertiser() instead. - BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; - mHandler = new Handler(Looper.getMainLooper()); - } - - /** - * Bluetooth GATT interface callbacks for advertising. - */ - private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { - private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; - private final AdvertiseCallback mAdvertiseCallback; - private final AdvertisementData mAdvertisement; - private final AdvertisementData mScanResponse; - private final Settings mSettings; - private final IBluetoothGatt mBluetoothGatt; - - // mLeHandle 0: not registered - // -1: scan stopped - // >0: registered and scan started - private int mLeHandle; - private boolean isAdvertising = false; - - public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, - AdvertisementData advertiseData, AdvertisementData scanResponse, Settings settings, - IBluetoothGatt bluetoothGatt) { - mAdvertiseCallback = advertiseCallback; - mAdvertisement = advertiseData; - mScanResponse = scanResponse; - mSettings = settings; - mBluetoothGatt = bluetoothGatt; - mLeHandle = 0; - } - - public boolean advertiseStarted() { - boolean started = false; - synchronized (this) { - if (mLeHandle == -1) { - return false; - } - try { - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - started = (mLeHandle > 0 && isAdvertising); - } - return started; - } - - public boolean advertiseStopped() { - synchronized (this) { - try { - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - return !isAdvertising; - } - } - - /** - * Application interface registered - app is ready to go - */ - @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); - synchronized (this) { - if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; - try { - mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, - mScanResponse, mSettings); - } catch (RemoteException e) { - Log.e(TAG, "fail to start le advertise: " + e); - mLeHandle = -1; - notifyAll(); - } catch (Exception e) { - Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); - } - } else { - // registration failed - mLeHandle = -1; - notifyAll(); - } - } - } - - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - - @Override - public void onScanResult(String address, int rssi, byte[] advData) { - // no op - } - - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - synchronized (this) { - if (status == 0) { - isAdvertising = !isAdvertising; - if (!isAdvertising) { - try { - mBluetoothGatt.unregisterClient(mLeHandle); - mLeHandle = -1; - } catch (RemoteException e) { - Log.e(TAG, "remote exception when unregistering", e); - } - } - mAdvertiseCallback.onSuccess(null); - } else { - mAdvertiseCallback.onFailure(status); - } - notifyAll(); - } - - } - - /** - * Callback reporting LE ATT MTU. - * - * @hide - */ - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - } - - /** - * Start Bluetooth LE Advertising. - * - * @param settings {@link Settings} for Bluetooth LE advertising. - * @param advertiseData {@link AdvertisementData} to be advertised. - * @param callback {@link AdvertiseCallback} for advertising status. - */ - public void startAdvertising(Settings settings, - AdvertisementData advertiseData, final AdvertiseCallback callback) { - startAdvertising(settings, advertiseData, null, callback); - } - - /** - * Start Bluetooth LE Advertising. - * @param settings {@link Settings} for Bluetooth LE advertising. - * @param advertiseData {@link AdvertisementData} to be advertised in advertisement packet. - * @param scanResponse {@link AdvertisementData} for scan response. - * @param callback {@link AdvertiseCallback} for advertising status. - */ - public void startAdvertising(Settings settings, - AdvertisementData advertiseData, AdvertisementData scanResponse, - final AdvertiseCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - if (mLeAdvertisers.containsKey(settings)) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISING_ALREADY_STARTED); - return; - } - AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - scanResponse, settings, mBluetoothGatt); - UUID uuid = UUID.randomUUID(); - try { - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.advertiseStarted()) { - mLeAdvertisers.put(settings, wrapper); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); - } - } - - /** - * Stop Bluetooth LE advertising. Returns immediately, the operation status will be delivered - * through the {@code callback}. - *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * - * @param settings {@link Settings} used to start Bluetooth LE advertising. - * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. - */ - public void stopAdvertising(final Settings settings, final AdvertiseCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(settings); - if (wrapper == null) { - postCallbackFailure(callback, AdvertiseCallback.ADVERISING_NOT_STARTED); - return; - } - try { - mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); - if (wrapper.advertiseStopped()) { - mLeAdvertisers.remove(settings); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); - } - } - - private void postCallbackFailure(final AdvertiseCallback callback, final int error) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onFailure(error); - } - }); - } -} diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.java b/framework/java/android/bluetooth/BluetoothLeScanner.java deleted file mode 100644 index ed3188b1c16..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeScanner.java +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Copyright (C) 2014 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.annotation.Nullable; -import android.os.Handler; -import android.os.Looper; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.SystemClock; -import android.util.Log; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of BLE devices using {@link BluetoothLeScanFilter}. It - * can also request different types of callbacks for delivering the result. - *

        - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of - * {@link BluetoothLeScanner}. - *

        - * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @see BluetoothLeScanFilter - */ -public class BluetoothLeScanner { - - private static final String TAG = "BluetoothLeScanner"; - private static final boolean DBG = true; - - /** - * Settings for Bluetooth LE scan. - */ - public static final class Settings implements Parcelable { - /** - * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes - * the least power. - */ - public static final int SCAN_MODE_LOW_POWER = 0; - /** - * Perform Bluetooth LE scan in balanced power mode. - */ - public static final int SCAN_MODE_BALANCED = 1; - /** - * Scan using highest duty cycle. It's recommended only using this mode when the application - * is running in foreground. - */ - public static final int SCAN_MODE_LOW_LATENCY = 2; - - /** - * Callback each time when a bluetooth advertisement is found. - */ - public static final int CALLBACK_TYPE_ON_UPDATE = 0; - /** - * Callback when a bluetooth advertisement is found for the first time. - */ - public static final int CALLBACK_TYPE_ON_FOUND = 1; - /** - * Callback when a bluetooth advertisement is found for the first time, then lost. - */ - public static final int CALLBACK_TYPE_ON_LOST = 2; - - /** - * Full scan result which contains device mac address, rssi, advertising and scan response - * and scan timestamp. - */ - public static final int SCAN_RESULT_TYPE_FULL = 0; - /** - * Truncated scan result which contains device mac address, rssi and scan timestamp. Note - * it's possible for an app to get more scan results that it asks if there are multiple apps - * using this type. TODO: decide whether we could unhide this setting. - * - * @hide - */ - public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; - - // Bluetooth LE scan mode. - private int mScanMode; - - // Bluetooth LE scan callback type - private int mCallbackType; - - // Bluetooth LE scan result type - private int mScanResultType; - - // Time of delay for reporting the scan result - private long mReportDelayMicros; - - public int getScanMode() { - return mScanMode; - } - - public int getCallbackType() { - return mCallbackType; - } - - public int getScanResultType() { - return mScanResultType; - } - - /** - * Returns report delay timestamp based on the device clock. - */ - public long getReportDelayMicros() { - return mReportDelayMicros; - } - - /** - * Creates a new {@link Builder} to build {@link Settings} object. - */ - public static Builder newBuilder() { - return new Builder(); - } - - private Settings(int scanMode, int callbackType, int scanResultType, - long reportDelayMicros) { - mScanMode = scanMode; - mCallbackType = callbackType; - mScanResultType = scanResultType; - mReportDelayMicros = reportDelayMicros; - } - - private Settings(Parcel in) { - mScanMode = in.readInt(); - mCallbackType = in.readInt(); - mScanResultType = in.readInt(); - mReportDelayMicros = in.readLong(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mScanMode); - dest.writeInt(mCallbackType); - dest.writeInt(mScanResultType); - dest.writeLong(mReportDelayMicros); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public Settings[] newArray(int size) { - return new Settings[size]; - } - - @Override - public Settings createFromParcel(Parcel in) { - return new Settings(in); - } - }; - - /** - * Builder for {@link BluetoothLeScanner.Settings}. - */ - public static class Builder { - private int mScanMode = SCAN_MODE_LOW_POWER; - private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; - private int mScanResultType = SCAN_RESULT_TYPE_FULL; - private long mReportDelayMicros = 0; - - // Hidden constructor. - private Builder() { - } - - /** - * Set scan mode for Bluetooth LE scan. - * - * @param scanMode The scan mode can be one of {@link Settings#SCAN_MODE_LOW_POWER}, - * {@link Settings#SCAN_MODE_BALANCED} or - * {@link Settings#SCAN_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the {@code scanMode} is invalid. - */ - public Builder scanMode(int scanMode) { - if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("invalid scan mode " + scanMode); - } - mScanMode = scanMode; - return this; - } - - /** - * Set callback type for Bluetooth LE scan. - * - * @param callbackType The callback type for the scan. Can be either one of - * {@link Settings#CALLBACK_TYPE_ON_UPDATE}, - * {@link Settings#CALLBACK_TYPE_ON_FOUND} or - * {@link Settings#CALLBACK_TYPE_ON_LOST}. - * @throws IllegalArgumentException If the {@code callbackType} is invalid. - */ - public Builder callbackType(int callbackType) { - if (callbackType < CALLBACK_TYPE_ON_UPDATE - || callbackType > CALLBACK_TYPE_ON_LOST) { - throw new IllegalArgumentException("invalid callback type - " + callbackType); - } - mCallbackType = callbackType; - return this; - } - - /** - * Set scan result type for Bluetooth LE scan. - * - * @param scanResultType Type for scan result, could be either - * {@link Settings#SCAN_RESULT_TYPE_FULL} or - * {@link Settings#SCAN_RESULT_TYPE_TRUNCATED}. - * @throws IllegalArgumentException If the {@code scanResultType} is invalid. - * @hide - */ - public Builder scanResultType(int scanResultType) { - if (scanResultType < SCAN_RESULT_TYPE_FULL - || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { - throw new IllegalArgumentException( - "invalid scanResultType - " + scanResultType); - } - mScanResultType = scanResultType; - return this; - } - - /** - * Set report delay timestamp for Bluetooth LE scan. - */ - public Builder reportDelayMicros(long reportDelayMicros) { - mReportDelayMicros = reportDelayMicros; - return this; - } - - /** - * Build {@link Settings}. - */ - public Settings build() { - return new Settings(mScanMode, mCallbackType, mScanResultType, mReportDelayMicros); - } - } - } - - /** - * ScanResult for Bluetooth LE scan. - */ - public static final class ScanResult implements Parcelable { - // Remote bluetooth device. - private BluetoothDevice mDevice; - - // Scan record, including advertising data and scan response data. - private byte[] mScanRecord; - - // Received signal strength. - private int mRssi; - - // Device timestamp when the result was last seen. - private long mTimestampMicros; - - // Constructor of scan result. - public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, long timestampMicros) { - mDevice = device; - mScanRecord = scanRecord; - mRssi = rssi; - mTimestampMicros = timestampMicros; - } - - private ScanResult(Parcel in) { - readFromParcel(in); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - if (mDevice != null) { - dest.writeInt(1); - mDevice.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mScanRecord != null) { - dest.writeInt(1); - dest.writeByteArray(mScanRecord); - } else { - dest.writeInt(0); - } - dest.writeInt(mRssi); - dest.writeLong(mTimestampMicros); - } - - private void readFromParcel(Parcel in) { - if (in.readInt() == 1) { - mDevice = BluetoothDevice.CREATOR.createFromParcel(in); - } - if (in.readInt() == 1) { - mScanRecord = in.createByteArray(); - } - mRssi = in.readInt(); - mTimestampMicros = in.readLong(); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Returns the remote bluetooth device identified by the bluetooth device address. - */ - @Nullable - public BluetoothDevice getDevice() { - return mDevice; - } - - @Nullable /** - * Returns the scan record, which can be a combination of advertisement and scan response. - */ - public byte[] getScanRecord() { - return mScanRecord; - } - - /** - * Returns the received signal strength in dBm. The valid range is [-127, 127]. - */ - public int getRssi() { - return mRssi; - } - - /** - * Returns timestamp since boot when the scan record was observed. - */ - public long getTimestampMicros() { - return mTimestampMicros; - } - - @Override - public int hashCode() { - return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampMicros); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ScanResult other = (ScanResult) obj; - return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.deepEquals(mScanRecord, other.mScanRecord) - && (mTimestampMicros == other.mTimestampMicros); - } - - @Override - public String toString() { - return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampMicros=" - + mTimestampMicros + '}'; - } - - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public ScanResult createFromParcel(Parcel source) { - return new ScanResult(source); - } - - @Override - public ScanResult[] newArray(int size) { - return new ScanResult[size]; - } - }; - - } - - /** - * Callback of Bluetooth LE scans. The results of the scans will be delivered through the - * callbacks. - */ - public interface ScanCallback { - /** - * Callback when any BLE beacon is found. - * - * @param result A Bluetooth LE scan result. - */ - public void onDeviceUpdate(ScanResult result); - - /** - * Callback when the BLE beacon is found for the first time. - * - * @param result The Bluetooth LE scan result when the onFound event is triggered. - */ - public void onDeviceFound(ScanResult result); - - /** - * Callback when the BLE device was lost. Note a device has to be "found" before it's lost. - * - * @param device The Bluetooth device that is lost. - */ - public void onDeviceLost(BluetoothDevice device); - - /** - * Callback when batch results are delivered. - * - * @param results List of scan results that are previously scanned. - */ - public void onBatchScanResults(List results); - - /** - * Fails to start scan as BLE scan with the same settings is already started by the app. - */ - public static final int SCAN_ALREADY_STARTED = 1; - /** - * Fails to start scan as app cannot be registered. - */ - public static final int APPLICATION_REGISTRATION_FAILED = 2; - /** - * Fails to start scan due to gatt service failure. - */ - public static final int GATT_SERVICE_FAILURE = 3; - /** - * Fails to start scan due to controller failure. - */ - public static final int CONTROLLER_FAILURE = 4; - - /** - * Callback when scan failed. - */ - public void onScanFailed(int errorCode); - } - - private final IBluetoothGatt mBluetoothGatt; - private final Handler mHandler; - private final Map mLeScanClients; - - BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; - mHandler = new Handler(Looper.getMainLooper()); - mLeScanClients = new HashMap(); - } - - /** - * Bluetooth GATT interface callbacks - */ - private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { - private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; - - private final ScanCallback mScanCallback; - private final List mFilters; - private Settings mSettings; - private IBluetoothGatt mBluetoothGatt; - - // mLeHandle 0: not registered - // -1: scan stopped - // > 0: registered and scan started - private int mLeHandle; - - public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, - List filters, Settings settings, ScanCallback scanCallback) { - mBluetoothGatt = bluetoothGatt; - mFilters = filters; - mSettings = settings; - mScanCallback = scanCallback; - mLeHandle = 0; - } - - public boolean scanStarted() { - synchronized (this) { - if (mLeHandle == -1) { - return false; - } - try { - wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - } - return mLeHandle > 0; - } - - public void stopLeScan() { - synchronized (this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); - return; - } - try { - mBluetoothGatt.stopScan(mLeHandle, false); - mBluetoothGatt.unregisterClient(mLeHandle); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop scan and unregister" + e); - } - mLeHandle = -1; - notifyAll(); - } - } - - /** - * Application interface registered - app is ready to go - */ - @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + - " clientIf=" + clientIf); - - synchronized (this) { - if (mLeHandle == -1) { - if (DBG) - Log.d(TAG, "onClientRegistered LE scan canceled"); - } - - if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; - try { - mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); - } catch (RemoteException e) { - Log.e(TAG, "fail to start le scan: " + e); - mLeHandle = -1; - } - } else { - // registration failed - mLeHandle = -1; - } - notifyAll(); - } - } - - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - - /** - * Callback reporting an LE scan result. - * - * @hide - */ - @Override - public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) - Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); - - // Check null in case the scan has been stopped - synchronized (this) { - if (mLeHandle <= 0) - return; - } - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - address); - long scanMicros = TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos()); - ScanResult result = new ScanResult(device, advData, rssi, - scanMicros); - mScanCallback.onDeviceUpdate(result); - } - - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - // no op - } - - @Override - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - } - - /** - * Scan Bluetooth LE scan. The scan results will be delivered through {@code callback}. - * - * @param filters {@link BluetoothLeScanFilter}s for finding exact BLE devices. - * @param settings Settings for ble scan. - * @param callback Callback when scan results are delivered. - * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. - */ - public void startScan(List filters, Settings settings, - final ScanCallback callback) { - if (settings == null || callback == null) { - throw new IllegalArgumentException("settings or callback is null"); - } - synchronized (mLeScanClients) { - if (mLeScanClients.get(settings) != null) { - postCallbackError(callback, ScanCallback.SCAN_ALREADY_STARTED); - return; - } - BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, - settings, callback); - try { - UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.scanStarted()) { - mLeScanClients.put(settings, wrapper); - } else { - postCallbackError(callback, ScanCallback.APPLICATION_REGISTRATION_FAILED); - return; - } - } catch (RemoteException e) { - Log.e(TAG, "GATT service exception when starting scan", e); - postCallbackError(callback, ScanCallback.GATT_SERVICE_FAILURE); - } - } - } - - private void postCallbackError(final ScanCallback callback, final int errorCode) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onScanFailed(errorCode); - } - }); - } - - /** - * Stop Bluetooth LE scan. - * - * @param settings The same settings as used in {@link #startScan}, which is used to identify - * the BLE scan. - */ - public void stopScan(Settings settings) { - synchronized (mLeScanClients) { - BleScanCallbackWrapper wrapper = mLeScanClients.remove(settings); - if (wrapper == null) { - return; - } - wrapper.stopLeScan(); - } - } - - /** - * Returns available storage size for batch scan results. It's recommended not to use batch scan - * if available storage size is small (less than 1k bytes, for instance). - * - * @hide TODO: unhide when batching is supported in stack. - */ - public int getAvailableBatchStorageSizeBytes() { - throw new UnsupportedOperationException("not impelemented"); - } - - /** - * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results - * batched on bluetooth controller. - * - * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one - * used to start scan. - * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will - * get batch scan callback if the batch scan buffer is flushed. - * @return Batch Scan results. - * @hide TODO: unhide when batching is supported in stack. - */ - public List getBatchScanResults(ScanCallback callback, boolean flush) { - throw new UnsupportedOperationException("not impelemented"); - } - -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index ceed52bff89..00a07502d3e 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -17,10 +17,10 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothLeAdvertiseScanData; -import android.bluetooth.BluetoothLeAdvertiser; -import android.bluetooth.BluetoothLeScanFilter; -import android.bluetooth.BluetoothLeScanner; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.AdvertisementData; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanSettings; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -38,13 +38,12 @@ interface IBluetoothGatt { void startScanWithUuidsScanParam(in int appIf, in boolean isServer, in ParcelUuid[] ids, int scanWindow, int scanInterval); void startScanWithFilters(in int appIf, in boolean isServer, - in BluetoothLeScanner.Settings settings, - in List filters); + in ScanSettings settings, in List filters); void stopScan(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, - in BluetoothLeAdvertiseScanData.AdvertisementData advertiseData, - in BluetoothLeAdvertiseScanData.AdvertisementData scanResponse, - in BluetoothLeAdvertiser.Settings settings); + in AdvertisementData advertiseData, + in AdvertisementData scanResponse, + in AdvertiseSettings settings); void stopMultiAdvertising(in int appIf); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java new file mode 100644 index 00000000000..f1334c2765b --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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.le; + +/** + * Callback of Bluetooth LE advertising, which is used to deliver advertising operation status. + */ +public abstract class AdvertiseCallback { + + /** + * The operation is success. + * + * @hide + */ + public static final int SUCCESS = 0; + /** + * Fails to start advertising as the advertisement data contains services that are not added to + * the local bluetooth GATT server. + */ + public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1; + /** + * Fails to start advertising as system runs out of quota for advertisers. + */ + public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; + + /** + * Fails to start advertising as the advertising is already started. + */ + public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; + /** + * Fails to stop advertising as the advertising is not started. + */ + public static final int ADVERTISE_FAILED_NOT_STARTED = 4; + + /** + * Operation fails due to bluetooth controller failure. + */ + public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5; + + /** + * Callback when advertising operation succeeds. + * + * @param settingsInEffect The actual settings used for advertising, which may be different from + * what the app asks. + */ + public abstract void onSuccess(AdvertiseSettings settingsInEffect); + + /** + * Callback when advertising operation fails. + * + * @param errorCode Error code for failures. + */ + public abstract void onFailure(int errorCode); +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl b/framework/java/android/bluetooth/le/AdvertiseSettings.aidl similarity index 91% rename from framework/java/android/bluetooth/BluetoothLeScanFilter.aidl rename to framework/java/android/bluetooth/le/AdvertiseSettings.aidl index 86ee06d3942..9f47d74ca53 100644 --- a/framework/java/android/bluetooth/BluetoothLeScanFilter.aidl +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -parcelable BluetoothLeScanFilter; +parcelable AdvertiseSettings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java new file mode 100644 index 00000000000..87d03467ed2 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2014 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each + * individual advertisement. Use {@link AdvertiseSettings.Builder} to create an instance. + */ +public final class AdvertiseSettings implements Parcelable { + /** + * Perform Bluetooth LE advertising in low power mode. This is the default and preferred + * advertising mode as it consumes the least power. + */ + public static final int ADVERTISE_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising + * frequency and power consumption. + */ + public static final int ADVERTISE_MODE_BALANCED = 1; + /** + * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power + * consumption and should not be used for background continuous advertising. + */ + public static final int ADVERTISE_MODE_LOW_LATENCY = 2; + + /** + * Advertise using the lowest transmission(tx) power level. An app can use low transmission + * power to restrict the visibility range of its advertising packet. + */ + public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; + /** + * Advertise using low tx power level. + */ + public static final int ADVERTISE_TX_POWER_LOW = 1; + /** + * Advertise using medium tx power level. + */ + public static final int ADVERTISE_TX_POWER_MEDIUM = 2; + /** + * Advertise using high tx power level. This is corresponding to largest visibility range of the + * advertising packet. + */ + public static final int ADVERTISE_TX_POWER_HIGH = 3; + + /** + * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.1 + * vol6, part B, section 4.4.2 - Advertising state. + */ + public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; + /** + * Scannable undirected advertise type, as defined in same spec mentioned above. This event type + * allows a scanner to send a scan request asking additional information about the advertiser. + */ + public static final int ADVERTISE_TYPE_SCANNABLE = 1; + /** + * Connectable undirected advertising type, as defined in same spec mentioned above. This event + * type allows a scanner to send scan request asking additional information about the + * advertiser. It also allows an initiator to send a connect request for connection. + */ + public static final int ADVERTISE_TYPE_CONNECTABLE = 2; + + private final int mAdvertiseMode; + private final int mAdvertiseTxPowerLevel; + private final int mAdvertiseEventType; + + private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel, + int advertiseEventType) { + mAdvertiseMode = advertiseMode; + mAdvertiseTxPowerLevel = advertiseTxPowerLevel; + mAdvertiseEventType = advertiseEventType; + } + + private AdvertiseSettings(Parcel in) { + mAdvertiseMode = in.readInt(); + mAdvertiseTxPowerLevel = in.readInt(); + mAdvertiseEventType = in.readInt(); + } + + /** + * Returns the advertise mode. + */ + public int getMode() { + return mAdvertiseMode; + } + + /** + * Returns the tx power level for advertising. + */ + public int getTxPowerLevel() { + return mAdvertiseTxPowerLevel; + } + + /** + * Returns the advertise event type. + */ + public int getType() { + return mAdvertiseEventType; + } + + @Override + public String toString() { + return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAdvertiseMode); + dest.writeInt(mAdvertiseTxPowerLevel); + dest.writeInt(mAdvertiseEventType); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertiseSettings[] newArray(int size) { + return new AdvertiseSettings[size]; + } + + @Override + public AdvertiseSettings createFromParcel(Parcel in) { + return new AdvertiseSettings(in); + } + }; + + /** + * Builder class for {@link AdvertiseSettings}. + */ + public static final class Builder { + private int mMode = ADVERTISE_MODE_LOW_POWER; + private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; + private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; + + /** + * Set advertise mode to control the advertising power and latency. + * + * @param advertiseMode Bluetooth LE Advertising mode, can only be one of + * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_POWER}, + * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, or + * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the advertiseMode is invalid. + */ + public Builder setAdvertiseMode(int advertiseMode) { + if (advertiseMode < ADVERTISE_MODE_LOW_POWER + || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("unknown mode " + advertiseMode); + } + mMode = advertiseMode; + return this; + } + + /** + * Set advertise tx power level to control the transmission power level for the advertising. + * + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_LOW}, + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} or + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}. + * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. + */ + public Builder setTxPowerLevel(int txPowerLevel) { + if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW + || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { + throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); + } + mTxPowerLevel = txPowerLevel; + return this; + } + + /** + * Set advertise type to control the event type of advertising. + * + * @param type Bluetooth LE Advertising type, can be either + * {@link AdvertiseSettings#ADVERTISE_TYPE_NON_CONNECTABLE}, + * {@link AdvertiseSettings#ADVERTISE_TYPE_SCANNABLE} or + * {@link AdvertiseSettings#ADVERTISE_TYPE_CONNECTABLE}. + * @throws IllegalArgumentException If the {@code type} is invalid. + */ + public Builder setType(int type) { + if (type < ADVERTISE_TYPE_NON_CONNECTABLE + || type > ADVERTISE_TYPE_CONNECTABLE) { + throw new IllegalArgumentException("unknown advertise type " + type); + } + mType = type; + return this; + } + + /** + * Build the {@link AdvertiseSettings} object. + */ + public AdvertiseSettings build() { + return new AdvertiseSettings(mMode, mTxPowerLevel, mType); + } + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl b/framework/java/android/bluetooth/le/AdvertisementData.aidl similarity index 90% rename from framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl rename to framework/java/android/bluetooth/le/AdvertisementData.aidl index 31086105610..3da1321f6c5 100644 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiser.aidl +++ b/framework/java/android/bluetooth/le/AdvertisementData.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -parcelable BluetoothLeAdvertiser.Settings; \ No newline at end of file +parcelable AdvertisementData; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisementData.java b/framework/java/android/bluetooth/le/AdvertisementData.java new file mode 100644 index 00000000000..c5872047be1 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisementData.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.Nullable; +import android.bluetooth.BluetoothUuid; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Advertisement data packet for Bluetooth LE advertising. This represents the data to be + * broadcasted in Bluetooth LE advertising as well as the scan response for active scan. + *

        + * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to be + * advertised. + * + * @see BluetoothLeAdvertiser + * @see ScanRecord + */ +public final class AdvertisementData implements Parcelable { + + @Nullable + private final List mServiceUuids; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; + + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; + + private boolean mIncludeTxPowerLevel; + + private AdvertisementData(List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, boolean includeTxPowerLevel) { + mServiceUuids = serviceUuids; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + mIncludeTxPowerLevel = includeTxPowerLevel; + } + + /** + * Returns a list of service uuids within the advertisement that are used to identify the + * bluetooth GATT services. + */ + public List getServiceUuids() { + return mServiceUuids; + } + + /** + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + /** + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. The first 2 bytes of the data contain the company id. + */ + public byte[] getManufacturerSpecificData() { + return mManufacturerSpecificData; + } + + /** + * Returns a 16 bit uuid of the service that the service data is associated with. + */ + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + + /** + * Returns service data. The first two bytes should be a 16 bit service uuid associated with the + * service data. + */ + public byte[] getServiceData() { + return mServiceData; + } + + /** + * Whether the transmission power level will be included in the advertisement packet. + */ + public boolean getIncludeTxPowerLevel() { + return mIncludeTxPowerLevel; + } + + @Override + public String toString() { + return "AdvertisementData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mServiceUuids == null) { + dest.writeInt(0); + } else { + dest.writeInt(mServiceUuids.size()); + dest.writeList(mServiceUuids); + } + + dest.writeInt(mManufacturerId); + if (mManufacturerSpecificData == null) { + dest.writeInt(0); + } else { + dest.writeInt(mManufacturerSpecificData.length); + dest.writeByteArray(mManufacturerSpecificData); + } + + if (mServiceDataUuid == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeParcelable(mServiceDataUuid, flags); + if (mServiceData == null) { + dest.writeInt(0); + } else { + dest.writeInt(mServiceData.length); + dest.writeByteArray(mServiceData); + } + } + dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertisementData[] newArray(int size) { + return new AdvertisementData[size]; + } + + @Override + public AdvertisementData createFromParcel(Parcel in) { + Builder builder = new Builder(); + if (in.readInt() > 0) { + List uuids = new ArrayList(); + in.readList(uuids, ParcelUuid.class.getClassLoader()); + builder.setServiceUuids(uuids); + } + int manufacturerId = in.readInt(); + int manufacturerDataLength = in.readInt(); + if (manufacturerDataLength > 0) { + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.setManufacturerData(manufacturerId, manufacturerData); + } + if (in.readInt() == 1) { + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.setServiceData(serviceDataUuid, serviceData); + } + } + builder.setIncludeTxPowerLevel(in.readByte() == 1); + return builder.build(); + } + }; + + /** + * Builder for {@link AdvertisementData}. + */ + public static final class Builder { + private static final int MAX_ADVERTISING_DATA_BYTES = 31; + // Each fields need one byte for field length and another byte for field type. + private static final int OVERHEAD_BYTES_PER_FIELD = 2; + // Flags field will be set by system. + private static final int FLAGS_FIELD_BYTES = 3; + + @Nullable + private List mServiceUuids; + private boolean mIncludeTxPowerLevel; + private int mManufacturerId; + @Nullable + private byte[] mManufacturerSpecificData; + @Nullable + private ParcelUuid mServiceDataUuid; + @Nullable + private byte[] mServiceData; + + /** + * Set the service uuids. Note the corresponding bluetooth Gatt services need to be already + * added on the device before start BLE advertising. + * + * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or 128-bit + * uuids. + * @throws IllegalArgumentException If the {@code serviceUuids} are null. + */ + public Builder setServiceUuids(List serviceUuids) { + if (serviceUuids == null) { + throw new IllegalArgumentException("serivceUuids are null"); + } + mServiceUuids = serviceUuids; + return this; + } + + /** + * Add service data to advertisement. + * + * @param serviceDataUuid A 16 bit uuid of the service data + * @param serviceData Service data - the first two bytes of the service data are the service + * data uuid. + * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is + * empty. + */ + public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + if (serviceDataUuid == null || serviceData == null) { + throw new IllegalArgumentException( + "serviceDataUuid or serviceDataUuid is null"); + } + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + return this; + } + + /** + * Set manufacturer id and data. See assigned + * manufacturer identifies for the existing company identifiers. + * + * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. + * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of the + * manufacturer specific data are the manufacturer id. + * @throws IllegalArgumentException If the {@code manufacturerId} is negative or + * {@code manufacturerSpecificData} is null. + */ + public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + if (manufacturerId < 0) { + throw new IllegalArgumentException( + "invalid manufacturerId - " + manufacturerId); + } + if (manufacturerSpecificData == null) { + throw new IllegalArgumentException("manufacturerSpecificData is null"); + } + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + return this; + } + + /** + * Whether the transmission power level should be included in the advertising packet. + */ + public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) { + mIncludeTxPowerLevel = includeTxPowerLevel; + return this; + } + + /** + * Build the {@link AdvertisementData}. + * + * @throws IllegalArgumentException If the data size is larger than 31 bytes. + */ + public AdvertisementData build() { + if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException( + "advertisement data size is larger than 31 bytes"); + } + return new AdvertisementData(mServiceUuids, + mServiceDataUuid, + mServiceData, mManufacturerId, mManufacturerSpecificData, + mIncludeTxPowerLevel); + } + + // Compute the size of the advertisement data. + private int totalBytes() { + int size = FLAGS_FIELD_BYTES; // flags field is always set. + if (mServiceUuids != null) { + int num16BitUuids = 0; + int num32BitUuids = 0; + int num128BitUuids = 0; + for (ParcelUuid uuid : mServiceUuids) { + if (BluetoothUuid.is16BitUuid(uuid)) { + ++num16BitUuids; + } else if (BluetoothUuid.is32BitUuid(uuid)) { + ++num32BitUuids; + } else { + ++num128BitUuids; + } + } + // 16 bit service uuids are grouped into one field when doing advertising. + if (num16BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + } + // 32 bit service uuids are grouped into one field when doing advertising. + if (num32BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + } + // 128 bit service uuids are grouped into one field when doing advertising. + if (num128BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + } + } + if (mServiceData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; + } + if (mManufacturerSpecificData != null) { + size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; + } + if (mIncludeTxPowerLevel) { + size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. + } + return size; + } + } +} diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java new file mode 100644 index 00000000000..ed43407d721 --- /dev/null +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2014 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.le; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothGattCallback; +import android.os.Handler; +import android.os.Looper; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop + * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by + * {@link AdvertisementData}. + *

        + * To get an instance of {@link BluetoothLeAdvertiser}, call the + * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. + *

        + * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see AdvertisementData + */ +public final class BluetoothLeAdvertiser { + + private static final String TAG = "BluetoothLeAdvertiser"; + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map + mLeAdvertisers = new HashMap(); + + /** + * Use BluetoothAdapter.getLeAdvertiser() instead. + * + * @param bluetoothGatt + * @hide + */ + public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + } + + /** + * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the + * operation succeeds. Returns immediately, the operation status are delivered through + * {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param settings Settings for Bluetooth LE advertising. + * @param advertiseData Advertisement data to be broadcasted. + * @param callback Callback for advertising status. + */ + public void startAdvertising(AdvertiseSettings settings, + AdvertisementData advertiseData, final AdvertiseCallback callback) { + startAdvertising(settings, advertiseData, null, callback); + } + + /** + * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the + * operation succeeds. The {@code scanResponse} would be returned when the scanning device sends + * active scan request. Method returns immediately, the operation status are delivered through + * {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + * @param settings Settings for Bluetooth LE advertising. + * @param advertiseData Advertisement data to be advertised in advertisement packet. + * @param scanResponse Scan response associated with the advertisement data. + * @param callback Callback for advertising status. + */ + public void startAdvertising(AdvertiseSettings settings, + AdvertisementData advertiseData, AdvertisementData scanResponse, + final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + if (mLeAdvertisers.containsKey(callback)) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); + return; + } + AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, + scanResponse, settings, mBluetoothGatt); + UUID uuid = UUID.randomUUID(); + try { + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.advertiseStarted()) { + mLeAdvertisers.put(callback, wrapper); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + /** + * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in + * {@link BluetoothLeAdvertiser#startAdvertising}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. + */ + public void stopAdvertising(final AdvertiseCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); + if (wrapper == null) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_NOT_STARTED); + return; + } + try { + mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); + if (wrapper.advertiseStopped()) { + mLeAdvertisers.remove(callback); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to stop advertising", e); + } + } + + /** + * Bluetooth GATT interface callbacks for advertising. + */ + private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; + private final AdvertiseCallback mAdvertiseCallback; + private final AdvertisementData mAdvertisement; + private final AdvertisementData mScanResponse; + private final AdvertiseSettings mSettings; + private final IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // >0: registered and scan started + private int mLeHandle; + private boolean isAdvertising = false; + + public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, + AdvertisementData advertiseData, AdvertisementData scanResponse, + AdvertiseSettings settings, + IBluetoothGatt bluetoothGatt) { + mAdvertiseCallback = advertiseCallback; + mAdvertisement = advertiseData; + mScanResponse = scanResponse; + mSettings = settings; + mBluetoothGatt = bluetoothGatt; + mLeHandle = 0; + } + + public boolean advertiseStarted() { + boolean started = false; + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: ", e); + } + started = (mLeHandle > 0 && isAdvertising); + } + return started; + } + + public boolean advertiseStopped() { + synchronized (this) { + try { + wait(LE_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + return !isAdvertising; + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + synchronized (this) { + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, + mScanResponse, mSettings); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le advertise: " + e); + mLeHandle = -1; + notifyAll(); + } catch (Exception e) { + Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); + } + } else { + // registration failed + mLeHandle = -1; + notifyAll(); + } + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + // no op + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + synchronized (this) { + if (status == 0) { + isAdvertising = !isAdvertising; + if (!isAdvertising) { + try { + mBluetoothGatt.unregisterClient(mLeHandle); + mLeHandle = -1; + } catch (RemoteException e) { + Log.e(TAG, "remote exception when unregistering", e); + } + } + mAdvertiseCallback.onSuccess(null); + } else { + mAdvertiseCallback.onFailure(status); + } + notifyAll(); + } + + } + + /** + * Callback reporting LE ATT MTU. + * + * @hide + */ + @Override + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + private void postCallbackFailure(final AdvertiseCallback callback, final int error) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onFailure(error); + } + }); + } +} diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java new file mode 100644 index 00000000000..4c6346cc1d8 --- /dev/null +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2014 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.le; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothGattCallback; +import android.os.Handler; +import android.os.Looper; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * This class provides methods to perform scan related operations for Bluetooth LE devices. An + * application can scan for a particular type of BLE devices using {@link ScanFilter}. It can also + * request different types of callbacks for delivering the result. + *

        + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of + * {@link BluetoothLeScanner}. + *

        + * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see ScanFilter + */ +public final class BluetoothLeScanner { + + private static final String TAG = "BluetoothLeScanner"; + private static final boolean DBG = true; + + private final IBluetoothGatt mBluetoothGatt; + private final Handler mHandler; + private final Map mLeScanClients; + + /** + * @hide + */ + public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { + mBluetoothGatt = bluetoothGatt; + mHandler = new Handler(Looper.getMainLooper()); + mLeScanClients = new HashMap(); + } + + /** + * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param filters {@link ScanFilter}s for finding exact BLE devices. + * @param settings Settings for ble scan. + * @param callback Callback when scan results are delivered. + * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. + */ + public void startScan(List filters, ScanSettings settings, + final ScanCallback callback) { + if (settings == null || callback == null) { + throw new IllegalArgumentException("settings or callback is null"); + } + synchronized (mLeScanClients) { + if (mLeScanClients.containsKey(callback)) { + postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); + return; + } + BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, + settings, callback); + try { + UUID uuid = UUID.randomUUID(); + mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.scanStarted()) { + mLeScanClients.put(callback, wrapper); + } else { + postCallbackError(callback, + ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); + return; + } + } catch (RemoteException e) { + Log.e(TAG, "GATT service exception when starting scan", e); + postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE); + } + } + } + + /** + * Stops an ongoing Bluetooth LE scan. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param callback + */ + public void stopScan(ScanCallback callback) { + synchronized (mLeScanClients) { + BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); + if (wrapper == null) { + return; + } + wrapper.stopLeScan(); + } + } + + /** + * Returns available storage size for batch scan results. It's recommended not to use batch scan + * if available storage size is small (less than 1k bytes, for instance). + * + * @hide TODO: unhide when batching is supported in stack. + */ + public int getAvailableBatchStorageSizeBytes() { + throw new UnsupportedOperationException("not impelemented"); + } + + /** + * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results + * batched on bluetooth controller. + * + * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one + * used to start scan. + * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will + * get batch scan callback if the batch scan buffer is flushed. + * @return Batch Scan results. + * @hide TODO: unhide when batching is supported in stack. + */ + public List getBatchScanResults(ScanCallback callback, boolean flush) { + throw new UnsupportedOperationException("not impelemented"); + } + + /** + * Bluetooth GATT interface callbacks + */ + private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; + + private final ScanCallback mScanCallback; + private final List mFilters; + private ScanSettings mSettings; + private IBluetoothGatt mBluetoothGatt; + + // mLeHandle 0: not registered + // -1: scan stopped + // > 0: registered and scan started + private int mLeHandle; + + public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, + List filters, ScanSettings settings, + ScanCallback scanCallback) { + mBluetoothGatt = bluetoothGatt; + mFilters = filters; + mSettings = settings; + mScanCallback = scanCallback; + mLeHandle = 0; + } + + public boolean scanStarted() { + synchronized (this) { + if (mLeHandle == -1) { + return false; + } + try { + wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + } + return mLeHandle > 0; + } + + public void stopLeScan() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + try { + mBluetoothGatt.stopScan(mLeHandle, false); + mBluetoothGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop scan and unregister" + e); + } + mLeHandle = -1; + notifyAll(); + } + } + + /** + * Application interface registered - app is ready to go + */ + @Override + public void onClientRegistered(int status, int clientIf) { + Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + + synchronized (this) { + if (mLeHandle == -1) { + if (DBG) + Log.d(TAG, "onClientRegistered LE scan canceled"); + } + + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + try { + mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); + } catch (RemoteException e) { + Log.e(TAG, "fail to start le scan: " + e); + mLeHandle = -1; + } + } else { + // registration failed + mLeHandle = -1; + } + notifyAll(); + } + } + + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + /** + * Callback reporting an LE scan result. + * + * @hide + */ + @Override + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) + Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); + + // Check null in case the scan has been stopped + synchronized (this) { + if (mLeHandle <= 0) + return; + } + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + address); + long scanNanos = SystemClock.elapsedRealtimeNanos(); + ScanResult result = new ScanResult(device, advData, rssi, + scanNanos); + mScanCallback.onAdvertisementUpdate(result); + } + + @Override + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + @Override + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + @Override + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + @Override + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descUuid) { + // no op + } + + @Override + public void onSearchComplete(String address, int status) { + // no op + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + @Override + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid, byte[] value) { + // no op + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int descInstId, ParcelUuid descrUuid) { + // no op + } + + @Override + public void onExecuteWrite(String address, int status) { + // no op + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + + @Override + public void onAdvertiseStateChange(int advertiseState, int status) { + // no op + } + + @Override + public void onMultiAdvertiseCallback(int status) { + // no op + } + + @Override + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } + } + + private void postCallbackError(final ScanCallback callback, final int errorCode) { + mHandler.post(new Runnable() { + @Override + public void run() { + callback.onScanFailed(errorCode); + } + }); + } +} diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java new file mode 100644 index 00000000000..50ebf5095bf --- /dev/null +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 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.le; + +import java.util.List; + +/** + * Callback of Bluetooth LE scans. The results of the scans will be delivered through the callbacks. + */ +public abstract class ScanCallback { + + /** + * Fails to start scan as BLE scan with the same settings is already started by the app. + */ + public static final int SCAN_FAILED_ALREADY_STARTED = 1; + /** + * Fails to start scan as app cannot be registered. + */ + public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2; + /** + * Fails to start scan due to gatt service failure. + */ + public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3; + /** + * Fails to start scan due to controller failure. + */ + public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4; + + /** + * Callback when a BLE advertisement is found. + * + * @param result A Bluetooth LE scan result. + */ + public abstract void onAdvertisementUpdate(ScanResult result); + + /** + * Callback when the BLE advertisement is found for the first time. + * + * @param result The Bluetooth LE scan result when the onFound event is triggered. + * @hide + */ + public abstract void onAdvertisementFound(ScanResult result); + + /** + * Callback when the BLE advertisement was lost. Note a device has to be "found" before it's + * lost. + * + * @param result The Bluetooth scan result that was last found. + * @hide + */ + public abstract void onAdvertisementLost(ScanResult result); + + /** + * Callback when batch results are delivered. + * + * @param results List of scan results that are previously scanned. + * @hide + */ + public abstract void onBatchScanResults(List results); + + /** + * Callback when scan failed. + */ + public abstract void onScanFailed(int errorCode); +} diff --git a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl b/framework/java/android/bluetooth/le/ScanFilter.aidl similarity index 87% rename from framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl rename to framework/java/android/bluetooth/le/ScanFilter.aidl index 4aa8881422d..4cecfe62e11 100644 --- a/framework/java/android/bluetooth/BluetoothLeAdvertiseScanData.aidl +++ b/framework/java/android/bluetooth/le/ScanFilter.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -parcelable BluetoothLeAdvertiseScanData.AdvertisementData; \ No newline at end of file +parcelable ScanFilter; diff --git a/framework/java/android/bluetooth/BluetoothLeScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java similarity index 71% rename from framework/java/android/bluetooth/BluetoothLeScanFilter.java rename to framework/java/android/bluetooth/le/ScanFilter.java index 2ed85ba0a70..c2e316b78a6 100644 --- a/framework/java/android/bluetooth/BluetoothLeScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; import android.annotation.Nullable; -import android.bluetooth.BluetoothLeAdvertiseScanData.ScanRecord; -import android.bluetooth.BluetoothLeScanner.ScanResult; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -29,8 +29,7 @@ import java.util.Objects; import java.util.UUID; /** - * {@link BluetoothLeScanFilter} abstracts different scan filters across Bluetooth Advertisement - * packet fields. + * {@link ScanFilter} abstracts different scan filters across Bluetooth Advertisement packet fields. *

        * Current filtering on the following fields are supported: *

      • Service UUIDs which identify the bluetooth gatt services running on the device. @@ -40,10 +39,10 @@ import java.util.UUID; *
      • Service data which is the data associated with a service. *
      • Manufacturer specific data which is the data associated with a particular manufacturer. * - * @see BluetoothLeAdvertiseScanData.ScanRecord + * @see ScanRecord * @see BluetoothLeScanner */ -public final class BluetoothLeScanFilter implements Parcelable { +public final class ScanFilter implements Parcelable { @Nullable private final String mLocalName; @@ -70,7 +69,7 @@ public final class BluetoothLeScanFilter implements Parcelable { private final int mMinRssi; private final int mMaxRssi; - private BluetoothLeScanFilter(String name, String macAddress, ParcelUuid uuid, + private ScanFilter(String name, String macAddress, ParcelUuid uuid, ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, int minRssi, int maxRssi) { @@ -105,88 +104,93 @@ public final class BluetoothLeScanFilter implements Parcelable { dest.writeInt(mServiceUuid == null ? 0 : 1); if (mServiceUuid != null) { dest.writeParcelable(mServiceUuid, flags); - } - dest.writeInt(mServiceUuidMask == null ? 0 : 1); - if (mServiceUuidMask != null) { - dest.writeParcelable(mServiceUuidMask, flags); + dest.writeInt(mServiceUuidMask == null ? 0 : 1); + if (mServiceUuidMask != null) { + dest.writeParcelable(mServiceUuidMask, flags); + } } dest.writeInt(mServiceData == null ? 0 : mServiceData.length); if (mServiceData != null) { dest.writeByteArray(mServiceData); - } - dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); - if (mServiceDataMask != null) { - dest.writeByteArray(mServiceDataMask); + dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); + if (mServiceDataMask != null) { + dest.writeByteArray(mServiceDataMask); + } } dest.writeInt(mManufacturerId); dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length); if (mManufacturerData != null) { dest.writeByteArray(mManufacturerData); - } - dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); - if (mManufacturerDataMask != null) { - dest.writeByteArray(mManufacturerDataMask); + dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); + if (mManufacturerDataMask != null) { + dest.writeByteArray(mManufacturerDataMask); + } } dest.writeInt(mMinRssi); dest.writeInt(mMaxRssi); } /** - * A {@link android.os.Parcelable.Creator} to create {@link BluetoothLeScanFilter} form parcel. + * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} form parcel. */ - public static final Creator - CREATOR = new Creator() { + public static final Creator + CREATOR = new Creator() { @Override - public BluetoothLeScanFilter[] newArray(int size) { - return new BluetoothLeScanFilter[size]; + public ScanFilter[] newArray(int size) { + return new ScanFilter[size]; } @Override - public BluetoothLeScanFilter createFromParcel(Parcel in) { - Builder builder = newBuilder(); + public ScanFilter createFromParcel(Parcel in) { + Builder builder = new Builder(); if (in.readInt() == 1) { - builder.name(in.readString()); + builder.setName(in.readString()); } if (in.readInt() == 1) { - builder.macAddress(in.readString()); + builder.setMacAddress(in.readString()); } if (in.readInt() == 1) { ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.serviceUuid(uuid); - } - if (in.readInt() == 1) { - ParcelUuid uuidMask = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.serviceUuidMask(uuidMask); + builder.setServiceUuid(uuid); + if (in.readInt() == 1) { + ParcelUuid uuidMask = in.readParcelable( + ParcelUuid.class.getClassLoader()); + builder.setServiceUuid(uuid, uuidMask); + } } + int serviceDataLength = in.readInt(); if (serviceDataLength > 0) { byte[] serviceData = new byte[serviceDataLength]; in.readByteArray(serviceData); - builder.serviceData(serviceData); - } - int serviceDataMaskLength = in.readInt(); - if (serviceDataMaskLength > 0) { - byte[] serviceDataMask = new byte[serviceDataMaskLength]; - in.readByteArray(serviceDataMask); - builder.serviceDataMask(serviceDataMask); + builder.setServiceData(serviceData); + int serviceDataMaskLength = in.readInt(); + if (serviceDataMaskLength > 0) { + byte[] serviceDataMask = new byte[serviceDataMaskLength]; + in.readByteArray(serviceDataMask); + builder.setServiceData(serviceData, serviceDataMask); + } } + int manufacturerId = in.readInt(); int manufacturerDataLength = in.readInt(); if (manufacturerDataLength > 0) { byte[] manufacturerData = new byte[manufacturerDataLength]; in.readByteArray(manufacturerData); - builder.manufacturerData(manufacturerId, manufacturerData); - } - int manufacturerDataMaskLength = in.readInt(); - if (manufacturerDataMaskLength > 0) { - byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; - in.readByteArray(manufacturerDataMask); - builder.manufacturerDataMask(manufacturerDataMask); + builder.setManufacturerData(manufacturerId, manufacturerData); + int manufacturerDataMaskLength = in.readInt(); + if (manufacturerDataMaskLength > 0) { + byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; + in.readByteArray(manufacturerDataMask); + builder.setManufacturerData(manufacturerId, manufacturerData, + manufacturerDataMask); + } } + int minRssi = in.readInt(); int maxRssi = in.readInt(); - builder.rssiRange(minRssi, maxRssi); + builder.setRssiRange(minRssi, maxRssi); return builder.build(); } }; @@ -199,9 +203,10 @@ public final class BluetoothLeScanFilter implements Parcelable { return mLocalName; } - @Nullable /** - * Returns the filter set on the service uuid. - */ + /** + * Returns the filter set on the service uuid. + */ + @Nullable public ParcelUuid getServiceUuid() { return mServiceUuid; } @@ -277,7 +282,7 @@ public final class BluetoothLeScanFilter implements Parcelable { } byte[] scanRecordBytes = scanResult.getScanRecord(); - ScanRecord scanRecord = ScanRecord.getParser().parseFromScanRecord(scanRecordBytes); + ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordBytes); // Scan record is null but there exist filters on it. if (scanRecord == null @@ -386,13 +391,13 @@ public final class BluetoothLeScanFilter implements Parcelable { if (obj == null || getClass() != obj.getClass()) { return false; } - BluetoothLeScanFilter other = (BluetoothLeScanFilter) obj; + ScanFilter other = (ScanFilter) obj; return Objects.equals(mLocalName, other.mLocalName) && Objects.equals(mMacAddress, other.mMacAddress) && - mManufacturerId == other.mManufacturerId && + mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && + mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && @@ -400,17 +405,9 @@ public final class BluetoothLeScanFilter implements Parcelable { } /** - * Returns the {@link Builder} for {@link BluetoothLeScanFilter}. + * Builder class for {@link ScanFilter}. */ - public static Builder newBuilder() { - return new Builder(); - } - - /** - * Builder class for {@link BluetoothLeScanFilter}. Use - * {@link BluetoothLeScanFilter#newBuilder()} to get an instance of the {@link Builder}. - */ - public static class Builder { + public static final class Builder { private String mLocalName; private String mMacAddress; @@ -428,27 +425,23 @@ public final class BluetoothLeScanFilter implements Parcelable { private int mMinRssi = Integer.MIN_VALUE; private int mMaxRssi = Integer.MAX_VALUE; - // Private constructor, use BluetoothLeScanFilter.newBuilder instead. - private Builder() { - } - /** - * Set filtering on local name. + * Set filter on local name. */ - public Builder name(String localName) { + public Builder setName(String localName) { mLocalName = localName; return this; } /** - * Set filtering on device mac address. + * Set filter on device mac address. * * @param macAddress The device mac address for the filter. It needs to be in the format of * "01:02:03:AB:CD:EF". The mac address can be validated using * {@link BluetoothAdapter#checkBluetoothAddress}. * @throws IllegalArgumentException If the {@code macAddress} is invalid. */ - public Builder macAddress(String macAddress) { + public Builder setMacAddress(String macAddress) { if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) { throw new IllegalArgumentException("invalid mac address " + macAddress); } @@ -457,68 +450,115 @@ public final class BluetoothLeScanFilter implements Parcelable { } /** - * Set filtering on service uuid. + * Set filter on service uuid. */ - public Builder serviceUuid(ParcelUuid serviceUuid) { + public Builder setServiceUuid(ParcelUuid serviceUuid) { mServiceUuid = serviceUuid; + mUuidMask = null; // clear uuid mask return this; } /** - * Set partial uuid filter. The {@code uuidMask} is the bit mask for the {@code uuid} set - * through {@link #serviceUuid(ParcelUuid)} method. Set any bit in the mask to 1 to indicate - * a match is needed for the bit in {@code serviceUuid}, and 0 to ignore that bit. - *

        - * The length of {@code uuidMask} must be the same as {@code serviceUuid}. + * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the + * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the + * bit in {@code serviceUuid}, and 0 to ignore that bit. + * + * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but + * {@code uuidMask} is not {@code null}. */ - public Builder serviceUuidMask(ParcelUuid uuidMask) { + public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) { + if (mUuidMask != null && mServiceUuid == null) { + throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); + } + mServiceUuid = serviceUuid; mUuidMask = uuidMask; return this; } /** - * Set service data filter. + * Set filtering on service data. */ - public Builder serviceData(byte[] serviceData) { + public Builder setServiceData(byte[] serviceData) { mServiceData = serviceData; + mServiceDataMask = null; // clear service data mask return this; } /** - * Set partial service data filter bit mask. For any bit in the mask, set it to 1 if it - * needs to match the one in service data, otherwise set it to 0 to ignore that bit. + * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to + * match the one in service data, otherwise set it to 0 to ignore that bit. *

        - * The {@code serviceDataMask} must have the same length of the {@code serviceData} set - * through {@link #serviceData(byte[])}. + * The {@code serviceDataMask} must have the same length of the {@code serviceData}. + * + * @throws IllegalArgumentException If {@code serviceDataMask} is {@code null} while + * {@code serviceData} is not or {@code serviceDataMask} and {@code serviceData} + * has different length. */ - public Builder serviceDataMask(byte[] serviceDataMask) { + public Builder setServiceData(byte[] serviceData, byte[] serviceDataMask) { + if (mServiceDataMask != null) { + if (mServiceData == null) { + throw new IllegalArgumentException( + "serviceData is null while serviceDataMask is not null"); + } + // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two + // byte array need to be the same. + if (mServiceData.length != mServiceDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for service data and service data mask"); + } + } + mServiceData = serviceData; mServiceDataMask = serviceDataMask; return this; } /** - * Set manufacturerId and manufacturerData. A negative manufacturerId is considered as - * invalid id. + * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. *

        * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. + * + * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. */ - public Builder manufacturerData(int manufacturerId, byte[] manufacturerData) { + public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) { if (manufacturerData != null && manufacturerId < 0) { throw new IllegalArgumentException("invalid manufacture id"); } mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; + mManufacturerDataMask = null; // clear manufacturer data mask return this; } /** - * Set partial manufacture data filter bit mask. For any bit in the mask, set it the 1 if it + * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it * needs to match the one in manufacturer data, otherwise set it to 0. *

        - * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData} - * set through {@link #manufacturerData(int, byte[])}. + * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}. + * + * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or + * {@code manufacturerData} is null while {@code manufacturerDataMask} is not, + * or {@code manufacturerData} and {@code manufacturerDataMask} have different + * length. */ - public Builder manufacturerDataMask(byte[] manufacturerDataMask) { + public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData, + byte[] manufacturerDataMask) { + if (manufacturerData != null && manufacturerId < 0) { + throw new IllegalArgumentException("invalid manufacture id"); + } + if (mManufacturerDataMask != null) { + if (mManufacturerData == null) { + throw new IllegalArgumentException( + "manufacturerData is null while manufacturerDataMask is not null"); + } + // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths + // of the two byte array need to be the same. + if (mManufacturerData.length != mManufacturerDataMask.length) { + throw new IllegalArgumentException( + "size mismatch for manufacturerData and manufacturerDataMask"); + } + } + mManufacturerId = manufacturerId; + mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; return this; } @@ -527,48 +567,19 @@ public final class BluetoothLeScanFilter implements Parcelable { * Set the desired rssi range for the filter. A scan result with rssi in the range of * [minRssi, maxRssi] will be consider as a match. */ - public Builder rssiRange(int minRssi, int maxRssi) { + public Builder setRssiRange(int minRssi, int maxRssi) { mMinRssi = minRssi; mMaxRssi = maxRssi; return this; } /** - * Build {@link BluetoothLeScanFilter}. + * Build {@link ScanFilter}. * * @throws IllegalArgumentException If the filter cannot be built. */ - public BluetoothLeScanFilter build() { - if (mUuidMask != null && mServiceUuid == null) { - throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); - } - - if (mServiceDataMask != null) { - if (mServiceData == null) { - throw new IllegalArgumentException( - "serviceData is null while serviceDataMask is not null"); - } - // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two - // byte array need to be the same. - if (mServiceData.length != mServiceDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for service data and service data mask"); - } - } - - if (mManufacturerDataMask != null) { - if (mManufacturerData == null) { - throw new IllegalArgumentException( - "manufacturerData is null while manufacturerDataMask is not null"); - } - // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths - // of the two byte array need to be the same. - if (mManufacturerData.length != mManufacturerDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for manufacturerData and manufacturerDataMask"); - } - } - return new BluetoothLeScanFilter(mLocalName, mMacAddress, + public ScanFilter build() { + return new ScanFilter(mLocalName, mMacAddress, mServiceUuid, mUuidMask, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java new file mode 100644 index 00000000000..bd7304bca9e --- /dev/null +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.Nullable; +import android.bluetooth.BluetoothUuid; +import android.os.ParcelUuid; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents a scan record from Bluetooth LE scan. + */ +public final class ScanRecord { + + private static final String TAG = "ScanRecord"; + + // The following data type values are assigned by Bluetooth SIG. + // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18. + private static final int DATA_TYPE_FLAGS = 0x01; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; + private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; + private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; + private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; + private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; + private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; + private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; + private static final int DATA_TYPE_SERVICE_DATA = 0x16; + private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; + + // Flags of the advertising data. + private final int mAdvertiseFlags; + + @Nullable + private final List mServiceUuids; + + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; + + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; + + // Transmission power level(in dB). + private final int mTxPowerLevel; + + // Local name of the Bluetooth LE device. + private final String mLocalName; + + /** + * Returns the advertising flags indicating the discoverable mode and capability of the device. + * Returns -1 if the flag field is not set. + */ + public int getAdvertiseFlags() { + return mAdvertiseFlags; + } + + /** + * Returns a list of service uuids within the advertisement that are used to identify the + * bluetooth gatt services. + */ + public List getServiceUuids() { + return mServiceUuids; + } + + /** + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. + */ + public int getManufacturerId() { + return mManufacturerId; + } + + /** + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. The first 2 bytes of the data contain the company id. + */ + public byte[] getManufacturerSpecificData() { + return mManufacturerSpecificData; + } + + /** + * Returns a 16 bit uuid of the service that the service data is associated with. + */ + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + + /** + * Returns service data. The first two bytes should be a 16 bit service uuid associated with the + * service data. + */ + public byte[] getServiceData() { + return mServiceData; + } + + /** + * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE} + * if the field is not set. This value can be used to calculate the path loss of a received + * packet using the following equation: + *

        + * pathloss = txPowerLevel - rssi + */ + public int getTxPowerLevel() { + return mTxPowerLevel; + } + + /** + * Returns the local name of the BLE device. The is a UTF-8 encoded string. + */ + @Nullable + public String getLocalName() { + return mLocalName; + } + + private ScanRecord(List serviceUuids, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, + String localName) { + mServiceUuids = serviceUuids; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; + mLocalName = localName; + mAdvertiseFlags = advertiseFlags; + mTxPowerLevel = txPowerLevel; + } + + @Override + public String toString() { + return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids + + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + + ", mTxPowerLevel=" + mTxPowerLevel + ", mLocalName=" + mLocalName + "]"; + } + + /** + * Parse scan record bytes to {@link ScanRecord}. + *

        + * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18. + *

        + * All numerical multi-byte entities and values shall use little-endian byte + * order. + * + * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. + */ + public static ScanRecord parseFromBytes(byte[] scanRecord) { + if (scanRecord == null) { + return null; + } + + int currentPos = 0; + int advertiseFlag = -1; + List serviceUuids = new ArrayList(); + String localName = null; + int txPowerLevel = Integer.MIN_VALUE; + ParcelUuid serviceDataUuid = null; + byte[] serviceData = null; + int manufacturerId = -1; + byte[] manufacturerSpecificData = null; + + try { + while (currentPos < scanRecord.length) { + // length is unsigned int. + int length = scanRecord[currentPos++] & 0xFF; + if (length == 0) { + break; + } + // Note the length includes the length of the field type itself. + int dataLength = length - 1; + // fieldType is unsigned int. + int fieldType = scanRecord[currentPos++] & 0xFF; + switch (fieldType) { + case DATA_TYPE_FLAGS: + advertiseFlag = scanRecord[currentPos] & 0xFF; + break; + case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, + dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); + break; + case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: + case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: + parseServiceUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); + break; + case DATA_TYPE_LOCAL_NAME_SHORT: + case DATA_TYPE_LOCAL_NAME_COMPLETE: + localName = new String( + extractBytes(scanRecord, currentPos, dataLength)); + break; + case DATA_TYPE_TX_POWER_LEVEL: + txPowerLevel = scanRecord[currentPos]; + break; + case DATA_TYPE_SERVICE_DATA: + serviceData = extractBytes(scanRecord, currentPos, dataLength); + // The first two bytes of the service data are service data uuid. + int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; + byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, + serviceUuidLength); + serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); + break; + case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: + manufacturerSpecificData = extractBytes(scanRecord, currentPos, + dataLength); + // The first two bytes of the manufacturer specific data are + // manufacturer ids in little endian. + manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + + (manufacturerSpecificData[0] & 0xFF); + break; + default: + // Just ignore, we don't handle such data type. + break; + } + currentPos += dataLength; + } + + if (serviceUuids.isEmpty()) { + serviceUuids = null; + } + return new ScanRecord(serviceUuids, serviceDataUuid, serviceData, + manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, + localName); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); + return null; + } + } + + // Parse service uuids. + private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, + int uuidLength, List serviceUuids) { + while (dataLength > 0) { + byte[] uuidBytes = extractBytes(scanRecord, currentPos, + uuidLength); + serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); + dataLength -= uuidLength; + currentPos += uuidLength; + } + return currentPos; + } + + // Helper method to extract bytes from byte array. + private static byte[] extractBytes(byte[] scanRecord, int start, int length) { + byte[] bytes = new byte[length]; + System.arraycopy(scanRecord, start, bytes, 0, length); + return bytes; + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeScanner.aidl b/framework/java/android/bluetooth/le/ScanResult.aidl similarity index 85% rename from framework/java/android/bluetooth/BluetoothLeScanner.aidl rename to framework/java/android/bluetooth/le/ScanResult.aidl index 8cecdd7aac0..39430350dac 100644 --- a/framework/java/android/bluetooth/BluetoothLeScanner.aidl +++ b/framework/java/android/bluetooth/le/ScanResult.aidl @@ -14,7 +14,6 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -parcelable BluetoothLeScanner.ScanResult; -parcelable BluetoothLeScanner.Settings; +parcelable ScanResult; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java new file mode 100644 index 00000000000..7e6e8f81c78 --- /dev/null +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.Nullable; +import android.bluetooth.BluetoothDevice; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * ScanResult for Bluetooth LE scan. + */ +public final class ScanResult implements Parcelable { + // Remote bluetooth device. + private BluetoothDevice mDevice; + + // Scan record, including advertising data and scan response data. + private byte[] mScanRecord; + + // Received signal strength. + private int mRssi; + + // Device timestamp when the result was last seen. + private long mTimestampNanos; + + /** + * Constructor of scan result. + * + * @hide + */ + public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, + long timestampNanos) { + mDevice = device; + mScanRecord = scanRecord; + mRssi = rssi; + mTimestampNanos = timestampNanos; + } + + private ScanResult(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mDevice != null) { + dest.writeInt(1); + mDevice.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mScanRecord != null) { + dest.writeInt(1); + dest.writeByteArray(mScanRecord); + } else { + dest.writeInt(0); + } + dest.writeInt(mRssi); + dest.writeLong(mTimestampNanos); + } + + private void readFromParcel(Parcel in) { + if (in.readInt() == 1) { + mDevice = BluetoothDevice.CREATOR.createFromParcel(in); + } + if (in.readInt() == 1) { + mScanRecord = in.createByteArray(); + } + mRssi = in.readInt(); + mTimestampNanos = in.readLong(); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the remote bluetooth device identified by the bluetooth device address. + */ + @Nullable + public BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Returns the scan record, which can be a combination of advertisement and scan response. + */ + @Nullable + public byte[] getScanRecord() { + return mScanRecord; + } + + /** + * Returns the received signal strength in dBm. The valid range is [-127, 127]. + */ + public int getRssi() { + return mRssi; + } + + /** + * Returns timestamp since boot when the scan record was observed. + */ + public long getTimestampNanos() { + return mTimestampNanos; + } + + @Override + public int hashCode() { + return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ScanResult other = (ScanResult) obj; + return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && + Objects.deepEquals(mScanRecord, other.mScanRecord) + && (mTimestampNanos == other.mTimestampNanos); + } + + @Override + public String toString() { + return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" + + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos=" + + mTimestampNanos + '}'; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public ScanResult createFromParcel(Parcel source) { + return new ScanResult(source); + } + + @Override + public ScanResult[] newArray(int size) { + return new ScanResult[size]; + } + }; + +} diff --git a/framework/java/android/bluetooth/le/ScanSettings.aidl b/framework/java/android/bluetooth/le/ScanSettings.aidl new file mode 100644 index 00000000000..eb169c1209f --- /dev/null +++ b/framework/java/android/bluetooth/le/ScanSettings.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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.le; + +parcelable ScanSettings; diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java new file mode 100644 index 00000000000..0a856756326 --- /dev/null +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2014 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Settings for Bluetooth LE scan. + */ +public final class ScanSettings implements Parcelable { + /** + * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the + * least power. + */ + public static final int SCAN_MODE_LOW_POWER = 0; + /** + * Perform Bluetooth LE scan in balanced power mode. + */ + public static final int SCAN_MODE_BALANCED = 1; + /** + * Scan using highest duty cycle. It's recommended only using this mode when the application is + * running in foreground. + */ + public static final int SCAN_MODE_LOW_LATENCY = 2; + + /** + * Callback each time when a bluetooth advertisement is found. + */ + public static final int CALLBACK_TYPE_ON_UPDATE = 0; + /** + * Callback when a bluetooth advertisement is found for the first time. + * + * @hide + */ + public static final int CALLBACK_TYPE_ON_FOUND = 1; + /** + * Callback when a bluetooth advertisement is found for the first time, then lost. + * + * @hide + */ + public static final int CALLBACK_TYPE_ON_LOST = 2; + + /** + * Full scan result which contains device mac address, rssi, advertising and scan response and + * scan timestamp. + */ + public static final int SCAN_RESULT_TYPE_FULL = 0; + /** + * Truncated scan result which contains device mac address, rssi and scan timestamp. Note it's + * possible for an app to get more scan results that it asks if there are multiple apps using + * this type. TODO: decide whether we could unhide this setting. + * + * @hide + */ + public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; + + // Bluetooth LE scan mode. + private int mScanMode; + + // Bluetooth LE scan callback type + private int mCallbackType; + + // Bluetooth LE scan result type + private int mScanResultType; + + // Time of delay for reporting the scan result + private long mReportDelayNanos; + + public int getScanMode() { + return mScanMode; + } + + public int getCallbackType() { + return mCallbackType; + } + + public int getScanResultType() { + return mScanResultType; + } + + /** + * Returns report delay timestamp based on the device clock. + */ + public long getReportDelayNanos() { + return mReportDelayNanos; + } + + private ScanSettings(int scanMode, int callbackType, int scanResultType, + long reportDelayNanos) { + mScanMode = scanMode; + mCallbackType = callbackType; + mScanResultType = scanResultType; + mReportDelayNanos = reportDelayNanos; + } + + private ScanSettings(Parcel in) { + mScanMode = in.readInt(); + mCallbackType = in.readInt(); + mScanResultType = in.readInt(); + mReportDelayNanos = in.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mScanMode); + dest.writeInt(mCallbackType); + dest.writeInt(mScanResultType); + dest.writeLong(mReportDelayNanos); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator + CREATOR = new Creator() { + @Override + public ScanSettings[] newArray(int size) { + return new ScanSettings[size]; + } + + @Override + public ScanSettings createFromParcel(Parcel in) { + return new ScanSettings(in); + } + }; + + /** + * Builder for {@link ScanSettings}. + */ + public static final class Builder { + private int mScanMode = SCAN_MODE_LOW_POWER; + private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; + private int mScanResultType = SCAN_RESULT_TYPE_FULL; + private long mReportDelayNanos = 0; + + /** + * Set scan mode for Bluetooth LE scan. + * + * @param scanMode The scan mode can be one of + * {@link ScanSettings#SCAN_MODE_LOW_POWER}, + * {@link ScanSettings#SCAN_MODE_BALANCED} or + * {@link ScanSettings#SCAN_MODE_LOW_LATENCY}. + * @throws IllegalArgumentException If the {@code scanMode} is invalid. + */ + public Builder setScanMode(int scanMode) { + if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { + throw new IllegalArgumentException("invalid scan mode " + scanMode); + } + mScanMode = scanMode; + return this; + } + + /** + * Set callback type for Bluetooth LE scan. + * + * @param callbackType The callback type for the scan. Can only be + * {@link ScanSettings#CALLBACK_TYPE_ON_UPDATE}. + * @throws IllegalArgumentException If the {@code callbackType} is invalid. + */ + public Builder setCallbackType(int callbackType) { + if (callbackType < CALLBACK_TYPE_ON_UPDATE + || callbackType > CALLBACK_TYPE_ON_LOST) { + throw new IllegalArgumentException("invalid callback type - " + callbackType); + } + mCallbackType = callbackType; + return this; + } + + /** + * Set scan result type for Bluetooth LE scan. + * + * @param scanResultType Type for scan result, could be either + * {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or + * {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}. + * @throws IllegalArgumentException If the {@code scanResultType} is invalid. + * @hide + */ + public Builder setScanResultType(int scanResultType) { + if (scanResultType < SCAN_RESULT_TYPE_FULL + || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { + throw new IllegalArgumentException( + "invalid scanResultType - " + scanResultType); + } + mScanResultType = scanResultType; + return this; + } + + /** + * Set report delay timestamp for Bluetooth LE scan. + */ + public Builder setReportDelayNanos(long reportDelayNanos) { + mReportDelayNanos = reportDelayNanos; + return this; + } + + /** + * Build {@link ScanSettings}. + */ + public ScanSettings build() { + return new ScanSettings(mScanMode, mCallbackType, mScanResultType, + mReportDelayNanos); + } + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java similarity index 59% rename from framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java rename to framework/tests/src/android/bluetooth/le/ScanFilterTest.java index ec35d85deb1..bf34f1d2381 100644 --- a/framework/tests/src/android/bluetooth/BluetoothLeScanFilterTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -import android.bluetooth.BluetoothLeScanner.ScanResult; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.ParcelUuid; import android.test.suitebuilder.annotation.SmallTest; @@ -26,22 +27,21 @@ import junit.framework.TestCase; /** * Unit test cases for Bluetooth LE scan filters. *

        - * To run this test, use adb shell am instrument -e class - * 'android.bluetooth.BluetoothLeScanFilterTest' -w + * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' */ -public class BluetoothLeScanFilterTest extends TestCase { +public class ScanFilterTest extends TestCase { private static final String DEVICE_MAC = "01:02:03:04:05:AB"; private ScanResult mScanResult; - private BluetoothLeScanFilter.Builder mFilterBuilder; + private ScanFilter.Builder mFilterBuilder; @Override protected void setUp() throws Exception { byte[] scanRecord = new byte[] { 0x02, 0x01, 0x1a, // advertising flags 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids - 0x04, 0x09, 0x50, 0x65, 0x64, // name + 0x04, 0x09, 0x50, 0x65, 0x64, // setName 0x02, 0x0A, (byte) 0xec, // tx power level 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data @@ -51,134 +51,135 @@ public class BluetoothLeScanFilterTest extends TestCase { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L); - mFilterBuilder = BluetoothLeScanFilter.newBuilder(); + mFilterBuilder = new ScanFilter.Builder(); } @SmallTest - public void testNameFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.name("Ped").build(); - assertTrue("name filter fails", filter.matches(mScanResult)); + public void testsetNameFilter() { + ScanFilter filter = mFilterBuilder.setName("Ped").build(); + assertTrue("setName filter fails", filter.matches(mScanResult)); - filter = mFilterBuilder.name("Pem").build(); - assertFalse("name filter fails", filter.matches(mScanResult)); + filter = mFilterBuilder.setName("Pem").build(); + assertFalse("setName filter fails", filter.matches(mScanResult)); } @SmallTest public void testDeviceFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.macAddress(DEVICE_MAC).build(); + ScanFilter filter = mFilterBuilder.setMacAddress(DEVICE_MAC).build(); assertTrue("device filter fails", filter.matches(mScanResult)); - filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build(); assertFalse("device filter fails", filter.matches(mScanResult)); } @SmallTest - public void testServiceUuidFilter() { - BluetoothLeScanFilter filter = mFilterBuilder.serviceUuid( + public void testsetServiceUuidFilter() { + ScanFilter filter = mFilterBuilder.setServiceUuid( ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build(); assertTrue("uuid filter fails", filter.matches(mScanResult)); - filter = mFilterBuilder.serviceUuid( + filter = mFilterBuilder.setServiceUuid( ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); assertFalse("uuid filter fails", filter.matches(mScanResult)); filter = mFilterBuilder - .serviceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")) - .serviceUuidMask(ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) + .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"), + ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) .build(); assertTrue("uuid filter fails", filter.matches(mScanResult)); } @SmallTest - public void testServiceDataFilter() { - byte[] serviceData = new byte[] { + public void testsetServiceDataFilter() { + byte[] setServiceData = new byte[] { 0x0b, 0x11, 0x50, 0x64 }; - BluetoothLeScanFilter filter = mFilterBuilder.serviceData(serviceData).build(); + ScanFilter filter = mFilterBuilder.setServiceData(setServiceData).build(); assertTrue("service data filter fails", filter.matches(mScanResult)); byte[] nonMatchData = new byte[] { 0x0b, 0x01, 0x50, 0x64 }; - filter = mFilterBuilder.serviceData(nonMatchData).build(); + filter = mFilterBuilder.setServiceData(nonMatchData).build(); assertFalse("service data filter fails", filter.matches(mScanResult)); byte[] mask = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.serviceData(nonMatchData).serviceDataMask(mask).build(); + filter = mFilterBuilder.setServiceData(nonMatchData, mask).build(); assertTrue("partial service data filter fails", filter.matches(mScanResult)); } @SmallTest public void testManufacturerSpecificData() { - byte[] manufacturerData = new byte[] { + byte[] setManufacturerData = new byte[] { (byte) 0xE0, 0x00, 0x02, 0x15 }; int manufacturerId = 224; - BluetoothLeScanFilter filter = - mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); - assertTrue("manufacturerData filter fails", filter.matches(mScanResult)); + ScanFilter filter = + mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build(); + assertTrue("setManufacturerData filter fails", filter.matches(mScanResult)); byte[] nonMatchData = new byte[] { (byte) 0xF0, 0x00, 0x02, 0x15 }; - filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData).build(); - assertFalse("manufacturerData filter fails", filter.matches(mScanResult)); + filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build(); + assertFalse("setManufacturerData filter fails", filter.matches(mScanResult)); byte[] mask = new byte[] { (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.manufacturerData(manufacturerId, nonMatchData) - .manufacturerDataMask(mask).build(); - assertTrue("partial manufacturerData filter fails", filter.matches(mScanResult)); + filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build(); + assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult)); } @SmallTest public void testReadWriteParcel() { - BluetoothLeScanFilter filter = mFilterBuilder.build(); + ScanFilter filter = mFilterBuilder.build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.name("Ped").build(); + filter = mFilterBuilder.setName("Ped").build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.macAddress("11:22:33:44:55:66").build(); + filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.serviceUuid( + filter = mFilterBuilder.setServiceUuid( ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.serviceUuidMask( + filter = mFilterBuilder.setServiceUuid( + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"), ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); testReadWriteParcelForFilter(filter); - byte[] serviceData = new byte[] { + byte[] setServiceData = new byte[] { 0x0b, 0x11, 0x50, 0x64 }; - filter = mFilterBuilder.serviceData(serviceData).build(); + filter = mFilterBuilder.setServiceData(setServiceData).build(); testReadWriteParcelForFilter(filter); byte[] serviceDataMask = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.serviceDataMask(serviceDataMask).build(); + filter = mFilterBuilder.setServiceData(setServiceData, serviceDataMask).build(); testReadWriteParcelForFilter(filter); byte[] manufacturerData = new byte[] { (byte) 0xE0, 0x00, 0x02, 0x15 }; int manufacturerId = 224; - filter = mFilterBuilder.manufacturerData(manufacturerId, manufacturerData).build(); + filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build(); testReadWriteParcelForFilter(filter); byte[] manufacturerDataMask = new byte[] { (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.manufacturerDataMask(manufacturerDataMask).build(); + filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData, + manufacturerDataMask).build(); testReadWriteParcelForFilter(filter); } - private void testReadWriteParcelForFilter(BluetoothLeScanFilter filter) { + private void testReadWriteParcelForFilter(ScanFilter filter) { Parcel parcel = Parcel.obtain(); filter.writeToParcel(parcel, 0); parcel.setDataPosition(0); - BluetoothLeScanFilter filterFromParcel = - BluetoothLeScanFilter.CREATOR.createFromParcel(parcel); + ScanFilter filterFromParcel = + ScanFilter.CREATOR.createFromParcel(parcel); System.out.println(filterFromParcel); assertEquals(filter, filterFromParcel); } diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java similarity index 87% rename from framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java rename to framework/tests/src/android/bluetooth/le/ScanRecordTest.java index eb6c419bad9..cece96b14b5 100644 --- a/framework/tests/src/android/bluetooth/BluetoothLeAdvertiseScanDataTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; +import android.bluetooth.le.ScanRecord; import android.os.ParcelUuid; import android.test.suitebuilder.annotation.SmallTest; @@ -24,13 +25,13 @@ import junit.framework.TestCase; import java.util.Arrays; /** - * Unit test cases for {@link BluetoothLeAdvertiseScanData}. + * Unit test cases for {@link ScanRecord}. *

        * To run this test, use adb shell am instrument -e class - * 'android.bluetooth.BluetoothLeAdvertiseScanDataTest' -w + * 'android.bluetooth.ScanRecordTest' -w * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' */ -public class BluetoothLeAdvertiseScanDataTest extends TestCase { +public class ScanRecordTest extends TestCase { @SmallTest public void testParser() { @@ -43,8 +44,7 @@ public class BluetoothLeAdvertiseScanDataTest extends TestCase { 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble }; - BluetoothLeAdvertiseScanData.ScanRecord data = BluetoothLeAdvertiseScanData.ScanRecord - .getParser().parseFromScanRecord(scanRecord); + ScanRecord data = ScanRecord.parseFromBytes(scanRecord); assertEquals(0x1a, data.getAdvertiseFlags()); ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); diff --git a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java b/framework/tests/src/android/bluetooth/le/ScanResultTest.java similarity index 79% rename from framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java rename to framework/tests/src/android/bluetooth/le/ScanResultTest.java index 8064ba8ac3b..241e88fb3d4 100644 --- a/framework/tests/src/android/bluetooth/BluetoothLeScannerTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanResultTest.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package android.bluetooth; +package android.bluetooth.le; -import android.bluetooth.BluetoothLeScanner.ScanResult; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; @@ -25,17 +26,18 @@ import junit.framework.TestCase; /** * Unit test cases for Bluetooth LE scans. *

        - * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothLeScannerTest' - * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' */ -public class BluetoothLeScannerTest extends TestCase { +public class ScanResultTest extends TestCase { /** * Test read and write parcel of ScanResult */ @SmallTest public void testScanResultParceling() { - BluetoothDevice device = new BluetoothDevice("01:02:03:04:05:06"); + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + "01:02:03:04:05:06"); byte[] scanRecord = new byte[] { 1, 2, 3 }; int rssi = -10; -- GitLab From e68ccdd0d0df81282bd06904c885c61b5b77ff09 Mon Sep 17 00:00:00 2001 From: Jay Civelli Date: Thu, 8 May 2014 09:24:08 -0700 Subject: [PATCH 0350/1408] Adding a method to know if there is a connection to a BluetoothDevice. Adding a new method to BluetoothDevice that lets callers know whether a remote device is connected. Change-Id: I6f7cb99147f3cdad1d676a93183856dbb4083a06 --- .../android/bluetooth/BluetoothDevice.java | 20 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 21 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7f8d0aba7b0..64d80a066c8 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -873,6 +873,26 @@ public final class BluetoothDevice implements Parcelable { return BOND_NONE; } + /** + * Returns whether there is an open connection to this device. + *

        Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return True if there is at least one open connection to this device. + * @hide + */ + public boolean isConnected() { + if (sService == null) { + // BT is not enabled, we cannot be connected. + return false; + } + try { + return sService.isConnected(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Get the Bluetooth class of the remote device. *

        Requires {@link android.Manifest.permission#BLUETOOTH}. diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 07db8cc9c18..a45c6b8d55b 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -58,6 +58,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); int getRemoteType(in BluetoothDevice device); -- GitLab From f05239d29204bf637c0ec380b3b3d73af01ebfb8 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 5 Jun 2014 16:22:55 -0700 Subject: [PATCH 0351/1408] Take out startScanWithUuidScanParam as part of refactoring Change-Id: Icfb9083571e22c0853fc64ad4d09a20cbf754257 --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 00a07502d3e..273d76dff88 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -35,8 +35,6 @@ interface IBluetoothGatt { void startScan(in int appIf, in boolean isServer); void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); - void startScanWithUuidsScanParam(in int appIf, in boolean isServer, - in ParcelUuid[] ids, int scanWindow, int scanInterval); void startScanWithFilters(in int appIf, in boolean isServer, in ScanSettings settings, in List filters); void stopScan(in int appIf, in boolean isServer); -- GitLab From 01e46dbe84320f2a10329a18f693620a4205686b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 11 Jun 2014 21:04:15 -0700 Subject: [PATCH 0352/1408] LE: Add oneway attribute to Gatt callbacks Adding the 'oneway' attribute to the GattService interface definition prevents applications from blocking callbacks. Bug: 15489651 Change-Id: Id7fcf7f95539092f03e5773ca318b5472b55cb4d --- .../java/android/bluetooth/IBluetoothGattCallback.aidl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index bf9e0a70180..2d8eed467fe 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -22,7 +22,7 @@ import android.os.ParcelUuid; * Callback definitions for interacting with BLE / GATT * @hide */ -interface IBluetoothGattCallback { +oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); @@ -63,7 +63,7 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - oneway void onAdvertiseStateChange(in int advertiseState, in int status); - oneway void onMultiAdvertiseCallback(in int status); + void onAdvertiseStateChange(in int advertiseState, in int status); + void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } -- GitLab From 67a995ada9ae181651c436130a56d46699a120c1 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Mon, 19 Aug 2013 19:03:51 +0530 Subject: [PATCH 0353/1408] Bluetooth: Add support for HFP Client role. Implementation changes in frameworks to support HFP Client role. Change-Id: Ifb10527cd6c1301297cae4f923b20734af672034 --- .../android/bluetooth/BluetoothAdapter.java | 9 +- .../bluetooth/BluetoothHandsfreeClient.java | 1162 +++++++++++++++++ .../BluetoothHandsfreeClientCall.aidl | 18 + .../BluetoothHandsfreeClientCall.java | 193 +++ .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/IBluetoothHandsfreeClient.aidl | 67 + 6 files changed, 1455 insertions(+), 2 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothHandsfreeClient.java create mode 100644 framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl create mode 100644 framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java create mode 100644 framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 42c2aebead5..8b8629ea754 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2009-2014 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. @@ -1402,6 +1402,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; + } else if (profile == BluetoothProfile.HANDSFREE_CLIENT) { + BluetoothHandsfreeClient hfpclient = new BluetoothHandsfreeClient(context, listener); + return true; } else { return false; } @@ -1454,6 +1457,10 @@ public final class BluetoothAdapter { BluetoothMap map = (BluetoothMap)proxy; map.close(); break; + case BluetoothProfile.HANDSFREE_CLIENT: + BluetoothHandsfreeClient hfpclient = (BluetoothHandsfreeClient)proxy; + hfpclient.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClient.java b/framework/java/android/bluetooth/BluetoothHandsfreeClient.java new file mode 100644 index 00000000000..8ea341ee4b5 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHandsfreeClient.java @@ -0,0 +1,1162 @@ +/* + * Copyright (C) 2014 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Public API to control Hands Free Profile (HFP role only). + *

        + * This class defines methods that shall be used by application to manage profile + * connection, calls states and calls actions. + *

        + * + * @hide + * */ +public final class BluetoothHandsfreeClient implements BluetoothProfile { + private static final String TAG = "BluetoothHandsfreeClient"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Intent sent whenever connection to remote changes. + * + *

        It includes two extras: + * BluetoothProfile.EXTRA_PREVIOUS_STATE + * and BluetoothProfile.EXTRA_STATE, which + * are mandatory. + *

        There are also non mandatory feature extras: + * {@link #EXTRA_AG_FEATURE_3WAY_CALLING}, + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}, + * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}, + * {@link #EXTRA_AG_FEATURE_REJECT_CALL}, + * {@link #EXTRA_AG_FEATURE_ECC}, + * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD}, + * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL}, + * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL}, + * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT}, + * {@link #EXTRA_AG_FEATURE_MERGE}, + * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}, + * sent as boolean values only when EXTRA_STATE + * is set to STATE_CONNECTED.

        + * + *

        Note that features supported by AG are being sent as + * booleans with value true, + * and not supported ones are not being sent at all.

        + */ + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.handsfreeclient.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent sent whenever audio state changes. + * + *

        It includes two mandatory extras: + * {@link BluetoothProfile.EXTRA_STATE}, + * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE}, + * with possible values: + * {@link #STATE_AUDIO_CONNECTING}, + * {@link #STATE_AUDIO_CONNECTED}, + * {@link #STATE_AUDIO_DISCONNECTED}

        + *

        When EXTRA_STATE is set + * to STATE_AUDIO_CONNECTED, + * it also includes {@link #EXTRA_AUDIO_WBS} + * indicating wide band speech support.

        + */ + public static final String ACTION_AUDIO_STATE_CHANGED = + "android.bluetooth.handsfreeclient.profile.action.AUDIO_STATE_CHANGED"; + + /** + * Intent sending updates of the Audio Gateway state. + * Each extra is being sent only when value it + * represents has been changed recently on AG. + *

        It can contain one or more of the following extras: + * {@link #EXTRA_NETWORK_STATUS}, + * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH}, + * {@link #EXTRA_NETWORK_ROAMING}, + * {@link #EXTRA_BATTERY_LEVEL}, + * {@link #EXTRA_OPERATOR_NAME}, + * {@link #EXTRA_VOICE_RECOGNITION}, + * {@link #EXTRA_IN_BAND_RING}

        + */ + public static final String ACTION_AG_EVENT = + "android.bluetooth.handsfreeclient.profile.action.AG_EVENT"; + + /** + * Intent sent whenever state of a call changes. + * + *

        It includes: + * {@link #EXTRA_CALL}, + * with value of {@link BluetoothHandsfreeClientCall} instance, + * representing actual call state.

        + */ + public static final String ACTION_CALL_CHANGED = + "android.bluetooth.handsfreeclient.profile.action.AG_CALL_CHANGED"; + + /** + * Intent that notifies about the result of the last issued action. + * Please note that not every action results in explicit action result code being sent. + * Instead other notifications about new Audio Gateway state might be sent, + * like ACTION_AG_EVENT with EXTRA_VOICE_RECOGNITION value + * when for example user started voice recognition from HF unit. + */ + public static final String ACTION_RESULT = + "android.bluetooth.handsfreeclient.profile.action.RESULT"; + + /** + * Intent that notifies about the number attached to the last voice tag + * recorded on AG. + * + *

        It contains: + * {@link #EXTRA_NUMBER}, + * with a String value representing phone number.

        + */ + public static final String ACTION_LAST_VTAG = + "android.bluetooth.handsfreeclient.profile.action.LAST_VTAG"; + + public static final int STATE_AUDIO_DISCONNECTED = 0; + public static final int STATE_AUDIO_CONNECTING = 1; + public static final int STATE_AUDIO_CONNECTED = 2; + + /** + * Extra with information if connected audio is WBS. + *

        Possible values: true, + * false.

        + */ + public static final String EXTRA_AUDIO_WBS = + "android.bluetooth.handsfreeclient.extra.AUDIO_WBS"; + + /** + * Extra for AG_EVENT indicates network status. + *

        Value: 0 - network unavailable, + * 1 - network available

        + */ + public static final String EXTRA_NETWORK_STATUS = + "android.bluetooth.handsfreeclient.extra.NETWORK_STATUS"; + /** + * Extra for AG_EVENT intent indicates network signal strength. + *

        Value: Integer representing signal strength.

        + */ + public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = + "android.bluetooth.handsfreeclient.extra.NETWORK_SIGNAL_STRENGTH"; + /** + * Extra for AG_EVENT intent indicates roaming state. + *

        Value: 0 - no roaming + * 1 - active roaming

        + */ + public static final String EXTRA_NETWORK_ROAMING = + "android.bluetooth.handsfreeclient.extra.NETWORK_ROAMING"; + /** + * Extra for AG_EVENT intent indicates the battery level. + *

        Value: Integer representing signal strength.

        + */ + public static final String EXTRA_BATTERY_LEVEL = + "android.bluetooth.handsfreeclient.extra.BATTERY_LEVEL"; + /** + * Extra for AG_EVENT intent indicates operator name. + *

        Value: String representing operator name.

        + */ + public static final String EXTRA_OPERATOR_NAME = + "android.bluetooth.handsfreeclient.extra.OPERATOR_NAME"; + /** + * Extra for AG_EVENT intent indicates voice recognition state. + *

        Value: + * 0 - voice recognition stopped, + * 1 - voice recognition started.

        + */ + public static final String EXTRA_VOICE_RECOGNITION = + "android.bluetooth.handsfreeclient.extra.VOICE_RECOGNITION"; + /** + * Extra for AG_EVENT intent indicates in band ring state. + *

        Value: + * 0 - in band ring tone not supported, or + * 1 - in band ring tone supported.

        + */ + public static final String EXTRA_IN_BAND_RING = + "android.bluetooth.handsfreeclient.extra.IN_BAND_RING"; + + /** + * Extra for AG_EVENT intent indicates subscriber info. + *

        Value: String containing subscriber information.

        + */ + public static final String EXTRA_SUBSCRIBER_INFO = + "android.bluetooth.handsfreeclient.extra.SUBSCRIBER_INFO"; + + /** + * Extra for AG_CALL_CHANGED intent indicates the + * {@link BluetoothHandsfreeClientCall} object that has changed. + */ + public static final String EXTRA_CALL = + "android.bluetooth.handsfreeclient.extra.CALL"; + + /** + * Extra for ACTION_LAST_VTAG intent. + *

        Value: String representing phone number + * corresponding to last voice tag recorded on AG

        + */ + public static final String EXTRA_NUMBER = + "android.bluetooth.handsfreeclient.extra.NUMBER"; + + /** + * Extra for ACTION_RESULT intent that shows the result code of + * last issued action. + *

        Possible results: + * {@link #ACTION_RESULT_OK}, + * {@link #ACTION_RESULT_ERROR}, + * {@link #ACTION_RESULT_ERROR_NO_CARRIER}, + * {@link #ACTION_RESULT_ERROR_BUSY}, + * {@link #ACTION_RESULT_ERROR_NO_ANSWER}, + * {@link #ACTION_RESULT_ERROR_DELAYED}, + * {@link #ACTION_RESULT_ERROR_BLACKLISTED}, + * {@link #ACTION_RESULT_ERROR_CME}

        + */ + public static final String EXTRA_RESULT_CODE = + "android.bluetooth.handsfreeclient.extra.RESULT_CODE"; + + /** + * Extra for ACTION_RESULT intent that shows the extended result code of + * last issued action. + *

        Value: Integer - error code.

        + */ + public static final String EXTRA_CME_CODE = + "android.bluetooth.handsfreeclient.extra.CME_CODE"; + + /* Extras for AG_FEATURES, extras type is boolean */ + // TODO verify if all of those are actually useful + /** + * AG feature: three way calling. + */ + public final static String EXTRA_AG_FEATURE_3WAY_CALLING = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; + /** + * AG feature: voice recognition. + */ + public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; + /** + * AG feature: fetching phone number for voice tagging procedure. + */ + public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; + /** + * AG feature: ability to reject incoming call. + */ + public final static String EXTRA_AG_FEATURE_REJECT_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; + /** + * AG feature: enhanced call handling (terminate specific call, private consultation). + */ + public final static String EXTRA_AG_FEATURE_ECC = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ECC"; + /** + * AG feature: response and hold. + */ + public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; + /** + * AG call handling feature: accept held or waiting call in three way calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; + /** + * AG call handling feature: release held or waiting call in three way calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; + /** + * AG call handling feature: release active call and accept held or waiting call in three way + * calling scenarios. + */ + public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; + /** + * AG call handling feature: merge two calls, held and active - multi party conference mode. + */ + public final static String EXTRA_AG_FEATURE_MERGE = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE"; + /** + * AG call handling feature: merge calls and disconnect from multi party + * conversation leaving peers connected to each other. + * Note that this feature needs to be supported by mobile network operator + * as it requires connection and billing transfer. + */ + public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH = + "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; + + /* Action result codes */ + public final static int ACTION_RESULT_OK = 0; + public final static int ACTION_RESULT_ERROR = 1; + public final static int ACTION_RESULT_ERROR_NO_CARRIER = 2; + public final static int ACTION_RESULT_ERROR_BUSY = 3; + public final static int ACTION_RESULT_ERROR_NO_ANSWER = 4; + public final static int ACTION_RESULT_ERROR_DELAYED = 5; + public final static int ACTION_RESULT_ERROR_BLACKLISTED = 6; + public final static int ACTION_RESULT_ERROR_CME = 7; + + /* Detailed CME error codes */ + public final static int CME_PHONE_FAILURE = 0; + public final static int CME_NO_CONNECTION_TO_PHONE = 1; + public final static int CME_OPERATION_NOT_ALLOWED = 3; + public final static int CME_OPERATION_NOT_SUPPORTED = 4; + public final static int CME_PHSIM_PIN_REQUIRED = 5; + public final static int CME_PHFSIM_PIN_REQUIRED = 6; + public final static int CME_PHFSIM_PUK_REQUIRED = 7; + public final static int CME_SIM_NOT_INSERTED = 10; + public final static int CME_SIM_PIN_REQUIRED = 11; + public final static int CME_SIM_PUK_REQUIRED = 12; + public final static int CME_SIM_FAILURE = 13; + public final static int CME_SIM_BUSY = 14; + public final static int CME_SIM_WRONG = 15; + public final static int CME_INCORRECT_PASSWORD = 16; + public final static int CME_SIM_PIN2_REQUIRED = 17; + public final static int CME_SIM_PUK2_REQUIRED = 18; + public final static int CME_MEMORY_FULL = 20; + public final static int CME_INVALID_INDEX = 21; + public final static int CME_NOT_FOUND = 22; + public final static int CME_MEMORY_FAILURE = 23; + public final static int CME_TEXT_STRING_TOO_LONG = 24; + public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; + public final static int CME_DIAL_STRING_TOO_LONG = 26; + public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; + public final static int CME_NO_NETWORK_SERVICE = 30; + public final static int CME_NETWORK_TIMEOUT = 31; + public final static int CME_EMERGENCY_SERVICE_ONLY = 32; + public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; + public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34; + public final static int CME_SIP_RESPONSE_CODE = 35; + public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; + public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; + public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; + public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; + public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; + public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; + public final static int CME_HIDDEN_KEY_REQUIRED = 48; + public final static int CME_EAP_NOT_SUPPORTED = 49; + public final static int CME_INCORRECT_PARAMETERS = 50; + + /* Action policy for other calls when accepting call */ + public static final int CALL_ACCEPT_NONE = 0; + public static final int CALL_ACCEPT_HOLD = 1; + public static final int CALL_ACCEPT_TERMINATE = 2; + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetoothHandsfreeClient mService; + private BluetoothAdapter mAdapter; + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + @Override + 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..."); + if (!mContext.bindService( + new Intent(IBluetoothHandsfreeClient.class.getName()), + mConnection, 0)) { + Log.e(TAG, + "Could not bind to Bluetooth Handsfree Client Service"); + } + } + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + }; + + /** + * Create a BluetoothHandsfreeClient proxy object. + */ + /*package*/ BluetoothHandsfreeClient(Context context, ServiceListener l) { + 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); + } + } + + if (!context.bindService( + new Intent(IBluetoothHandsfreeClient.class.getName()), mConnection, 0)) { + Log.e(TAG, "Could not bind to Bluetooth Handsfree Client Service"); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothHandsfreeClient will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + /*package*/ void close() { + if (VDBG) log("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; + } + + /** + * Connects to remote device. + * + * Currently, the system supports only 1 connection. So, in case of the + * second connection, this implementation will disconnect already connected + * device automatically and will process the new one. + * + * @param device a remote device we want connect to + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} + * intent. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connect(device); + } 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; + } + + /** + * Disconnects remote device + * + * @param device a remote device we want disconnect + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} + * intent. + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } 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; + } + + /** + * Return the list of connected remote devices + * + * @return list of connected devices; empty list if nothing is connected. + */ + @Override + public List getConnectedDevices() { + if (VDBG) log("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(); + } + + /** + * Returns list of remote devices in a particular state + * + * @param states collection of states + * @return list of devices that state matches the states listed in + * states; empty list if nothing matches the + * states + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("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(); + } + + /** + * Returns state of the device + * + * @param device a remote device + * @return the state of connection of the device + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("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. + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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. + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("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; + } + + /** + * Starts voice recognition. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AG_EVENT} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean startVoiceRecognition(BluetoothDevice device) { + if (DBG) log("startVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.startVoiceRecognition(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Stops voice recognition. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AG_EVENT} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean stopVoiceRecognition(BluetoothDevice device) { + if (DBG) log("stopVoiceRecognition()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.stopVoiceRecognition(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Returns list of all calls in any state. + * + * @param device remote device + * @return list of calls; empty list if none call exists + */ + public List getCurrentCalls(BluetoothDevice device) { + if (DBG) log("getCurrentCalls()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getCurrentCalls(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + /** + * Returns list of current values of AG indicators. + * + * @param device remote device + * @return bundle of AG indicators; null if device is not in + * CONNECTED state + */ + public Bundle getCurrentAgEvents(BluetoothDevice device) { + if (DBG) log("getCurrentCalls()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getCurrentAgEvents(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + /** + * Accepts a call + * + * @param device remote device + * @param flag action policy while accepting a call. Possible values + * {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD}, + * {@link #CALL_ACCEPT_TERMINATE} + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + */ + public boolean acceptCall(BluetoothDevice device, int flag) { + if (DBG) log("acceptCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.acceptCall(device, flag); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Holds a call. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + */ + public boolean holdCall(BluetoothDevice device) { + if (DBG) log("holdCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.holdCall(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Rejects a call. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_REJECT_CALL}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean rejectCall(BluetoothDevice device) { + if (DBG) log("rejectCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.rejectCall(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Terminates a specified call. + * + * Works only when Extended Call Control is supported by Audio Gateway. + * + * @param device remote device + * @param index index of the call to be terminated + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ECC}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean terminateCall(BluetoothDevice device, int index) { + if (DBG) log("terminateCall()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.terminateCall(device, index); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Enters private mode with a specified call. + * + * Works only when Extended Call Control is supported by Audio Gateway. + * + * @param device remote device + * @param index index of the call to connect in private mode + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ECC}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean enterPrivateMode(BluetoothDevice device, int index) { + if (DBG) log("enterPrivateMode()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.enterPrivateMode(device, index); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Performs explicit call transfer. + * + * That means connect other calls and disconnect. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent. + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean explicitCallTransfer(BluetoothDevice device) { + if (DBG) log("explicitCallTransfer()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.explicitCallTransfer(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Redials last number from Audio Gateway. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean redial(BluetoothDevice device) { + if (DBG) log("redial()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.redial(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Places a call with specified number. + * + * @param device remote device + * @param number valid phone number + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean dial(BluetoothDevice device, String number) { + if (DBG) log("dial()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.dial(device, number); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Places a call to the number under specified memory location. + * + * @param device remote device + * @param location valid memory location + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; + */ + public boolean dialMemory(BluetoothDevice device, int location) { + if (DBG) log("dialMemory()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.dialMemory(device, location); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Sends DTMF code. + * + * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# + * + * @param device remote device + * @param code ASCII code + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_RESULT} intent; + */ + public boolean sendDTMF(BluetoothDevice device, byte code) { + if (DBG) log("sendDTMF()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.sendDTMF(device, code); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get a number corresponding to last voice tag recorded on AG. + * + * @param device remote device + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_LAST_VTAG} + * or {@link #ACTION_RESULT} intent; + * + *

        Feature required for successful execution is being reported by: + * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. + * This method invocation will fail silently when feature is not supported.

        + */ + public boolean getLastVoiceTagNumber(BluetoothDevice device) { + if (DBG) log("getLastVoiceTagNumber()"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getLastVoiceTagNumber(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Accept the incoming connection. + */ + public boolean acceptIncomingConnect(BluetoothDevice device) { + if (DBG) log("acceptIncomingConnect"); + if (mService != null && isEnabled()) { + try { + return mService.acceptIncomingConnect(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; + } + + /** + * Reject the incoming connection. + */ + public boolean rejectIncomingConnect(BluetoothDevice device) { + if (DBG) log("rejectIncomingConnect"); + if (mService != null) { + try { + return mService.rejectIncomingConnect(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; + } + + /** + * Returns current audio state of Audio Gateway. + * + * Note: This is an internal function and shouldn't be exposed + */ + public int getAudioState(BluetoothDevice device) { + if (VDBG) log("getAudioState"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioState(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 BluetoothHandsfreeClient.STATE_AUDIO_DISCONNECTED; + } + + /** + * Initiates a connection of audio channel. + * + * It setup SCO channel with remote connected Handsfree AG device. + * + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} + * intent; + */ + public boolean connectAudio() { + if (mService != null && isEnabled()) { + try { + return mService.connectAudio(); + } 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; + } + + /** + * Disconnects audio channel. + * + * It tears down the SCO channel from remote AG device. + * + * @return true if command has been issued successfully; + * false otherwise; + * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} + * intent; + */ + public boolean disconnectAudio() { + if (mService != null && isEnabled()) { + try { + return mService.disconnectAudio(); + } 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; + } + + /** + * Get Audio Gateway features + * + * @param device remote device + * @return bundle of AG features; null if no service or + * AG not connected + */ + public Bundle getCurrentAgFeatures(BluetoothDevice device) { + if (mService != null && isEnabled()) { + try { + return mService.getCurrentAgFeatures(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 null; + } + + + private ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothHandsfreeClient.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HANDSFREE_CLIENT, + BluetoothHandsfreeClient.this); + } + } + @Override + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HANDSFREE_CLIENT); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl b/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl new file mode 100644 index 00000000000..00200d3a13d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothHandsfreeClientCall; diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java b/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java new file mode 100644 index 00000000000..68fc63839b2 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2014 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.os.Parcel; +import android.os.Parcelable; + +/** + * This class represents a single call, its state and properties. + * It implements {@link Parcelable} for inter-process message passing. + * @hide + */ +public final class BluetoothHandsfreeClientCall implements Parcelable { + + /* Call state */ + /** + * Call is active. + */ + public static final int CALL_STATE_ACTIVE = 0; + /** + * Call is in held state. + */ + public static final int CALL_STATE_HELD = 1; + /** + * Outgoing call that is being dialed right now. + */ + public static final int CALL_STATE_DIALING = 2; + /** + * Outgoing call that remote party has already been alerted about. + */ + public static final int CALL_STATE_ALERTING = 3; + /** + * Incoming call that can be accepted or rejected. + */ + public static final int CALL_STATE_INCOMING = 4; + /** + * Waiting call state when there is already an active call. + */ + public static final int CALL_STATE_WAITING = 5; + /** + * Call that has been held by response and hold + * (see Bluetooth specification for further references). + */ + public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6; + /** + * Call that has been already terminated and should not be referenced as a valid call. + */ + public static final int CALL_STATE_TERMINATED = 7; + + private final int mId; + private int mState; + private String mNumber; + private boolean mMultiParty; + private final boolean mOutgoing; + + /** + * Creates BluetoothHandsfreeClientCall instance. + */ + public BluetoothHandsfreeClientCall(int id, int state, String number, boolean multiParty, + boolean outgoing) { + mId = id; + mState = state; + mNumber = number != null ? number : ""; + mMultiParty = multiParty; + mOutgoing = outgoing; + } + + /** + * Sets call's state. + * + *

        Note: This is an internal function and shouldn't be exposed

        + * + * @param state new call state. + */ + public void setState(int state) { + mState = state; + } + + /** + * Sets call's number. + * + *

        Note: This is an internal function and shouldn't be exposed

        + * + * @param number String representing phone number. + */ + public void setNumber(String number) { + mNumber = number; + } + + /** + * Sets this call as multi party call. + * + *

        Note: This is an internal function and shouldn't be exposed

        + * + * @param multiParty if true sets this call as a part + * of multi party conference. + */ + public void setMultiParty(boolean multiParty) { + mMultiParty = multiParty; + } + + /** + * Gets call's Id. + * + * @return call id. + */ + public int getId() { + return mId; + } + + /** + * Gets call's current state. + * + * @return state of this particular phone call. + */ + public int getState() { + return mState; + } + + /** + * Gets call's number. + * + * @return string representing phone number. + */ + public String getNumber() { + return mNumber; + } + + /** + * Checks if call is an active call in a conference mode (aka multi party). + * + * @return true if call is a multi party call, + * false otherwise. + */ + public boolean isMultiParty() { + return mMultiParty; + } + + /** + * Checks if this call is an outgoing call. + * + * @return true if its outgoing call, + * false otherwise. + */ + public boolean isOutgoing() { + return mOutgoing; + } + + /** + * {@link Parcelable.Creator} interface implementation. + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public BluetoothHandsfreeClientCall createFromParcel(Parcel in) { + return new BluetoothHandsfreeClientCall(in.readInt(), in.readInt(), + in.readString(), in.readInt() == 1, in.readInt() == 1); + } + + @Override + public BluetoothHandsfreeClientCall[] newArray(int size) { + return new BluetoothHandsfreeClientCall[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mId); + out.writeInt(mState); + out.writeString(mNumber); + out.writeInt(mMultiParty ? 1 : 0); + out.writeInt(mOutgoing ? 1 : 0); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index d8980602855..41dabb33ac3 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2010-2014 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. @@ -109,6 +109,12 @@ public interface BluetoothProfile { */ public static final int A2DP_SINK = 10; + /** + * Handsfree Client - HFP HF Role + * @hide + */ + public static final int HANDSFREE_CLIENT = 16; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl b/framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl new file mode 100644 index 00000000000..d7ddebe05ea --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHandsfreeClientCall; +import android.os.Bundle; + +/** + * API for Bluetooth Handsfree Client service (HFP HF Role) + * + * {@hide} + */ +interface IBluetoothHandsfreeClient { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + + boolean acceptIncomingConnect(in BluetoothDevice device); + boolean rejectIncomingConnect(in BluetoothDevice device); + + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + + boolean startVoiceRecognition(in BluetoothDevice device); + boolean stopVoiceRecognition(in BluetoothDevice device); + + List getCurrentCalls(in BluetoothDevice device); + Bundle getCurrentAgEvents(in BluetoothDevice device); + + boolean acceptCall(in BluetoothDevice device, int flag); + boolean holdCall(in BluetoothDevice device); + boolean rejectCall(in BluetoothDevice device); + boolean terminateCall(in BluetoothDevice device, int index); + + boolean enterPrivateMode(in BluetoothDevice device, int index); + boolean explicitCallTransfer(in BluetoothDevice device); + + boolean redial(in BluetoothDevice device); + boolean dial(in BluetoothDevice device, String number); + boolean dialMemory(in BluetoothDevice device, int location); + + boolean sendDTMF(in BluetoothDevice device, byte code); + boolean getLastVoiceTagNumber(in BluetoothDevice device); + + int getAudioState(in BluetoothDevice device); + boolean connectAudio(); + boolean disconnectAudio(); + + Bundle getCurrentAgFeatures(in BluetoothDevice device); +} -- GitLab From f48a72776380b9691c85aba73012bd3d69cebe01 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 12 Jun 2014 11:23:40 -0700 Subject: [PATCH 0354/1408] Rename BluetoothHandsfreeClient to BluetoothHeadsetClient This makes our terminology consistent with the existing BluetoothHeadset profile Also updated arguments to Context.bindService() Change-Id: I27cc5a6fde256b1f5dccca53a7a15ec8f58691c2 --- .../android/bluetooth/BluetoothAdapter.java | 10 +- ...lient.java => BluetoothHeadsetClient.java} | 111 +++++++++--------- ...l.aidl => BluetoothHeadsetClientCall.aidl} | 2 +- ...l.java => BluetoothHeadsetClientCall.java} | 18 +-- .../android/bluetooth/BluetoothProfile.java | 4 +- ...ient.aidl => IBluetoothHeadsetClient.aidl} | 8 +- 6 files changed, 79 insertions(+), 74 deletions(-) rename framework/java/android/bluetooth/{BluetoothHandsfreeClient.java => BluetoothHeadsetClient.java} (91%) rename framework/java/android/bluetooth/{BluetoothHandsfreeClientCall.aidl => BluetoothHeadsetClientCall.aidl} (94%) rename framework/java/android/bluetooth/{BluetoothHandsfreeClientCall.java => BluetoothHeadsetClientCall.java} (87%) rename framework/java/android/bluetooth/{IBluetoothHandsfreeClient.aidl => IBluetoothHeadsetClient.aidl} (90%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8b8629ea754..6daa61d79a8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1402,8 +1402,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; - } else if (profile == BluetoothProfile.HANDSFREE_CLIENT) { - BluetoothHandsfreeClient hfpclient = new BluetoothHandsfreeClient(context, listener); + } else if (profile == BluetoothProfile.HEADSET_CLIENT) { + BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); return true; } else { return false; @@ -1457,9 +1457,9 @@ public final class BluetoothAdapter { BluetoothMap map = (BluetoothMap)proxy; map.close(); break; - case BluetoothProfile.HANDSFREE_CLIENT: - BluetoothHandsfreeClient hfpclient = (BluetoothHandsfreeClient)proxy; - hfpclient.close(); + case BluetoothProfile.HEADSET_CLIENT: + BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy; + headsetClient.close(); break; } } diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java similarity index 91% rename from framework/java/android/bluetooth/BluetoothHandsfreeClient.java rename to framework/java/android/bluetooth/BluetoothHeadsetClient.java index 8ea341ee4b5..ff4ebee221e 100644 --- a/framework/java/android/bluetooth/BluetoothHandsfreeClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -37,8 +37,8 @@ import java.util.List; * * @hide * */ -public final class BluetoothHandsfreeClient implements BluetoothProfile { - private static final String TAG = "BluetoothHandsfreeClient"; +public final class BluetoothHeadsetClient implements BluetoothProfile { + private static final String TAG = "BluetoothHeadsetClient"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -69,7 +69,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * and not supported ones are not being sent at all.

        */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.handsfreeclient.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent sent whenever audio state changes. @@ -87,7 +87,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * indicating wide band speech support.

        */ public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.handsfreeclient.profile.action.AUDIO_STATE_CHANGED"; + "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; /** * Intent sending updates of the Audio Gateway state. @@ -103,18 +103,18 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * {@link #EXTRA_IN_BAND_RING}

        */ public static final String ACTION_AG_EVENT = - "android.bluetooth.handsfreeclient.profile.action.AG_EVENT"; + "android.bluetooth.headsetclient.profile.action.AG_EVENT"; /** * Intent sent whenever state of a call changes. * *

        It includes: * {@link #EXTRA_CALL}, - * with value of {@link BluetoothHandsfreeClientCall} instance, + * with value of {@link BluetoothHeadsetClientCall} instance, * representing actual call state.

        */ public static final String ACTION_CALL_CHANGED = - "android.bluetooth.handsfreeclient.profile.action.AG_CALL_CHANGED"; + "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; /** * Intent that notifies about the result of the last issued action. @@ -124,7 +124,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * when for example user started voice recognition from HF unit. */ public static final String ACTION_RESULT = - "android.bluetooth.handsfreeclient.profile.action.RESULT"; + "android.bluetooth.headsetclient.profile.action.RESULT"; /** * Intent that notifies about the number attached to the last voice tag @@ -135,7 +135,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * with a String value representing phone number.

        */ public static final String ACTION_LAST_VTAG = - "android.bluetooth.handsfreeclient.profile.action.LAST_VTAG"; + "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; public static final int STATE_AUDIO_DISCONNECTED = 0; public static final int STATE_AUDIO_CONNECTING = 1; @@ -147,7 +147,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * false.

        */ public static final String EXTRA_AUDIO_WBS = - "android.bluetooth.handsfreeclient.extra.AUDIO_WBS"; + "android.bluetooth.headsetclient.extra.AUDIO_WBS"; /** * Extra for AG_EVENT indicates network status. @@ -155,32 +155,32 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * 1 - network available

        */ public static final String EXTRA_NETWORK_STATUS = - "android.bluetooth.handsfreeclient.extra.NETWORK_STATUS"; + "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; /** * Extra for AG_EVENT intent indicates network signal strength. *

        Value: Integer representing signal strength.

        */ public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = - "android.bluetooth.handsfreeclient.extra.NETWORK_SIGNAL_STRENGTH"; + "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH"; /** * Extra for AG_EVENT intent indicates roaming state. *

        Value: 0 - no roaming * 1 - active roaming

        */ public static final String EXTRA_NETWORK_ROAMING = - "android.bluetooth.handsfreeclient.extra.NETWORK_ROAMING"; + "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; /** * Extra for AG_EVENT intent indicates the battery level. *

        Value: Integer representing signal strength.

        */ public static final String EXTRA_BATTERY_LEVEL = - "android.bluetooth.handsfreeclient.extra.BATTERY_LEVEL"; + "android.bluetooth.headsetclient.extra.BATTERY_LEVEL"; /** * Extra for AG_EVENT intent indicates operator name. *

        Value: String representing operator name.

        */ public static final String EXTRA_OPERATOR_NAME = - "android.bluetooth.handsfreeclient.extra.OPERATOR_NAME"; + "android.bluetooth.headsetclient.extra.OPERATOR_NAME"; /** * Extra for AG_EVENT intent indicates voice recognition state. *

        Value: @@ -188,7 +188,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * 1 - voice recognition started.

        */ public static final String EXTRA_VOICE_RECOGNITION = - "android.bluetooth.handsfreeclient.extra.VOICE_RECOGNITION"; + "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; /** * Extra for AG_EVENT intent indicates in band ring state. *

        Value: @@ -196,21 +196,21 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * 1 - in band ring tone supported.

        */ public static final String EXTRA_IN_BAND_RING = - "android.bluetooth.handsfreeclient.extra.IN_BAND_RING"; + "android.bluetooth.headsetclient.extra.IN_BAND_RING"; /** * Extra for AG_EVENT intent indicates subscriber info. *

        Value: String containing subscriber information.

        */ public static final String EXTRA_SUBSCRIBER_INFO = - "android.bluetooth.handsfreeclient.extra.SUBSCRIBER_INFO"; + "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; /** * Extra for AG_CALL_CHANGED intent indicates the - * {@link BluetoothHandsfreeClientCall} object that has changed. + * {@link BluetoothHeadsetClientCall} object that has changed. */ public static final String EXTRA_CALL = - "android.bluetooth.handsfreeclient.extra.CALL"; + "android.bluetooth.headsetclient.extra.CALL"; /** * Extra for ACTION_LAST_VTAG intent. @@ -218,7 +218,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * corresponding to last voice tag recorded on AG

        */ public static final String EXTRA_NUMBER = - "android.bluetooth.handsfreeclient.extra.NUMBER"; + "android.bluetooth.headsetclient.extra.NUMBER"; /** * Extra for ACTION_RESULT intent that shows the result code of @@ -234,7 +234,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * {@link #ACTION_RESULT_ERROR_CME}

        */ public static final String EXTRA_RESULT_CODE = - "android.bluetooth.handsfreeclient.extra.RESULT_CODE"; + "android.bluetooth.headsetclient.extra.RESULT_CODE"; /** * Extra for ACTION_RESULT intent that shows the extended result code of @@ -242,7 +242,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { *

        Value: Integer - error code.

        */ public static final String EXTRA_CME_CODE = - "android.bluetooth.handsfreeclient.extra.CME_CODE"; + "android.bluetooth.headsetclient.extra.CME_CODE"; /* Extras for AG_FEATURES, extras type is boolean */ // TODO verify if all of those are actually useful @@ -250,53 +250,53 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * AG feature: three way calling. */ public final static String EXTRA_AG_FEATURE_3WAY_CALLING = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; /** * AG feature: voice recognition. */ public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; /** * AG feature: fetching phone number for voice tagging procedure. */ public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; /** * AG feature: ability to reject incoming call. */ public final static String EXTRA_AG_FEATURE_REJECT_CALL = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; /** * AG feature: enhanced call handling (terminate specific call, private consultation). */ public final static String EXTRA_AG_FEATURE_ECC = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ECC"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; /** * AG feature: response and hold. */ public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; /** * AG call handling feature: accept held or waiting call in three way calling scenarios. */ public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; /** * AG call handling feature: release held or waiting call in three way calling scenarios. */ public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; /** * AG call handling feature: release active call and accept held or waiting call in three way * calling scenarios. */ public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; /** * AG call handling feature: merge two calls, held and active - multi party conference mode. */ public final static String EXTRA_AG_FEATURE_MERGE = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; /** * AG call handling feature: merge calls and disconnect from multi party * conversation leaving peers connected to each other. @@ -304,7 +304,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * as it requires connection and billing transfer. */ public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH = - "android.bluetooth.handsfreeclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; + "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; /* Action result codes */ public final static int ACTION_RESULT_OK = 0; @@ -366,7 +366,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHandsfreeClient mService; + private IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -389,12 +389,8 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { try { if (mService == null) { if (VDBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService( - new Intent(IBluetoothHandsfreeClient.class.getName()), - mConnection, 0)) { - Log.e(TAG, - "Could not bind to Bluetooth Handsfree Client Service"); - } + Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); + doBind(); } } catch (Exception re) { Log.e(TAG,"",re); @@ -405,9 +401,9 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { }; /** - * Create a BluetoothHandsfreeClient proxy object. + * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHandsfreeClient(Context context, ServiceListener l) { + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -421,15 +417,24 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { } } - if (!context.bindService( - new Intent(IBluetoothHandsfreeClient.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Handsfree Client Service"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHeadsetClient.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 Headset Client Service with " + intent); + return false; } + return true; } /** * Close the connection to the backing service. - * Other public functions of BluetoothHandsfreeClient will return default error + * Other public functions of BluetoothHeadsetClient will return default error * results once close() has been called. Multiple invocations of close() * are ok. */ @@ -677,7 +682,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { * @param device remote device * @return list of calls; empty list if none call exists */ - public List getCurrentCalls(BluetoothDevice device) { + public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); if (mService != null && isEnabled() && isValidDevice(device)) { @@ -1050,7 +1055,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return BluetoothHandsfreeClient.STATE_AUDIO_DISCONNECTED; + return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; } /** @@ -1127,11 +1132,11 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHandsfreeClient.Stub.asInterface(service); + mService = IBluetoothHeadsetClient.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HANDSFREE_CLIENT, - BluetoothHandsfreeClient.this); + mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, + BluetoothHeadsetClient.this); } } @Override @@ -1139,7 +1144,7 @@ public final class BluetoothHandsfreeClient implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HANDSFREE_CLIENT); + mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); } } }; diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl similarity index 94% rename from framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl rename to framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl index 00200d3a13d..35f792387ec 100644 --- a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.aidl +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl @@ -15,4 +15,4 @@ */ package android.bluetooth; -parcelable BluetoothHandsfreeClientCall; +parcelable BluetoothHeadsetClientCall; diff --git a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java similarity index 87% rename from framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java rename to framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 68fc63839b2..159e1f79464 100644 --- a/framework/java/android/bluetooth/BluetoothHandsfreeClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -24,7 +24,7 @@ import android.os.Parcelable; * It implements {@link Parcelable} for inter-process message passing. * @hide */ -public final class BluetoothHandsfreeClientCall implements Parcelable { +public final class BluetoothHeadsetClientCall implements Parcelable { /* Call state */ /** @@ -68,9 +68,9 @@ public final class BluetoothHandsfreeClientCall implements Parcelable { private final boolean mOutgoing; /** - * Creates BluetoothHandsfreeClientCall instance. + * Creates BluetoothHeadsetClientCall instance. */ - public BluetoothHandsfreeClientCall(int id, int state, String number, boolean multiParty, + public BluetoothHeadsetClientCall(int id, int state, String number, boolean multiParty, boolean outgoing) { mId = id; mState = state; @@ -163,17 +163,17 @@ public final class BluetoothHandsfreeClientCall implements Parcelable { /** * {@link Parcelable.Creator} interface implementation. */ - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { @Override - public BluetoothHandsfreeClientCall createFromParcel(Parcel in) { - return new BluetoothHandsfreeClientCall(in.readInt(), in.readInt(), + public BluetoothHeadsetClientCall createFromParcel(Parcel in) { + return new BluetoothHeadsetClientCall(in.readInt(), in.readInt(), in.readString(), in.readInt() == 1, in.readInt() == 1); } @Override - public BluetoothHandsfreeClientCall[] newArray(int size) { - return new BluetoothHandsfreeClientCall[size]; + public BluetoothHeadsetClientCall[] newArray(int size) { + return new BluetoothHeadsetClientCall[size]; } }; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 41dabb33ac3..ee95eceadbb 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -110,10 +110,10 @@ public interface BluetoothProfile { public static final int A2DP_SINK = 10; /** - * Handsfree Client - HFP HF Role + * Headset Client - HFP HF Role * @hide */ - public static final int HANDSFREE_CLIENT = 16; + public static final int HEADSET_CLIENT = 16; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl similarity index 90% rename from framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl rename to framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index d7ddebe05ea..e518b7d225f 100644 --- a/framework/java/android/bluetooth/IBluetoothHandsfreeClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -17,15 +17,15 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHandsfreeClientCall; +import android.bluetooth.BluetoothHeadsetClientCall; import android.os.Bundle; /** - * API for Bluetooth Handsfree Client service (HFP HF Role) + * API for Bluetooth Headset Client service (HFP HF Role) * * {@hide} */ -interface IBluetoothHandsfreeClient { +interface IBluetoothHeadsetClient { boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); @@ -41,7 +41,7 @@ interface IBluetoothHandsfreeClient { boolean startVoiceRecognition(in BluetoothDevice device); boolean stopVoiceRecognition(in BluetoothDevice device); - List getCurrentCalls(in BluetoothDevice device); + List getCurrentCalls(in BluetoothDevice device); Bundle getCurrentAgEvents(in BluetoothDevice device); boolean acceptCall(in BluetoothDevice device, int flag); -- GitLab From 1189a3b805f9cfa59e9ff05a6b152bc7015fa9e3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 12 Jun 2014 13:10:07 -0700 Subject: [PATCH 0355/1408] Add BluetoothHeadsetClientCall.toString method Change-Id: I8a95fda4f67daa09dfefedffec804c06af53ebe0 --- .../bluetooth/BluetoothHeadsetClientCall.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 159e1f79464..a15bd975ad8 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -160,6 +160,31 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mOutgoing; } + public String toString() { + StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mId: "); + builder.append(mId); + builder.append(", mState: "); + switch (mState) { + case CALL_STATE_ACTIVE: builder.append("ACTIVE"); break; + case CALL_STATE_HELD: builder.append("HELD"); break; + case CALL_STATE_DIALING: builder.append("DIALING"); break; + case CALL_STATE_ALERTING: builder.append("ALERTING"); break; + case CALL_STATE_INCOMING: builder.append("INCOMING"); break; + case CALL_STATE_WAITING: builder.append("WAITING"); break; + case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: builder.append("HELD_BY_RESPONSE_AND_HOLD"); break; + case CALL_STATE_TERMINATED: builder.append("TERMINATED"); break; + default: builder.append(mState); break; + } + builder.append(", mNumber: "); + builder.append(mNumber); + builder.append(", mMultiParty: "); + builder.append(mMultiParty); + builder.append(", mOutgoing: "); + builder.append(mOutgoing); + builder.append("}"); + return builder.toString(); + } + /** * {@link Parcelable.Creator} interface implementation. */ -- GitLab From f0de508aeaac6686cb132c73e224980fede48727 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 14 May 2014 09:51:30 -0700 Subject: [PATCH 0356/1408] BluetoothA2dpSink: Add new BluetoothProfile subclass for A2DP sink Change-Id: I09d5cb8fdaea4c4828f333949b7c18deffd22722 --- .../android/bluetooth/BluetoothA2dpSink.java | 432 ++++++++++++++++++ .../android/bluetooth/BluetoothAdapter.java | 7 + .../bluetooth/BluetoothAudioConfig.aidl | 19 + .../bluetooth/BluetoothAudioConfig.java | 111 +++++ .../android/bluetooth/IBluetoothA2dpSink.aidl | 34 ++ 5 files changed, 603 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothA2dpSink.java create mode 100644 framework/java/android/bluetooth/BluetoothAudioConfig.aidl create mode 100644 framework/java/android/bluetooth/BluetoothAudioConfig.java create mode 100644 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java new file mode 100644 index 00000000000..2e273459add --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2014 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth A2DP Sink + * profile. + * + *

        BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothA2dpSink proxy object. + * + * @hide + */ +public final class BluetoothA2dpSink implements BluetoothProfile { + private static final String TAG = "BluetoothA2dpSink"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the A2DP Sink + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the change in the Playing state of the A2DP Sink + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + public static final String ACTION_PLAYING_STATE_CHANGED = + "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED"; + + /** + * A2DP sink device is streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_PLAYING = 10; + + /** + * A2DP sink device is NOT streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_NOT_PLAYING = 11; + + /** + * Intent used to broadcast the change in the Playing state of the A2DP Sink + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + public static final String ACTION_AUDIO_CONFIG_CHANGED = + "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED"; + + /** + * Extra for the {@link #ACTION_AUDIO_CONFIG_CHANGED} intent. + * + * This extra represents the current audio configuration of the A2DP source device. + * {@see BluetoothAudioConfig} + */ + public static final String EXTRA_AUDIO_CONFIG + = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetoothA2dpSink mService; + private BluetoothAdapter mAdapter; + + 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 BluetoothA2dp proxy object for interacting with the local + * Bluetooth A2DP service. + * + */ + /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { + 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(IBluetoothA2dpSink.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 A2DP Service with " + intent); + return false; + } + return true; + } + + /*package*/ void close() { + mServiceListener = null; + 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); + } + } + } + } + + public void finalize() { + close(); + } + /** + * Initiate connection to a profile of the remote bluetooth device. + * + *

        Currently, the system supports only 1 connection to the + * A2DP profile. The API will automatically disconnect connected + * devices before connecting. + * + *

        This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Initiate disconnection from a profile + * + *

        This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

        If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * {@inheritDoc} + */ + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Get the current audio configuration for the A2DP source device, + * or null if the device has no audio configuration + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @return audio configuration for the device, or null + * + * {@see BluetoothAudioConfig} + */ + public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { + if (VDBG) log("getAudioConfig(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getAudioConfig(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; + default: + return ""; + } + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothA2dpSink.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, + BluetoothA2dpSink.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6daa61d79a8..ee0da22cdcb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1390,6 +1390,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.A2DP) { BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); return true; + } else if (profile == BluetoothProfile.A2DP_SINK) { + BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); + return true; } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; @@ -1433,6 +1436,10 @@ public final class BluetoothAdapter { BluetoothA2dp a2dp = (BluetoothA2dp)proxy; a2dp.close(); break; + case BluetoothProfile.A2DP_SINK: + BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink)proxy; + a2dpSink.close(); + break; case BluetoothProfile.INPUT_DEVICE: BluetoothInputDevice iDev = (BluetoothInputDevice)proxy; iDev.close(); diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl b/framework/java/android/bluetooth/BluetoothAudioConfig.aidl new file mode 100644 index 00000000000..63be5cff87d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009 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; + +parcelable BluetoothAudioConfig; diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java new file mode 100644 index 00000000000..03176b9e073 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 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.os.Parcel; +import android.os.Parcelable; + +/** + * Represents the audio configuration for a Bluetooth A2DP source device. + * + * {@see BluetoothA2dpSink} + * + * {@hide} + */ +public final class BluetoothAudioConfig implements Parcelable { + + private final int mSampleRate; + private final int mChannelConfig; + private final int mAudioFormat; + + public BluetoothAudioConfig(int sampleRate, int channelConfig, int audioFormat) { + mSampleRate = sampleRate; + mChannelConfig = channelConfig; + mAudioFormat = audioFormat; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothAudioConfig) { + BluetoothAudioConfig bac = (BluetoothAudioConfig)o; + return (bac.mSampleRate == mSampleRate && + bac.mChannelConfig == mChannelConfig && + bac.mAudioFormat == mAudioFormat); + } + return false; + } + + @Override + public int hashCode() { + return mSampleRate | (mChannelConfig << 24) | (mAudioFormat << 28); + } + + @Override + public String toString() { + return "{mSampleRate:" + mSampleRate + ",mChannelConfig:" + mChannelConfig + + ",mAudioFormat:" + mAudioFormat + "}"; + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothAudioConfig createFromParcel(Parcel in) { + int sampleRate = in.readInt(); + int channelConfig = in.readInt(); + int audioFormat = in.readInt(); + return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat); + } + public BluetoothAudioConfig[] newArray(int size) { + return new BluetoothAudioConfig[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSampleRate); + out.writeInt(mChannelConfig); + out.writeInt(mAudioFormat); + } + + /** + * Returns the sample rate in samples per second + * @return sample rate + */ + public int getSampleRate() { + return mSampleRate; + } + + /** + * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO} + * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO}) + * @return channel configuration + */ + public int getChannelConfig() { + return mChannelConfig; + } + + /** + * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT} + * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT} + * @return audio format + */ + public int getAudioFormat() { + return mAudioFormat; + } +} diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl new file mode 100644 index 00000000000..b7c64767700 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothAudioConfig; +import android.bluetooth.BluetoothDevice; + +/** + * APIs for Bluetooth A2DP sink service + * + * @hide + */ +interface IBluetoothA2dpSink { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); +} -- GitLab From c7afa17621bfdc826a071e463c9be121fdafd633 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Fri, 14 Feb 2014 19:53:31 +0530 Subject: [PATCH 0357/1408] Add Support for AVRCP Controller Feature - Provide support for AVRCP Controller CAT 1 and CAT 2 passthrough commands. Change-Id: Iefbb7fcd6273c49dc8a305b7a25ec6e94c60a5a9 --- .../java/android/bluetooth/BluetoothA2dp.java | 35 +++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 2 ++ 2 files changed, 37 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7b709acd911..927aa2127e9 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -90,6 +90,11 @@ public final class BluetoothA2dp implements BluetoothProfile { public static final String ACTION_PLAYING_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = + "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; + /** * A2DP sink device is streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of @@ -547,4 +552,34 @@ public final class BluetoothA2dp implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } + + /** @hide */ + public void sendPassThroughCmd(int keyCode, int keyState) { + if (DBG) Log.d(TAG, "sendPassThroughCmd"); + if (mService != null && isEnabled()) { + try { + mService.sendPassThroughCmd(keyCode, keyState); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + /** @hide */ + public boolean isAvrcpConnected(BluetoothDevice device) { + if (DBG) Log.d(TAG, "isAvrcpConnected"); + if (mService != null && isEnabled()) { + try { + return mService.isAvrcpConnected(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in isAvrcpConnected()", e); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } } diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 26ff9e274c3..4d9c0079fe1 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -36,4 +36,6 @@ interface IBluetoothA2dp { oneway void adjustAvrcpAbsoluteVolume(int direction); oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); + void sendPassThroughCmd(int keyCode, int keyState); + boolean isAvrcpConnected(in BluetoothDevice device); } -- GitLab From 517b04f1485e17c93ad21bb303e8d1acade86ba6 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 2 Jun 2014 16:20:37 -0700 Subject: [PATCH 0358/1408] BluetoothAvrcpController: Move AVRCP controller support to new BluetoothProfile subclass Change-Id: Id988040a7ce623ed68e0349920301ff48db1fbce --- .../java/android/bluetooth/BluetoothA2dp.java | 30 -- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothAvrcp.java | 93 +++++++ .../bluetooth/BluetoothAvrcpController.java | 260 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 6 + .../android/bluetooth/IBluetoothA2dp.aidl | 2 - .../bluetooth/IBluetoothAvrcpController.aidl | 31 +++ 7 files changed, 397 insertions(+), 32 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothAvrcp.java create mode 100644 framework/java/android/bluetooth/BluetoothAvrcpController.java create mode 100644 framework/java/android/bluetooth/IBluetoothAvrcpController.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 927aa2127e9..51754901163 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -552,34 +552,4 @@ public final class BluetoothA2dp implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } - - /** @hide */ - public void sendPassThroughCmd(int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); - if (mService != null && isEnabled()) { - try { - mService.sendPassThroughCmd(keyCode, keyState); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); - return; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } - - /** @hide */ - public boolean isAvrcpConnected(BluetoothDevice device) { - if (DBG) Log.d(TAG, "isAvrcpConnected"); - if (mService != null && isEnabled()) { - try { - return mService.isAvrcpConnected(device); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in isAvrcpConnected()", e); - return false; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ee0da22cdcb..ba42f51b624 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1393,6 +1393,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.A2DP_SINK) { BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); return true; + } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { + BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); + return true; } else if (profile == BluetoothProfile.INPUT_DEVICE) { BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); return true; @@ -1440,6 +1443,10 @@ public final class BluetoothAdapter { BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink)proxy; a2dpSink.close(); break; + case BluetoothProfile.AVRCP_CONTROLLER: + BluetoothAvrcpController avrcp = (BluetoothAvrcpController)proxy; + avrcp.close(); + break; case BluetoothProfile.INPUT_DEVICE: BluetoothInputDevice iDev = (BluetoothInputDevice)proxy; iDev.close(); diff --git a/framework/java/android/bluetooth/BluetoothAvrcp.java b/framework/java/android/bluetooth/BluetoothAvrcp.java new file mode 100644 index 00000000000..44fe1b73712 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcp.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 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; + +/** + * This class contains constants for Bluetooth AVRCP profile. + * + * {@hide} + */ +public final class BluetoothAvrcp { + + /* + * State flags for Passthrough commands + */ + public static final int PASSTHROUGH_STATE_PRESS = 0; + public static final int PASSTHROUGH_STATE_RELEASE = 1; + + /* + * Operation IDs for Passthrough commands + */ + public static final int PASSTHROUGH_ID_SELECT = 0x00; /* select */ + public static final int PASSTHROUGH_ID_UP = 0x01; /* up */ + public static final int PASSTHROUGH_ID_DOWN = 0x02; /* down */ + public static final int PASSTHROUGH_ID_LEFT = 0x03; /* left */ + public static final int PASSTHROUGH_ID_RIGHT = 0x04; /* right */ + public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05; /* right-up */ + public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06; /* right-down */ + public static final int PASSTHROUGH_ID_LEFT_UP = 0x07; /* left-up */ + public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08; /* left-down */ + public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09; /* root menu */ + public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A; /* setup menu */ + public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B; /* contents menu */ + public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C; /* favorite menu */ + public static final int PASSTHROUGH_ID_EXIT = 0x0D; /* exit */ + public static final int PASSTHROUGH_ID_0 = 0x20; /* 0 */ + public static final int PASSTHROUGH_ID_1 = 0x21; /* 1 */ + public static final int PASSTHROUGH_ID_2 = 0x22; /* 2 */ + public static final int PASSTHROUGH_ID_3 = 0x23; /* 3 */ + public static final int PASSTHROUGH_ID_4 = 0x24; /* 4 */ + public static final int PASSTHROUGH_ID_5 = 0x25; /* 5 */ + public static final int PASSTHROUGH_ID_6 = 0x26; /* 6 */ + public static final int PASSTHROUGH_ID_7 = 0x27; /* 7 */ + public static final int PASSTHROUGH_ID_8 = 0x28; /* 8 */ + public static final int PASSTHROUGH_ID_9 = 0x29; /* 9 */ + public static final int PASSTHROUGH_ID_DOT = 0x2A; /* dot */ + public static final int PASSTHROUGH_ID_ENTER = 0x2B; /* enter */ + public static final int PASSTHROUGH_ID_CLEAR = 0x2C; /* clear */ + public static final int PASSTHROUGH_ID_CHAN_UP = 0x30; /* channel up */ + public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31; /* channel down */ + public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32; /* previous channel */ + public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33; /* sound select */ + public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34; /* input select */ + public static final int PASSTHROUGH_ID_DISP_INFO = 0x35; /* display information */ + public static final int PASSTHROUGH_ID_HELP = 0x36; /* help */ + public static final int PASSTHROUGH_ID_PAGE_UP = 0x37; /* page up */ + public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38; /* page down */ + public static final int PASSTHROUGH_ID_POWER = 0x40; /* power */ + public static final int PASSTHROUGH_ID_VOL_UP = 0x41; /* volume up */ + public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42; /* volume down */ + public static final int PASSTHROUGH_ID_MUTE = 0x43; /* mute */ + public static final int PASSTHROUGH_ID_PLAY = 0x44; /* play */ + public static final int PASSTHROUGH_ID_STOP = 0x45; /* stop */ + public static final int PASSTHROUGH_ID_PAUSE = 0x46; /* pause */ + public static final int PASSTHROUGH_ID_RECORD = 0x47; /* record */ + public static final int PASSTHROUGH_ID_REWIND = 0x48; /* rewind */ + public static final int PASSTHROUGH_ID_FAST_FOR = 0x49; /* fast forward */ + public static final int PASSTHROUGH_ID_EJECT = 0x4A; /* eject */ + public static final int PASSTHROUGH_ID_FORWARD = 0x4B; /* forward */ + public static final int PASSTHROUGH_ID_BACKWARD = 0x4C; /* backward */ + public static final int PASSTHROUGH_ID_ANGLE = 0x50; /* angle */ + public static final int PASSTHROUGH_ID_SUBPICT = 0x51; /* subpicture */ + public static final int PASSTHROUGH_ID_F1 = 0x71; /* F1 */ + public static final int PASSTHROUGH_ID_F2 = 0x72; /* F2 */ + public static final int PASSTHROUGH_ID_F3 = 0x73; /* F3 */ + public static final int PASSTHROUGH_ID_F4 = 0x74; /* F4 */ + public static final int PASSTHROUGH_ID_F5 = 0x75; /* F5 */ + public static final int PASSTHROUGH_ID_VENDOR = 0x7E; /* vendor unique */ + public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80; +} diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java new file mode 100644 index 00000000000..b53a8fc0f6f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2014 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth AVRCP Controller + * profile. + * + *

        BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothAvrcpController proxy object. + * + * {@hide} + */ +public final class BluetoothAvrcpController implements BluetoothProfile { + private static final String TAG = "BluetoothAvrcpController"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the AVRCP Controller + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + + private Context mContext; + private ServiceListener mServiceListener; + private IBluetoothAvrcpController mService; + private BluetoothAdapter mAdapter; + + 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 BluetoothAvrcpController proxy object for interacting with the local + * Bluetooth AVRCP service. + * + */ + /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { + 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(IBluetoothAvrcpController.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 AVRCP Controller Service with " + intent); + return false; + } + return true; + } + + /*package*/ void close() { + mServiceListener = null; + 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); + } + } + } + } + + public void finalize() { + close(); + } + + /** + * {@inheritDoc} + */ + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { + if (DBG) Log.d(TAG, "sendPassThroughCmd"); + if (mService != null && isEnabled()) { + try { + mService.sendPassThroughCmd(device, keyCode, keyState); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothAvrcpController.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, + BluetoothAvrcpController.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index ee95eceadbb..136740505a4 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -109,6 +109,12 @@ public interface BluetoothProfile { */ public static final int A2DP_SINK = 10; + /** + * AVRCP Controller Profile + * @hide + */ + public static final int AVRCP_CONTROLLER = 11; + /** * Headset Client - HFP HF Role * @hide diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 4d9c0079fe1..26ff9e274c3 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -36,6 +36,4 @@ interface IBluetoothA2dp { oneway void adjustAvrcpAbsoluteVolume(int direction); oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); - void sendPassThroughCmd(int keyCode, int keyState); - boolean isAvrcpConnected(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl new file mode 100644 index 00000000000..f917a50860b --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 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.bluetooth.BluetoothDevice; + +/** + * APIs for Bluetooth AVRCP controller service + * + * @hide + */ +interface IBluetoothAvrcpController { + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); +} -- GitLab From 7160e5f7b668b874f4388c5d0735961b22ff564a Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Tue, 24 Jun 2014 10:19:45 -0700 Subject: [PATCH 0359/1408] Fix bug where GATT descriptors weren't being added to right characteristic When there's more than one characteristic with the same UUID in a service, the descriptors for the non-zero instanceID characteristics were being assigned to the zero instanceID one, so descriptor fetching wouldn't work. This is particularly bad for HID devices since separate reports would show up as multiple characteristics with the same UUID but there'd be no way to identify them without their descriptor attributes. Change-Id: I92bb4be34aa941dd3c6750874bef2e8a261ded92 Signed-off-by: Mike J. Chen --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index c9df9c0eede..7b5bfbd8af5 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -267,7 +267,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (service == null) return; BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid()); + charUuid.getUuid(), charInstId); if (characteristic == null) return; characteristic.addDescriptor(new BluetoothGattDescriptor( -- GitLab From c325fca597511474ba22432e0cc76aae6affc911 Mon Sep 17 00:00:00 2001 From: Ang Li Date: Wed, 25 Jun 2014 13:16:01 -0700 Subject: [PATCH 0360/1408] Fix comment for sendData. Merging kwd commit 683de828a8d1244d3220bb43f5d5742f594887d6 Change-Id: I5f3c16f3e99dce8bbb0011af3322ce3e6487d6d9 --- framework/java/android/bluetooth/BluetoothInputDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 554df3e4554..252e3d28a25 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -640,7 +640,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param device Remote Bluetooth Device - * @param data Data to send + * @param report Report to send * @return false on immediate error, * true otherwise * @hide -- GitLab From 47f8574d0ea25a6f099f0905275b12562cde90e7 Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Tue, 20 May 2014 15:30:13 -0400 Subject: [PATCH 0361/1408] Convert BluetoothTetheringDataTracker into a NetworkFactory. bug:15407087 Change-Id: I0437ca52dedf73e8ec69ac82e46353f6fafc4a42 --- .../BluetoothTetheringDataTracker.java | 418 ------------------ 1 file changed, 418 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothTetheringDataTracker.java diff --git a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java b/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java deleted file mode 100644 index f0c82997aa9..00000000000 --- a/framework/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2010 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.net.BaseNetworkStateTracker; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.DhcpResults; -import android.net.NetworkCapabilities; -import android.net.LinkProperties; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; -import android.net.NetworkStateTracker; -import android.net.NetworkUtils; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.util.AsyncChannel; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -/** - * This class tracks the data connection associated with Bluetooth - * reverse tethering. This is a singleton class and an instance will be - * created by ConnectivityService. BluetoothService will call into this - * when a reverse tethered connection needs to be activated. - * - * @hide - */ -public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { - private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; - private static final String TAG = "BluetoothTethering"; - private static final boolean DBG = true; - private static final boolean VDBG = true; - - // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. - private static final int EVENT_NETWORK_FAILED = 1; - - private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); - private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); - private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); - private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); - - private final Object mLinkPropertiesLock = new Object(); - private final Object mNetworkInfoLock = new Object(); - - private BluetoothPan mBluetoothPan; - private static String mRevTetheredIface; - /* For sending events to connectivity service handler */ - private Handler mCsHandler; - private static BluetoothTetheringDataTracker sInstance; - private BtdtHandler mBtdtHandler; - private AtomicReference mAsyncChannel = new AtomicReference(null); - - private BluetoothTetheringDataTracker() { - mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); - mLinkProperties = new LinkProperties(); - mNetworkCapabilities = new NetworkCapabilities(); - - mNetworkInfo.setIsAvailable(false); - setTeardownRequested(false); - } - - public static synchronized BluetoothTetheringDataTracker getInstance() { - if (sInstance == null) sInstance = new BluetoothTetheringDataTracker(); - return sInstance; - } - - public Object Clone() throws CloneNotSupportedException { - throw new CloneNotSupportedException(); - } - - public void setTeardownRequested(boolean isRequested) { - mTeardownRequested.set(isRequested); - } - - public boolean isTeardownRequested() { - return mTeardownRequested.get(); - } - - /** - * Begin monitoring connectivity - */ - public void startMonitoring(Context context, Handler target) { - if (DBG) Log.d(TAG, "startMonitoring: target: " + target); - mContext = context; - mCsHandler = target; - if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); - } - mBtdtHandler = new BtdtHandler(target.getLooper(), this); - } - - private BluetoothProfile.ServiceListener mProfileServiceListener = - new BluetoothProfile.ServiceListener() { - public void onServiceConnected(int profile, BluetoothProfile proxy) { - mBluetoothPan = (BluetoothPan) proxy; - } - public void onServiceDisconnected(int profile) { - mBluetoothPan = null; - } - }; - - /** - * Disable connectivity to a network - * TODO: do away with return value after making MobileDataStateTracker async - */ - public boolean teardown() { - mTeardownRequested.set(true); - if (mBluetoothPan != null) { - for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { - mBluetoothPan.disconnect(device); - } - } - return true; - } - - @Override - public void captivePortalCheckCompleted(boolean isCaptivePortal) { - // not implemented - } - - /** - * Re-enable connectivity to a network after a {@link #teardown()}. - */ - public boolean reconnect() { - mTeardownRequested.set(false); - //Ignore - return true; - } - - /** - * Turn the wireless radio off for a network. - * @param turnOn {@code true} to turn the radio on, {@code false} - */ - public boolean setRadio(boolean turnOn) { - return true; - } - - /** - * @return true - If are we currently tethered with another device. - */ - public synchronized boolean isAvailable() { - return mNetworkInfo.isAvailable(); - } - - /** - * Tells the underlying networking system that the caller wants to - * begin using the named feature. The interpretation of {@code feature} - * is completely up to each networking implementation. - * @param feature the name of the feature to be used - * @param callingPid the process ID of the process that is issuing this request - * @param callingUid the user ID of the process that is issuing this request - * @return an integer value representing the outcome of the request. - * The interpretation of this value is specific to each networking - * implementation+feature combination, except that the value {@code -1} - * always indicates failure. - * TODO: needs to go away - */ - public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; - } - - /** - * Tells the underlying networking system that the caller is finished - * using the named feature. The interpretation of {@code feature} - * is completely up to each networking implementation. - * @param feature the name of the feature that is no longer needed. - * @param callingPid the process ID of the process that is issuing this request - * @param callingUid the user ID of the process that is issuing this request - * @return an integer value representing the outcome of the request. - * The interpretation of this value is specific to each networking - * implementation+feature combination, except that the value {@code -1} - * always indicates failure. - * TODO: needs to go away - */ - public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; - } - - @Override - public void setUserDataEnable(boolean enabled) { - Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); - } - - @Override - public void setPolicyDataEnable(boolean enabled) { - Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); - } - - /** - * Check if private DNS route is set for the network - */ - public boolean isPrivateDnsRouteSet() { - return mPrivateDnsRouteSet.get(); - } - - /** - * Set a flag indicating private DNS route is set - */ - public void privateDnsRouteSet(boolean enabled) { - mPrivateDnsRouteSet.set(enabled); - } - - /** - * Fetch NetworkInfo for the network - */ - public NetworkInfo getNetworkInfo() { - synchronized (mNetworkInfoLock) { - return new NetworkInfo(mNetworkInfo); - } - } - - /** - * Fetch LinkProperties for the network - */ - public LinkProperties getLinkProperties() { - synchronized (mLinkPropertiesLock) { - return new LinkProperties(mLinkProperties); - } - } - - /** - * Fetch default gateway address for the network - */ - public int getDefaultGatewayAddr() { - return mDefaultGatewayAddr.get(); - } - - /** - * Check if default route is set - */ - public boolean isDefaultRouteSet() { - return mDefaultRouteSet.get(); - } - - /** - * Set a flag indicating default route is set for the network - */ - public void defaultRouteSet(boolean enabled) { - mDefaultRouteSet.set(enabled); - } - - /** - * Return the system properties name associated with the tcp buffer sizes - * for this network. - */ - public String getTcpBufferSizesPropName() { - return "net.tcp.buffersize.wifi"; - } - - private static short countPrefixLength(byte [] mask) { - short count = 0; - for (byte b : mask) { - for (int i = 0; i < 8; ++i) { - if ((b & (1 << i)) != 0) { - ++count; - } - } - } - return count; - } - - void startReverseTether(final LinkProperties linkProperties) { - if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) { - Log.e(TAG, "attempted to reverse tether with empty interface"); - return; - } - synchronized (mLinkPropertiesLock) { - if (mLinkProperties.getInterfaceName() != null) { - Log.e(TAG, "attempted to reverse tether while already in process"); - return; - } - mLinkProperties = linkProperties; - } - Thread dhcpThread = new Thread(new Runnable() { - public void run() { - //Currently this thread runs independently. - DhcpResults dhcpResults = new DhcpResults(); - boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(), - dhcpResults); - synchronized (mLinkPropertiesLock) { - if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) { - Log.e(TAG, "obsolete DHCP run aborted"); - return; - } - if (!success) { - Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); - mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); - return; - } - mLinkProperties = dhcpResults.linkProperties; - synchronized (mNetworkInfoLock) { - mNetworkInfo.setIsAvailable(true); - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - if (mCsHandler != null) { - Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, - new NetworkInfo(mNetworkInfo)); - msg.sendToTarget(); - } - } - return; - } - } - }); - dhcpThread.start(); - } - - void stopReverseTether() { - synchronized (mLinkPropertiesLock) { - if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) { - Log.e(TAG, "attempted to stop reverse tether with nothing tethered"); - return; - } - NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName()); - mLinkProperties.clear(); - synchronized (mNetworkInfoLock) { - mNetworkInfo.setIsAvailable(false); - mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - - if (mCsHandler != null) { - mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)). - sendToTarget(); - } - } - } - } - - public void setDependencyMet(boolean met) { - // not supported on this network - } - - @Override - public void addStackedLink(LinkProperties link) { - mLinkProperties.addStackedLink(link); - } - - @Override - public void removeStackedLink(LinkProperties link) { - mLinkProperties.removeStackedLink(link); - } - - static class BtdtHandler extends Handler { - private AsyncChannel mStackChannel; - private final BluetoothTetheringDataTracker mBtdt; - - BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) { - super(looper); - mBtdt = parent; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED"); - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - AsyncChannel ac = (AsyncChannel)msg.obj; - if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) { - Log.e(TAG, "Trying to set mAsyncChannel twice!"); - } else { - ac.sendMessage( - AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - } - } - break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED"); - mBtdt.stopReverseTether(); - mBtdt.mAsyncChannel.set(null); - break; - case NetworkStateTracker.EVENT_NETWORK_CONNECTED: - LinkProperties linkProperties = (LinkProperties)(msg.obj); - if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties); - mBtdt.startReverseTether(linkProperties); - break; - case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED: - linkProperties = (LinkProperties)(msg.obj); - if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); - mBtdt.stopReverseTether(); - break; - case EVENT_NETWORK_FAILED: - if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); - mBtdt.teardown(); - break; - } - } - } - - @Override - public void supplyMessenger(Messenger messenger) { - if (messenger != null) { - new AsyncChannel().connect(mContext, mBtdtHandler, messenger); - } - } -} -- GitLab From 745ef9e3f3793abf0f0a14ab8765f17be153ece0 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 28 Mar 2014 14:54:53 -0700 Subject: [PATCH 0362/1408] LE: Add notification sent and congestion callbacks (3/4) This change introduces two new callbacks for applications to better handle LE notification flow control and transport congestion. The notification callback is invoked when the remote platform confirms an indication or when a local notification has been passed to the controller. No new notifications should be sent until a callback is received. Congestion callbacks are triggered when a GATT operation cannot be sent to the local Bluetooth controller. Repeatedly calling writeCharacteristic() for example will eventually trigger a congestion callback. Applications cannot send additional data until a further callback is received, indicating that the congestion has cleared up. Also made server callbacks "oneway" in the AIDL definition file. Change-Id: I7fa3324712205c79efce58e5e3df8b80a265a442 --- .../android/bluetooth/BluetoothAdapter.java | 11 +++--- .../java/android/bluetooth/BluetoothGatt.java | 18 ++++++++++ .../bluetooth/BluetoothGattCallback.java | 15 ++++++++ .../bluetooth/BluetoothGattServer.java | 36 +++++++++++++++++++ .../BluetoothGattServerCallback.java | 30 ++++++++++++++++ .../bluetooth/IBluetoothGattCallback.aidl | 1 + .../IBluetoothGattServerCallback.aidl | 4 ++- .../bluetooth/le/BluetoothLeAdvertiser.java | 5 +++ .../bluetooth/le/BluetoothLeScanner.java | 5 +++ 9 files changed, 120 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ba42f51b624..22872465b73 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2079,12 +2079,15 @@ public final class BluetoothAdapter { public void onMultiAdvertiseCallback(int status) { // no op } - /** - * Callback reporting LE ATT MTU. - * @hide - */ + + @Override public void onConfigureMTU(String address, int mtu, int status) { // no op } + + @Override + public void onConnectionCongested(String address, boolean congested) { + // no op + } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 7b5bfbd8af5..c63de62f861 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -85,6 +85,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; + /** A remote device connection is congested. */ + public static final int GATT_CONNECTION_CONGESTED = 0x8f; + /** A GATT operation failed, errors other than the above */ public static final int GATT_FAILURE = 0x101; @@ -607,6 +610,21 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * Callback indicating the remote device connection is congested. + * @hide + */ + public void onConnectionCongested(String address, boolean congested) { + if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address + + " congested=" + congested); + if (!address.equals(mDevice.getAddress())) return; + try { + mCallback.onConnectionCongested(BluetoothGatt.this, congested); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 5180259cc12..b5e60f2c981 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -153,4 +153,19 @@ public abstract class BluetoothGattCallback { */ public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { } + + /** + * Callback indicating that a remote device connection congestestion status has changed. + * + * An application should refrain from sending additional data to a remote device when + * a callback is received with the congested flag set to true. Once the congestion status + * is cleared up, the application will receive an additional callback with the congested + * flag set to false. + * + * @param gatt The GATT client associated with the remote device + * @param congested true, if the connection is currently congested + * @hide + */ + public void onConnectionCongested(BluetoothGatt gatt, boolean congested) { + } } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 34e86055db7..2e993c9df12 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -265,6 +265,42 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * A notification/indication has been sent. + * @hide + */ + public void onNotificationSent(String address, int status) { + if (DBG) Log.d(TAG, "onNotificationSent() - " + + "device=" + address + ", status=" + status); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onNotificationSent(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * Callback indicating the remote device connection is congested. + * @hide + */ + public void onConnectionCongested(String address, boolean congested) { + if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address + + " congested=" + congested); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onConnectionCongested(device, congested); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } }; /** diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index fc3ffe88bcc..4fbeb46735f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -131,4 +131,34 @@ public abstract class BluetoothGattServerCallback { */ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { } + + /** + * Callback invoked when a notification or indication has been sent to + * a remote device. + * + *

        When multiple notifications are to be sent, an application must + * wait for this callback to be received before sending additional + * notifications. + * + * @param device The remote device the notification has been sent to + * @param status 0 if the operation was successful + * @hide + */ + public void onNotificationSent(BluetoothDevice device, int status) { + } + + /** + * Callback indicating that a remote device connection congestestion status has changed. + * + * An application should refrain from sending additional data (notifications, indications + * etc.) to a remote device when a callback is received with the congested flag set + * to true. Once the congestion status is cleared up, the application will receive an + * additional callback with the congested flag set to false. + * + * @param device The remote device that triggered the congestion state change + * @param congested true, if the connection is currently congested + * @hide + */ + public void onConnectionCongested(BluetoothDevice device, boolean congested) { + } } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 2d8eed467fe..946a6f6174b 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -66,4 +66,5 @@ oneway interface IBluetoothGattCallback { void onAdvertiseStateChange(in int advertiseState, in int status); void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); + void onConnectionCongested(in String address, in boolean congested); } diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index ae9bffcf4d3..6e31da1d31e 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -22,7 +22,7 @@ import android.os.ParcelUuid; * Callback definitions for interacting with BLE / GATT * @hide */ -interface IBluetoothGattServerCallback { +oneway interface IBluetoothGattServerCallback { void onServerRegistered(in int status, in int serverIf); void onScanResult(in String address, in int rssi, in byte[] advData); void onServerConnectionState(in int status, in int serverIf, @@ -58,4 +58,6 @@ interface IBluetoothGattServerCallback { in ParcelUuid descrId, in byte[] value); void onExecuteWrite(in String address, in int transId, in boolean execWrite); + void onNotificationSent(in String address, in int status); + void onConnectionCongested(in String address, in boolean congested); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ed43407d721..c20b81be669 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -355,6 +355,11 @@ public final class BluetoothLeAdvertiser { public void onConfigureMTU(String address, int mtu, int status) { // no op } + + @Override + public void onConnectionCongested(String address, boolean congested) { + // no op + } } private void postCallbackFailure(final AdvertiseCallback callback, final int error) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 4c6346cc1d8..fbaf5d2e63a 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -358,6 +358,11 @@ public final class BluetoothLeScanner { public void onConfigureMTU(String address, int mtu, int status) { // no op } + + @Override + public void onConnectionCongested(String address, boolean congested) { + // no op + } } private void postCallbackError(final ScanCallback callback, final int errorCode) { -- GitLab From 1f5efc48e349132fbb12cf5eac9a3df30ff8101d Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 27 Jun 2014 11:42:19 -0700 Subject: [PATCH 0363/1408] LE: Hide new congestion constant for now Change-Id: I86455be16bc3356a1ad24db6d6e6835cb299eb5f --- framework/java/android/bluetooth/BluetoothGatt.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index c63de62f861..6c1a45e301b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -85,7 +85,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; - /** A remote device connection is congested. */ + /** A remote device connection is congested. + * @hide + */ public static final int GATT_CONNECTION_CONGESTED = 0x8f; /** A GATT operation failed, errors other than the above */ -- GitLab From 80bf6280c19a741a1d0aa758ea29bda7ad2cc8a9 Mon Sep 17 00:00:00 2001 From: Mudumba Ananth Date: Sun, 27 Apr 2014 13:11:00 -0700 Subject: [PATCH 0364/1408] Add WBS support on Bluedroid (6/6) Bug 13764086 Change-Id: I7afbddb02fc247d7799ab0fb264a467c7853cb32 --- .../android/bluetooth/BluetoothHeadset.java | 44 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 2 + 2 files changed, 46 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index f88a173908c..353f0fba05d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -886,6 +886,50 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * enable WBS codec setting. + * + * @return true if successful + * false if there was some error such as + * there is no connected headset + * @hide + */ + public boolean enableWBS() { + if (mService != null && isEnabled()) { + try { + return mService.enableWBS(); + } 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; + } + + /** + * disable WBS codec settting. It set NBS codec. + * + * @return true if successful + * false if there was some error such as + * there is no connected headset + * @hide + */ + public boolean disableWBS() { + if (mService != null && isEnabled()) { + try { + return mService.disableWBS(); + } 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; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 524ca6f77c7..0e23fada831 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -55,4 +55,6 @@ interface IBluetoothHeadset { void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type); + boolean enableWBS(); + boolean disableWBS(); } -- GitLab From 25eb545b4c89baee20525f2fb4dc7737eae8a73b Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 27 Jun 2014 14:31:37 -0700 Subject: [PATCH 0365/1408] LE: Fix GATT server documentation to reflect API updates Change-Id: I21100dd55a7bf9e85a07742f571d5743bad5ece1 --- .../java/android/bluetooth/BluetoothGattServer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 2e993c9df12..fbbc2f77bb9 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -32,12 +32,12 @@ import java.util.UUID; * Public API for the Bluetooth GATT Profile server role. * *

        This class provides Bluetooth GATT server role functionality, - * allowing applications to create and advertise Bluetooth Smart services - * and characteristics. + * allowing applications to create Bluetooth Smart services and + * characteristics. * *

        BluetoothGattServer is a proxy object for controlling the Bluetooth Service - * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the - * BluetoothGatt proxy object. + * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance + * of this class. */ public final class BluetoothGattServer implements BluetoothProfile { private static final String TAG = "BluetoothGattServer"; @@ -545,7 +545,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Add a service to the list of services to be hosted. * - *

        Once a service has been addded to the the list, the service and it's + *

        Once a service has been addded to the the list, the service and its * included characteristics will be provided by the local device. * *

        If the local device has already exposed services when this function -- GitLab From 64ec47dc431569c9f0854f76b3b652462d508176 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Fri, 16 Aug 2013 14:57:55 +0530 Subject: [PATCH 0366/1408] Bluetooth: Support MAP Client role on Bluedroid. Implementation changes to support MAP client and PBAP client role on Bluedroid stack. Change-Id: I1733a67bf5256bd7b181bd5e68e40b476994ebfd --- .../android/bluetooth/BluetoothDevice.java | 24 +++- .../bluetooth/BluetoothMasInstance.java | 103 ++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothMasInstance.java diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 64d80a066c8..a94bc413df8 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -306,6 +306,11 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_MAS_INSTANCE = + "android.bluetooth.device.action.MAS_INSTANCE"; + /** * Broadcast Action: Indicates a failure to retrieve the name of a remote * device. @@ -522,14 +527,17 @@ public final class BluetoothDevice implements Parcelable { * Prefer BR/EDR transport for GATT connections to remote dual-mode devices * @hide */ - public static final int TRANSPORT_BREDR = 1; + public static final int TRANSPORT_BREDR = 1; /** * Prefer LE transport for GATT connections to remote dual-mode devices * @hide */ - public static final int TRANSPORT_LE = 2; + public static final int TRANSPORT_LE = 2; + /** @hide */ + public static final String EXTRA_MAS_INSTANCE = + "android.bluetooth.device.extra.MAS_INSTANCE"; /** * Lazy initialization. Guaranteed final after first object constructed, or @@ -995,6 +1003,18 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ + public boolean fetchMasInstances() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances"); + return false; + } + try { + return sService.fetchRemoteMasInstances(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** @hide */ public int getServiceChannel(ParcelUuid uuid) { //TODO(BT) diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java new file mode 100644 index 00000000000..4459e2c44bd --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothMasInstance.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public final class BluetoothMasInstance implements Parcelable { + private final int mId; + private final String mName; + private final int mChannel; + private final int mMsgTypes; + + public BluetoothMasInstance(int id, String name, int channel, int msgTypes) { + mId = id; + mName = name; + mChannel = channel; + mMsgTypes = msgTypes; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothMasInstance) { + return mId == ((BluetoothMasInstance)o).mId; + } + return false; + } + + @Override + public int hashCode() { + return mId + (mChannel << 8) + (mMsgTypes << 16); + } + + @Override + public String toString() { + return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" + + Integer.toHexString(mMsgTypes); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothMasInstance createFromParcel(Parcel in) { + return new BluetoothMasInstance(in.readInt(), in.readString(), + in.readInt(), in.readInt()); + } + public BluetoothMasInstance[] newArray(int size) { + return new BluetoothMasInstance[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mId); + out.writeString(mName); + out.writeInt(mChannel); + out.writeInt(mMsgTypes); + } + + public static final class MessageType { + public static final int EMAIL = 0x01; + public static final int SMS_GSM = 0x02; + public static final int SMS_CDMA = 0x04; + public static final int MMS = 0x08; + } + + public int getId() { + return mId; + } + + public String getName() { + return mName; + } + + public int getChannel() { + return mChannel; + } + + public int getMsgTypes() { + return mMsgTypes; + } + + public boolean msgSupported(int msg) { + return (mMsgTypes & msg) != 0; + } +} diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index a45c6b8d55b..df6037ecc5f 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -67,6 +67,7 @@ interface IBluetooth int getRemoteClass(in BluetoothDevice device); ParcelUuid[] getRemoteUuids(in BluetoothDevice device); boolean fetchRemoteUuids(in BluetoothDevice device); + boolean fetchRemoteMasInstances(in BluetoothDevice device); boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] -- GitLab From d9b0b0eb7f08f129f461013b8a7a00e7ec55769d Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 2 Jul 2014 12:30:38 -0700 Subject: [PATCH 0367/1408] Obtain capabilities from chipset for it's support of various features Change-Id: I01bdb31136be63e4e46fb4e054c902eddc5647ab --- .../android/bluetooth/BluetoothAdapter.java | 48 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 ++ 2 files changed, 52 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 22872465b73..d75304feefe 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1038,6 +1038,54 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if the multi advertisement is supported by the chipset + * + * @hide + * @return true if Multiple Advertisement feature is supported + */ + public boolean isMultipleAdvertisementSupported() { + if (getState() != STATE_ON) return false; + try { + return mService.isMultiAdvertisementSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e); + } + return false; + } + + /** + * Return true if offloaded filters are supported + * + * @hide + * @return true if chipset supports on-chip filtering + */ + public boolean isOffloadedFilteringSupported() { + if (getState() != STATE_ON) return false; + try { + return mService.isOffloadedFilteringSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); + } + return false; + } + + /** + * Return true if offloaded scan batching is supported + * + * @hide + * @return true if chipset supports on-chip scan batching + */ + public boolean isOffloadedScanBatchingSupported() { + if (getState() != STATE_ON) return false; + try { + return mService.isOffloadedScanBatchingSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e); + } + return false; + } + /** * Returns whether BLE is currently advertising. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index df6037ecc5f..d334b9193c2 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -84,4 +84,8 @@ interface IBluetooth ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); boolean configHciSnoopLog(boolean enable); + + boolean isMultiAdvertisementSupported(); + boolean isOffloadedFilteringSupported(); + boolean isOffloadedScanBatchingSupported(); } -- GitLab From 7dbe2ac8c144bc6c05f07ded3fa146066c4c148b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 1 Jul 2014 15:10:06 -0700 Subject: [PATCH 0368/1408] Implement batch scan API Change-Id: Ibb527280a221fbdd0fc6b805a7527c29079294b4 --- .../android/bluetooth/BluetoothAdapter.java | 23 ++--- .../java/android/bluetooth/BluetoothGatt.java | 6 ++ .../android/bluetooth/IBluetoothGatt.aidl | 1 + .../bluetooth/IBluetoothGattCallback.aidl | 3 +- .../bluetooth/le/AdvertiseCallback.java | 6 ++ .../bluetooth/le/BluetoothLeAdvertiser.java | 38 +++++++-- .../bluetooth/le/BluetoothLeScanner.java | 83 +++++++++++++++---- .../android/bluetooth/le/ScanSettings.java | 2 +- 8 files changed, 121 insertions(+), 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d75304feefe..35e50500670 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanResult; import android.content.Context; import android.os.Handler; import android.os.IBinder; @@ -37,6 +38,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -504,13 +506,7 @@ public final class BluetoothAdapter { */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { // TODO: Return null if this feature is not supported by hardware. - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - return new BluetoothLeAdvertiser(iGatt); - } catch (RemoteException e) { - Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e); - return null; - } + return new BluetoothLeAdvertiser(mManagerService); } /** @@ -518,13 +514,7 @@ public final class BluetoothAdapter { */ public BluetoothLeScanner getBluetoothLeScanner() { // TODO: Return null if BLE scan is not supported by hardware. - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - return new BluetoothLeScanner(iGatt); - } catch (RemoteException e) { - Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e); - return null; - } + return new BluetoothLeScanner(mManagerService); } /** @@ -2137,5 +2127,10 @@ public final class BluetoothAdapter { public void onConnectionCongested(String address, boolean congested) { // no op } + + @Override + public void onBatchScanResults(List results) { + // no op + } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 6c1a45e301b..4255cd46ecb 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.bluetooth.le.ScanResult; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; @@ -627,6 +628,11 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + @Override + public void onBatchScanResults(List results) { + // no op + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 273d76dff88..5ed10a67c19 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -38,6 +38,7 @@ interface IBluetoothGatt { void startScanWithFilters(in int appIf, in boolean isServer, in ScanSettings settings, in List filters); void stopScan(in int appIf, in boolean isServer); + void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, in AdvertisementData advertiseData, in AdvertisementData scanResponse, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 946a6f6174b..c18d357ce41 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -16,7 +16,7 @@ package android.bluetooth; import android.os.ParcelUuid; - +import android.bluetooth.le.ScanResult; /** * Callback definitions for interacting with BLE / GATT @@ -27,6 +27,7 @@ oneway interface IBluetoothGattCallback { void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); void onScanResult(in String address, in int rssi, in byte[] advData); + void onBatchScanResults(in List batchResults); void onGetService(in String address, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid); void onGetIncludedService(in String address, in int srvcType, in int srvcInstId, diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java index f1334c2765b..59f8cdc964a 100644 --- a/framework/java/android/bluetooth/le/AdvertiseCallback.java +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -51,6 +51,12 @@ public abstract class AdvertiseCallback { */ public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5; + /** + * Operation fails due to GATT service failure. + * @hide + */ + public static final int ADVERTISE_FAILED_GATT_SERVICE_FAILURE = 6; + /** * Callback when advertising operation succeeds. * diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index c20b81be669..a83d875e097 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -27,6 +28,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -47,7 +49,7 @@ public final class BluetoothLeAdvertiser { private static final String TAG = "BluetoothLeAdvertiser"; - private final IBluetoothGatt mBluetoothGatt; + private final IBluetoothManager mBluetoothManager; private final Handler mHandler; private final Map mLeAdvertisers = new HashMap(); @@ -55,11 +57,11 @@ public final class BluetoothLeAdvertiser { /** * Use BluetoothAdapter.getLeAdvertiser() instead. * - * @param bluetoothGatt + * @param bluetoothManager * @hide */ - public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; + public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { + mBluetoothManager = bluetoothManager; mHandler = new Handler(Looper.getMainLooper()); } @@ -102,11 +104,19 @@ public final class BluetoothLeAdvertiser { postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); return; } + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get bluetooth gatt - ", e); + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_CONTROLLER_FAILURE); + return; + } AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - scanResponse, settings, mBluetoothGatt); + scanResponse, settings, gatt); UUID uuid = UUID.randomUUID(); try { - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + gatt.registerClient(new ParcelUuid(uuid), wrapper); if (wrapper.advertiseStarted()) { mLeAdvertisers.put(callback, wrapper); } @@ -133,12 +143,19 @@ public final class BluetoothLeAdvertiser { return; } try { - mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle); + IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt(); + if (gatt == null) { + postCallbackFailure(callback, + AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE); + } + gatt.stopMultiAdvertising(wrapper.mLeHandle); if (wrapper.advertiseStopped()) { mLeAdvertisers.remove(callback); } } catch (RemoteException e) { Log.e(TAG, "failed to stop advertising", e); + postCallbackFailure(callback, + AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE); } } @@ -214,8 +231,6 @@ public final class BluetoothLeAdvertiser { Log.e(TAG, "fail to start le advertise: " + e); mLeHandle = -1; notifyAll(); - } catch (Exception e) { - Log.e(TAG, "fail to start advertise: " + e.getStackTrace()); } } else { // registration failed @@ -360,6 +375,11 @@ public final class BluetoothLeAdvertiser { public void onConnectionCongested(String address, boolean congested) { // no op } + + @Override + public void onBatchScanResults(List results) { + // no op + } } private void postCallbackFailure(final AdvertiseCallback callback, final int error) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index fbaf5d2e63a..44aa117ffe0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -51,15 +52,15 @@ public final class BluetoothLeScanner { private static final String TAG = "BluetoothLeScanner"; private static final boolean DBG = true; - private final IBluetoothGatt mBluetoothGatt; + private final IBluetoothManager mBluetoothManager; private final Handler mHandler; private final Map mLeScanClients; /** * @hide */ - public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) { - mBluetoothGatt = bluetoothGatt; + public BluetoothLeScanner(IBluetoothManager bluetoothManager) { + mBluetoothManager = bluetoothManager; mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap(); } @@ -84,11 +85,21 @@ public final class BluetoothLeScanner { postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); return; } - BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters, + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + gatt = null; + } + if (gatt == null) { + postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE); + return; + } + BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, callback); try { UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper); + gatt.registerClient(new ParcelUuid(uuid), wrapper); if (wrapper.scanStarted()) { mLeScanClients.put(callback, wrapper); } else { @@ -131,18 +142,26 @@ public final class BluetoothLeScanner { } /** - * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results - * batched on bluetooth controller. + * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth + * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data + * will be delivered through the {@code callback}. * * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one * used to start scan. - * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will - * get batch scan callback if the batch scan buffer is flushed. - * @return Batch Scan results. - * @hide TODO: unhide when batching is supported in stack. + * + * @hide */ - public List getBatchScanResults(ScanCallback callback, boolean flush) { - throw new UnsupportedOperationException("not impelemented"); + public void flushPendingScanResults(ScanCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null!"); + } + synchronized (mLeScanClients) { + BleScanCallbackWrapper wrapper = mLeScanClients.get(callback); + if (wrapper == null) { + return; + } + wrapper.flushPendingBatchResults(); + } } /** @@ -195,13 +214,27 @@ public final class BluetoothLeScanner { mBluetoothGatt.stopScan(mLeHandle, false); mBluetoothGatt.unregisterClient(mLeHandle); } catch (RemoteException e) { - Log.e(TAG, "Failed to stop scan and unregister" + e); + Log.e(TAG, "Failed to stop scan and unregister", e); } mLeHandle = -1; notifyAll(); } } + void flushPendingBatchResults() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + try { + mBluetoothGatt.flushPendingBatchResults(mLeHandle, false); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get pending scan results", e); + } + } + } + /** * Application interface registered - app is ready to go */ @@ -256,9 +289,27 @@ public final class BluetoothLeScanner { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( address); long scanNanos = SystemClock.elapsedRealtimeNanos(); - ScanResult result = new ScanResult(device, advData, rssi, + final ScanResult result = new ScanResult(device, advData, rssi, scanNanos); - mScanCallback.onAdvertisementUpdate(result); + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + mScanCallback.onAdvertisementUpdate(result); + } + }); + + } + + @Override + public void onBatchScanResults(final List results) { + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + mScanCallback.onBatchScanResults(results); + } + }); } @Override diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 0a856756326..3bb39b33826 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -44,7 +44,6 @@ public final class ScanSettings implements Parcelable { public static final int CALLBACK_TYPE_ON_UPDATE = 0; /** * Callback when a bluetooth advertisement is found for the first time. - * * @hide */ public static final int CALLBACK_TYPE_ON_FOUND = 1; @@ -190,6 +189,7 @@ public final class ScanSettings implements Parcelable { * {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or * {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}. * @throws IllegalArgumentException If the {@code scanResultType} is invalid. + * * @hide */ public Builder setScanResultType(int scanResultType) { -- GitLab From b4c9d6108c2b2e895eb7817b661f1a544ace541a Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 8 Jul 2014 16:33:34 -0700 Subject: [PATCH 0369/1408] Tie in BLE hw capabilities with api invocation and support. Change-Id: Ic1f0b3f156cf2fdde8f04b5c4384e4fd3d79623a --- .../bluetooth/le/AdvertiseCallback.java | 6 +++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 6 +++++ .../bluetooth/le/BluetoothLeScanner.java | 22 +++++++++++++++++++ .../android/bluetooth/le/ScanCallback.java | 5 +++++ 4 files changed, 39 insertions(+) diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java index 59f8cdc964a..1d26ef93933 100644 --- a/framework/java/android/bluetooth/le/AdvertiseCallback.java +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -57,6 +57,12 @@ public abstract class AdvertiseCallback { */ public static final int ADVERTISE_FAILED_GATT_SERVICE_FAILURE = 6; + /** + * Operation fails as this feature is not supported + */ + public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 7; + + /** * Callback when advertising operation succeeds. * diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index a83d875e097..6b21ce25478 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -51,6 +51,7 @@ public final class BluetoothLeAdvertiser { private final IBluetoothManager mBluetoothManager; private final Handler mHandler; + private BluetoothAdapter mBluetoothAdapter; private final Map mLeAdvertisers = new HashMap(); @@ -62,6 +63,7 @@ public final class BluetoothLeAdvertiser { */ public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { mBluetoothManager = bluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mHandler = new Handler(Looper.getMainLooper()); } @@ -112,6 +114,10 @@ public final class BluetoothLeAdvertiser { postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_CONTROLLER_FAILURE); return; } + if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); + return; + } AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, scanResponse, settings, gatt); UUID uuid = UUID.randomUUID(); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 44aa117ffe0..863282add9f 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -54,6 +54,7 @@ public final class BluetoothLeScanner { private final IBluetoothManager mBluetoothManager; private final Handler mHandler; + private BluetoothAdapter mBluetoothAdapter; private final Map mLeScanClients; /** @@ -61,6 +62,7 @@ public final class BluetoothLeScanner { */ public BluetoothLeScanner(IBluetoothManager bluetoothManager) { mBluetoothManager = bluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap(); } @@ -95,6 +97,11 @@ public final class BluetoothLeScanner { postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE); return; } + if (!isSettingsConfigAllowedForScan(settings)) { + postCallbackError(callback, + ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); + return; + } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, callback); try { @@ -424,4 +431,19 @@ public final class BluetoothLeScanner { } }); } + + private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { + boolean ret = true; + int callbackType; + + callbackType = settings.getCallbackType(); + if (((callbackType == ScanSettings.CALLBACK_TYPE_ON_LOST) || + (callbackType == ScanSettings.CALLBACK_TYPE_ON_FOUND) || + (callbackType == ScanSettings.CALLBACK_TYPE_ON_UPDATE && + settings.getReportDelayNanos() > 0) && + (!mBluetoothAdapter.isOffloadedFilteringSupported()))) { + ret = false; + } + return ret; + } } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 50ebf5095bf..f5d7a9a995f 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -40,6 +40,11 @@ public abstract class ScanCallback { */ public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4; + /** + * Fails to start power optimized scan as this feature is not supported. + */ + public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 5; + /** * Callback when a BLE advertisement is found. * -- GitLab From d1e941c77cfdb59efac6040e52e6afde4c54068c Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 30 Jun 2014 11:37:05 -0700 Subject: [PATCH 0370/1408] LE: Un-hide new congestion control and MTU APIs Change-Id: I5ce395481620dd7ab611b1ab0eaeb5e29c4f13fa --- framework/java/android/bluetooth/BluetoothGatt.java | 5 +---- framework/java/android/bluetooth/BluetoothGattCallback.java | 2 -- .../java/android/bluetooth/BluetoothGattServerCallback.java | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 4255cd46ecb..8b7b0af9f78 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -86,9 +86,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; - /** A remote device connection is congested. - * @hide - */ + /** A remote device connection is congested. */ public static final int GATT_CONNECTION_CONGESTED = 0x8f; /** A GATT operation failed, errors other than the above */ @@ -1263,7 +1261,6 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return true, if the new MTU value has been requested successfully - * @hide */ public boolean configureMTU(int mtu) { if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index b5e60f2c981..5817d6844ae 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -149,7 +149,6 @@ public abstract class BluetoothGattCallback { * @param gatt GATT client invoked {@link BluetoothGatt#configureMTU} * @param mtu The new MTU size * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully - * @hide */ public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { } @@ -164,7 +163,6 @@ public abstract class BluetoothGattCallback { * * @param gatt The GATT client associated with the remote device * @param congested true, if the connection is currently congested - * @hide */ public void onConnectionCongested(BluetoothGatt gatt, boolean congested) { } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 4fbeb46735f..3a1b38ee6b3 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -142,7 +142,6 @@ public abstract class BluetoothGattServerCallback { * * @param device The remote device the notification has been sent to * @param status 0 if the operation was successful - * @hide */ public void onNotificationSent(BluetoothDevice device, int status) { } @@ -157,7 +156,6 @@ public abstract class BluetoothGattServerCallback { * * @param device The remote device that triggered the congestion state change * @param congested true, if the connection is currently congested - * @hide */ public void onConnectionCongested(BluetoothDevice device, boolean congested) { } -- GitLab From 5daedc1e0c3eab37a8dae3dc4ce739e6cafcfdc6 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 9 Jul 2014 12:51:59 -0700 Subject: [PATCH 0371/1408] OnFound and Onlost callback integration Change-Id: I23473b18484f041c4dd808c85bb92545a77e20c2 --- .../android/bluetooth/BluetoothAdapter.java | 6 ++++++ .../java/android/bluetooth/BluetoothGatt.java | 9 +++++++++ .../bluetooth/IBluetoothGattCallback.aidl | 2 ++ .../bluetooth/le/BluetoothLeAdvertiser.java | 6 ++++++ .../bluetooth/le/BluetoothLeScanner.java | 19 +++++++++++++++++++ .../android/bluetooth/le/ScanCallback.java | 2 -- .../android/bluetooth/le/ScanSettings.java | 3 --- 7 files changed, 42 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 35e50500670..4bc9cfc6470 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2132,5 +2132,11 @@ public final class BluetoothAdapter { public void onBatchScanResults(List results) { // no op } + + @Override + public void onFoundOrLost(boolean onFound, String address,int rssi, + byte[] advData) { + // no op + } } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 8b7b0af9f78..a287b3c4925 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -631,6 +631,15 @@ public final class BluetoothGatt implements BluetoothProfile { public void onBatchScanResults(List results) { // no op } + + /** + * @hide + */ + @Override + public void onFoundOrLost(boolean onFound, String address, int rssi, + byte[] advData) { + // no op. + } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index c18d357ce41..af218eb26a9 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -68,4 +68,6 @@ oneway interface IBluetoothGattCallback { void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); void onConnectionCongested(in String address, in boolean congested); + void onFoundOrLost(in boolean onFound, in String address, in int rssi, + in byte[] advData); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 6b21ce25478..d395d4372f2 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -386,6 +386,12 @@ public final class BluetoothLeAdvertiser { public void onBatchScanResults(List results) { // no op } + + @Override + public void onFoundOrLost(boolean onFound, String address, int rssi, + byte[] advData) { + // no op + } } private void postCallbackFailure(final AdvertiseCallback callback, final int error) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 863282add9f..d5a4728a0f4 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -421,6 +421,25 @@ public final class BluetoothLeScanner { public void onConnectionCongested(String address, boolean congested) { // no op } + + @Override + public void onFoundOrLost(boolean onFound, String address, int rssi, + byte[] advData) { + if (DBG) { + Log.d(TAG, "onFoundOrLost() - Device=" + address); + } + // ToDo: Fix issue with underlying reporting from chipset + BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( + address); + long scanNanos = SystemClock.elapsedRealtimeNanos(); + ScanResult result = new ScanResult(device, advData, rssi, + scanNanos); + if (onFound) { + mScanCallback.onAdvertisementFound(result); + } else { + mScanCallback.onAdvertisementLost(result); + } + } } private void postCallbackError(final ScanCallback callback, final int errorCode) { diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index f5d7a9a995f..593f7f8ab3f 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -56,7 +56,6 @@ public abstract class ScanCallback { * Callback when the BLE advertisement is found for the first time. * * @param result The Bluetooth LE scan result when the onFound event is triggered. - * @hide */ public abstract void onAdvertisementFound(ScanResult result); @@ -65,7 +64,6 @@ public abstract class ScanCallback { * lost. * * @param result The Bluetooth scan result that was last found. - * @hide */ public abstract void onAdvertisementLost(ScanResult result); diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 3bb39b33826..7c0f9e12f79 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -44,13 +44,10 @@ public final class ScanSettings implements Parcelable { public static final int CALLBACK_TYPE_ON_UPDATE = 0; /** * Callback when a bluetooth advertisement is found for the first time. - * @hide */ public static final int CALLBACK_TYPE_ON_FOUND = 1; /** * Callback when a bluetooth advertisement is found for the first time, then lost. - * - * @hide */ public static final int CALLBACK_TYPE_ON_LOST = 2; -- GitLab From 0649e964a14ffea79689a81fdcb5546ee6ad4553 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 9 Jul 2014 14:23:24 -0700 Subject: [PATCH 0372/1408] Unhide the Bluetooth(BLE) offload capability apis Change-Id: Ice3f4f5ff4b8318bf6afe7021b253fe9ea4661d3 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4bc9cfc6470..be145044490 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1031,7 +1031,6 @@ public final class BluetoothAdapter { /** * Return true if the multi advertisement is supported by the chipset * - * @hide * @return true if Multiple Advertisement feature is supported */ public boolean isMultipleAdvertisementSupported() { @@ -1047,7 +1046,6 @@ public final class BluetoothAdapter { /** * Return true if offloaded filters are supported * - * @hide * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { @@ -1063,7 +1061,6 @@ public final class BluetoothAdapter { /** * Return true if offloaded scan batching is supported * - * @hide * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { -- GitLab From 7faed2f183c09307b398c22e1b92ac52f1d93a7e Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 9 Jul 2014 14:03:42 -0700 Subject: [PATCH 0373/1408] Unhide Bluetooth batch APIs. Deprecate BluetoothAdpater scan APIs. (1/2) Change-Id: Ib0c4ea6c8372a15473269660355fb5ccf4284457 --- .../android/bluetooth/BluetoothAdapter.java | 10 ++ .../android/bluetooth/IBluetoothGatt.aidl | 6 +- .../bluetooth/le/AdvertiseCallback.java | 50 ++++---- ...ertisementData.aidl => AdvertiseData.aidl} | 2 +- ...ertisementData.java => AdvertiseData.java} | 69 +++++------ .../bluetooth/le/AdvertiseSettings.java | 108 ++++++++++-------- .../bluetooth/le/BluetoothLeAdvertiser.java | 72 ++++++------ .../bluetooth/le/BluetoothLeScanner.java | 72 ++++++------ .../android/bluetooth/le/ScanCallback.java | 49 +++----- .../java/android/bluetooth/le/ScanFilter.java | 82 ++++++------- .../java/android/bluetooth/le/ScanRecord.java | 23 ++-- .../android/bluetooth/le/ScanSettings.java | 93 ++++++++------- .../android/bluetooth/le/ScanFilterTest.java | 12 +- .../android/bluetooth/le/ScanRecordTest.java | 2 +- 14 files changed, 336 insertions(+), 314 deletions(-) rename framework/java/android/bluetooth/le/{AdvertisementData.aidl => AdvertiseData.aidl} (95%) rename framework/java/android/bluetooth/le/{AdvertisementData.java => AdvertiseData.java} (83%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index be145044490..97e3fc510d2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,7 +20,9 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.Handler; import android.os.IBinder; @@ -1734,7 +1736,10 @@ public final class BluetoothAdapter { * * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully + * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} + * instead. */ + @Deprecated public boolean startLeScan(LeScanCallback callback) { return startLeScan(null, callback); } @@ -1751,7 +1756,10 @@ public final class BluetoothAdapter { * @param serviceUuids Array of services to look for * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully + * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} + * instead. */ + @Deprecated public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) { if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); @@ -1794,7 +1802,9 @@ public final class BluetoothAdapter { * * @param callback used to identify which scan to stop * must be the same handle used to start the scan + * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ + @Deprecated public void stopLeScan(LeScanCallback callback) { if (DBG) Log.d(TAG, "stopLeScan()"); GattCallbackWrapper wrapper; diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 5ed10a67c19..0f0eee67faf 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -18,7 +18,7 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.AdvertisementData; +import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; import android.os.ParcelUuid; @@ -40,8 +40,8 @@ interface IBluetoothGatt { void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, - in AdvertisementData advertiseData, - in AdvertisementData scanResponse, + in AdvertiseData advertiseData, + in AdvertiseData scanResponse, in AdvertiseSettings settings); void stopMultiAdvertising(in int appIf); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java index 1d26ef93933..2afbadf47ec 100644 --- a/framework/java/android/bluetooth/le/AdvertiseCallback.java +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -17,64 +17,58 @@ package android.bluetooth.le; /** - * Callback of Bluetooth LE advertising, which is used to deliver advertising operation status. + * Bluetooth LE advertising callbacks, used to deliver advertising operation status. */ public abstract class AdvertiseCallback { /** - * The operation is success. - * + * The requested operation was successful. * @hide */ - public static final int SUCCESS = 0; + public static final int ADVERTISE_SUCCESS = 0; + /** - * Fails to start advertising as the advertisement data contains services that are not added to - * the local bluetooth GATT server. + * Failed to start advertising as the advertisement data contains services that are not + * added to the local Bluetooth GATT server. */ public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1; + /** - * Fails to start advertising as system runs out of quota for advertisers. + * Failed to start advertising because no advertising instance is available. */ public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; /** - * Fails to start advertising as the advertising is already started. + * Failed to start advertising as the advertising is already started. */ public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; - /** - * Fails to stop advertising as the advertising is not started. - */ - public static final int ADVERTISE_FAILED_NOT_STARTED = 4; /** - * Operation fails due to bluetooth controller failure. - */ - public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5; - - /** - * Operation fails due to GATT service failure. - * @hide + * Operation failed due to an internal error. */ - public static final int ADVERTISE_FAILED_GATT_SERVICE_FAILURE = 6; + public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; /** - * Operation fails as this feature is not supported + * This feature is not supported on this platform. */ - public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 7; + public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; /** - * Callback when advertising operation succeeds. + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertising} indicating + * that the advertising has been started successfully. * * @param settingsInEffect The actual settings used for advertising, which may be different from - * what the app asks. + * what has been requested. */ - public abstract void onSuccess(AdvertiseSettings settingsInEffect); + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + } /** - * Callback when advertising operation fails. + * Callback when advertising could not be started. * - * @param errorCode Error code for failures. + * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for */ - public abstract void onFailure(int errorCode); + public void onStartFailure(int errorCode) { + } } diff --git a/framework/java/android/bluetooth/le/AdvertisementData.aidl b/framework/java/android/bluetooth/le/AdvertiseData.aidl similarity index 95% rename from framework/java/android/bluetooth/le/AdvertisementData.aidl rename to framework/java/android/bluetooth/le/AdvertiseData.aidl index 3da1321f6c5..bcbf2243e18 100644 --- a/framework/java/android/bluetooth/le/AdvertisementData.aidl +++ b/framework/java/android/bluetooth/le/AdvertiseData.aidl @@ -16,4 +16,4 @@ package android.bluetooth.le; -parcelable AdvertisementData; \ No newline at end of file +parcelable AdvertiseData; diff --git a/framework/java/android/bluetooth/le/AdvertisementData.java b/framework/java/android/bluetooth/le/AdvertiseData.java similarity index 83% rename from framework/java/android/bluetooth/le/AdvertisementData.java rename to framework/java/android/bluetooth/le/AdvertiseData.java index c5872047be1..d0f52b2e14c 100644 --- a/framework/java/android/bluetooth/le/AdvertisementData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -27,16 +27,16 @@ import java.util.Arrays; import java.util.List; /** - * Advertisement data packet for Bluetooth LE advertising. This represents the data to be - * broadcasted in Bluetooth LE advertising as well as the scan response for active scan. - *

        - * Use {@link AdvertisementData.Builder} to create an instance of {@link AdvertisementData} to be + * Advertisement data packet container for Bluetooth LE advertising. This represents the data to be + * advertised as well as the scan response data for active scans. + * + *

        Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be * advertised. * * @see BluetoothLeAdvertiser * @see ScanRecord */ -public final class AdvertisementData implements Parcelable { +public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; @@ -52,7 +52,7 @@ public final class AdvertisementData implements Parcelable { private boolean mIncludeTxPowerLevel; - private AdvertisementData(List serviceUuids, + private AdvertiseData(List serviceUuids, ParcelUuid serviceDataUuid, byte[] serviceData, int manufacturerId, byte[] manufacturerSpecificData, boolean includeTxPowerLevel) { @@ -65,8 +65,8 @@ public final class AdvertisementData implements Parcelable { } /** - * Returns a list of service uuids within the advertisement that are used to identify the - * bluetooth GATT services. + * Returns a list of service UUIDs within the advertisement that are used to identify the + * Bluetooth GATT services. */ public List getServiceUuids() { return mServiceUuids; @@ -89,15 +89,14 @@ public final class AdvertisementData implements Parcelable { } /** - * Returns a 16 bit uuid of the service that the service data is associated with. + * Returns a 16-bit UUID of the service that the service data is associated with. */ public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; } /** - * Returns service data. The first two bytes should be a 16 bit service uuid associated with the - * service data. + * Returns service data. */ public byte[] getServiceData() { return mServiceData; @@ -112,7 +111,7 @@ public final class AdvertisementData implements Parcelable { @Override public String toString() { - return "AdvertisementData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) @@ -156,15 +155,15 @@ public final class AdvertisementData implements Parcelable { dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); } - public static final Parcelable.Creator CREATOR = - new Creator() { + public static final Parcelable.Creator CREATOR = + new Creator() { @Override - public AdvertisementData[] newArray(int size) { - return new AdvertisementData[size]; + public AdvertiseData[] newArray(int size) { + return new AdvertiseData[size]; } @Override - public AdvertisementData createFromParcel(Parcel in) { + public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); if (in.readInt() > 0) { List uuids = new ArrayList(); @@ -194,7 +193,7 @@ public final class AdvertisementData implements Parcelable { }; /** - * Builder for {@link AdvertisementData}. + * Builder for {@link AdvertiseData}. */ public static final class Builder { private static final int MAX_ADVERTISING_DATA_BYTES = 31; @@ -215,11 +214,13 @@ public final class AdvertisementData implements Parcelable { private byte[] mServiceData; /** - * Set the service uuids. Note the corresponding bluetooth Gatt services need to be already - * added on the device before start BLE advertising. + * Set the service UUIDs. + * + *

        Note: The corresponding Bluetooth Gatt services need to already + * be added on the device (using {@link android.bluetooth.BluetoothGattServer#addService}) prior + * to advertising them. * - * @param serviceUuids Service uuids to be advertised, could be 16-bit, 32-bit or 128-bit - * uuids. + * @param serviceUuids Service UUIDs to be advertised. * @throws IllegalArgumentException If the {@code serviceUuids} are null. */ public Builder setServiceUuids(List serviceUuids) { @@ -233,9 +234,8 @@ public final class AdvertisementData implements Parcelable { /** * Add service data to advertisement. * - * @param serviceDataUuid A 16 bit uuid of the service data - * @param serviceData Service data - the first two bytes of the service data are the service - * data uuid. + * @param serviceDataUuid 16-bit UUID of the service the data is associated with + * @param serviceData Service data * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is * empty. */ @@ -250,13 +250,14 @@ public final class AdvertisementData implements Parcelable { } /** - * Set manufacturer id and data. See assigned - * manufacturer identifies for the existing company identifiers. + * Set manufacturer specific data. + * + *

        Please refer to the Bluetooth Assigned Numbers document provided by the + * Bluetooth SIG for a list of existing + * company identifiers. * - * @param manufacturerId Manufacturer id assigned by Bluetooth SIG. - * @param manufacturerSpecificData Manufacturer specific data - the first two bytes of the - * manufacturer specific data are the manufacturer id. + * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. + * @param manufacturerSpecificData Manufacturer specific data * @throws IllegalArgumentException If the {@code manufacturerId} is negative or * {@code manufacturerSpecificData} is null. */ @@ -282,16 +283,16 @@ public final class AdvertisementData implements Parcelable { } /** - * Build the {@link AdvertisementData}. + * Build the {@link AdvertiseData}. * * @throws IllegalArgumentException If the data size is larger than 31 bytes. */ - public AdvertisementData build() { + public AdvertiseData build() { if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { throw new IllegalArgumentException( "advertisement data size is larger than 31 bytes"); } - return new AdvertisementData(mServiceUuids, + return new AdvertiseData(mServiceUuids, mServiceDataUuid, mServiceData, mManufacturerId, mManufacturerSpecificData, mIncludeTxPowerLevel); diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 87d03467ed2..02b4a5b0941 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -21,7 +21,8 @@ import android.os.Parcelable; /** * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each - * individual advertisement. Use {@link AdvertiseSettings.Builder} to create an instance. + * Bluetooth LE advertisement instance. Use {@link AdvertiseSettings.Builder} to create an + * instance of this class. */ public final class AdvertiseSettings implements Parcelable { /** @@ -29,68 +30,64 @@ public final class AdvertiseSettings implements Parcelable { * advertising mode as it consumes the least power. */ public static final int ADVERTISE_MODE_LOW_POWER = 0; + /** * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising * frequency and power consumption. */ public static final int ADVERTISE_MODE_BALANCED = 1; + /** * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power - * consumption and should not be used for background continuous advertising. + * consumption and should not be used for continuous background advertising. */ public static final int ADVERTISE_MODE_LOW_LATENCY = 2; /** - * Advertise using the lowest transmission(tx) power level. An app can use low transmission - * power to restrict the visibility range of its advertising packet. + * Advertise using the lowest transmission (TX) power level. Low transmission power can be used + * to restrict the visibility range of advertising packets. */ public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; + /** - * Advertise using low tx power level. + * Advertise using low TX power level. */ public static final int ADVERTISE_TX_POWER_LOW = 1; + /** - * Advertise using medium tx power level. + * Advertise using medium TX power level. */ public static final int ADVERTISE_TX_POWER_MEDIUM = 2; + /** - * Advertise using high tx power level. This is corresponding to largest visibility range of the + * Advertise using high TX power level. This corresponds to largest visibility range of the * advertising packet. */ public static final int ADVERTISE_TX_POWER_HIGH = 3; /** - * Non-connectable undirected advertising event, as defined in Bluetooth Specification V4.1 - * vol6, part B, section 4.4.2 - Advertising state. - */ - public static final int ADVERTISE_TYPE_NON_CONNECTABLE = 0; - /** - * Scannable undirected advertise type, as defined in same spec mentioned above. This event type - * allows a scanner to send a scan request asking additional information about the advertiser. - */ - public static final int ADVERTISE_TYPE_SCANNABLE = 1; - /** - * Connectable undirected advertising type, as defined in same spec mentioned above. This event - * type allows a scanner to send scan request asking additional information about the - * advertiser. It also allows an initiator to send a connect request for connection. + * The maximimum limited advertisement duration as specified by the Bluetooth SIG */ - public static final int ADVERTISE_TYPE_CONNECTABLE = 2; + private static final int LIMITED_ADVERTISING_MAX_DURATION = 180; private final int mAdvertiseMode; private final int mAdvertiseTxPowerLevel; - private final int mAdvertiseEventType; + private final int mAdvertiseTimeoutSeconds; + private final boolean mAdvertiseConnectable; private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel, - int advertiseEventType) { + boolean advertiseConnectable, int advertiseTimeout) { mAdvertiseMode = advertiseMode; mAdvertiseTxPowerLevel = advertiseTxPowerLevel; - mAdvertiseEventType = advertiseEventType; + mAdvertiseConnectable = advertiseConnectable; + mAdvertiseTimeoutSeconds = advertiseTimeout; } private AdvertiseSettings(Parcel in) { mAdvertiseMode = in.readInt(); mAdvertiseTxPowerLevel = in.readInt(); - mAdvertiseEventType = in.readInt(); + mAdvertiseConnectable = in.readInt() != 0 ? true : false; + mAdvertiseTimeoutSeconds = in.readInt(); } /** @@ -101,23 +98,32 @@ public final class AdvertiseSettings implements Parcelable { } /** - * Returns the tx power level for advertising. + * Returns the TX power level for advertising. */ public int getTxPowerLevel() { return mAdvertiseTxPowerLevel; } /** - * Returns the advertise event type. + * Returns whether the advertisement will indicate connectable. */ - public int getType() { - return mAdvertiseEventType; + public boolean getIsConnectable() { + return mAdvertiseConnectable; + } + + /** + * Returns the advertising time limit in seconds. + */ + public int getTimeout() { + return mAdvertiseTimeoutSeconds; } @Override public String toString() { - return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" - + mAdvertiseTxPowerLevel + ", mAdvertiseEventType=" + mAdvertiseEventType + "]"; + return "Settings [mAdvertiseMode=" + mAdvertiseMode + + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel + + ", mAdvertiseConnectable=" + mAdvertiseConnectable + + ", mAdvertiseTimeoutSeconds=" + mAdvertiseTimeoutSeconds + "]"; } @Override @@ -129,7 +135,8 @@ public final class AdvertiseSettings implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mAdvertiseMode); dest.writeInt(mAdvertiseTxPowerLevel); - dest.writeInt(mAdvertiseEventType); + dest.writeInt(mAdvertiseConnectable ? 1 : 0); + dest.writeInt(mAdvertiseTimeoutSeconds); } public static final Parcelable.Creator CREATOR = @@ -151,7 +158,8 @@ public final class AdvertiseSettings implements Parcelable { public static final class Builder { private int mMode = ADVERTISE_MODE_LOW_POWER; private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; - private int mType = ADVERTISE_TYPE_NON_CONNECTABLE; + private int mTimeoutSeconds = 0; + private boolean mConnectable = true; /** * Set advertise mode to control the advertising power and latency. @@ -172,7 +180,7 @@ public final class AdvertiseSettings implements Parcelable { } /** - * Set advertise tx power level to control the transmission power level for the advertising. + * Set advertise TX power level to control the transmission power level for the advertising. * * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, @@ -191,20 +199,28 @@ public final class AdvertiseSettings implements Parcelable { } /** - * Set advertise type to control the event type of advertising. + * Set whether the advertisement type should be connectable or non-connectable. * - * @param type Bluetooth LE Advertising type, can be either - * {@link AdvertiseSettings#ADVERTISE_TYPE_NON_CONNECTABLE}, - * {@link AdvertiseSettings#ADVERTISE_TYPE_SCANNABLE} or - * {@link AdvertiseSettings#ADVERTISE_TYPE_CONNECTABLE}. - * @throws IllegalArgumentException If the {@code type} is invalid. + * @param isConnectable Controls whether the advertisment type will be connectable (true) + * or non-connectable (false). + */ + public Builder setIsConnectable(boolean isConnectable) { + mConnectable = isConnectable; + return this; + } + + /** + * Limit advertising to a given amount of time. + * @param timeoutSeconds Advertising time limit. May not exceed 180 seconds. + * A value of 0 will disable the time limit. + * @throws IllegalArgumentException If the provided timeout is over 180s. */ - public Builder setType(int type) { - if (type < ADVERTISE_TYPE_NON_CONNECTABLE - || type > ADVERTISE_TYPE_CONNECTABLE) { - throw new IllegalArgumentException("unknown advertise type " + type); + public Builder setTimeout(int timeoutSeconds) { + if (timeoutSeconds < 0 || timeoutSeconds > LIMITED_ADVERTISING_MAX_DURATION) { + throw new IllegalArgumentException("timeoutSeconds invalid (must be 0-" + + LIMITED_ADVERTISING_MAX_DURATION + " seconds)"); } - mType = type; + mTimeoutSeconds = timeoutSeconds; return this; } @@ -212,7 +228,7 @@ public final class AdvertiseSettings implements Parcelable { * Build the {@link AdvertiseSettings} object. */ public AdvertiseSettings build() { - return new AdvertiseSettings(mMode, mTxPowerLevel, mType); + return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutSeconds); } } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index d395d4372f2..e232512f8eb 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -33,17 +33,17 @@ import java.util.Map; import java.util.UUID; /** - * This class provides a way to perform Bluetooth LE advertise operations, such as start and stop - * advertising. An advertiser can broadcast up to 31 bytes of advertisement data represented by - * {@link AdvertisementData}. + * This class provides a way to perform Bluetooth LE advertise operations, such as starting and + * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data + * represented by {@link AdvertiseData}. *

        * To get an instance of {@link BluetoothLeAdvertiser}, call the * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. *

        - * Note most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * Note: Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} * permission. * - * @see AdvertisementData + * @see AdvertiseData */ public final class BluetoothLeAdvertiser { @@ -57,8 +57,6 @@ public final class BluetoothLeAdvertiser { /** * Use BluetoothAdapter.getLeAdvertiser() instead. - * - * @param bluetoothManager * @hide */ public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { @@ -68,8 +66,8 @@ public final class BluetoothLeAdvertiser { } /** - * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the - * operation succeeds. Returns immediately, the operation status are delivered through + * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be + * broadcasted. Returns immediately, the operation status is delivered through * {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. @@ -79,15 +77,15 @@ public final class BluetoothLeAdvertiser { * @param callback Callback for advertising status. */ public void startAdvertising(AdvertiseSettings settings, - AdvertisementData advertiseData, final AdvertiseCallback callback) { + AdvertiseData advertiseData, final AdvertiseCallback callback) { startAdvertising(settings, advertiseData, null, callback); } /** - * Start Bluetooth LE Advertising. The {@code advertiseData} would be broadcasted after the - * operation succeeds. The {@code scanResponse} would be returned when the scanning device sends - * active scan request. Method returns immediately, the operation status are delivered through - * {@code callback}. + * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the + * operation succeeds. The {@code scanResponse} is returned when a scanning device sends + * an active scan request. This method returns immediately, the operation status is + * delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * @@ -97,7 +95,7 @@ public final class BluetoothLeAdvertiser { * @param callback Callback for advertising status. */ public void startAdvertising(AdvertiseSettings settings, - AdvertisementData advertiseData, AdvertisementData scanResponse, + AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -110,8 +108,8 @@ public final class BluetoothLeAdvertiser { try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { - Log.e(TAG, "failed to get bluetooth gatt - ", e); - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_CONTROLLER_FAILURE); + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); return; } if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) { @@ -127,7 +125,7 @@ public final class BluetoothLeAdvertiser { mLeAdvertisers.put(callback, wrapper); } } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); + Log.e(TAG, "Failed to stop advertising", e); } } @@ -137,31 +135,24 @@ public final class BluetoothLeAdvertiser { *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * - * @param callback {@link AdvertiseCallback} for delivering stopping advertising status. + * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. */ public void stopAdvertising(final AdvertiseCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); - if (wrapper == null) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_NOT_STARTED); - return; - } + if (wrapper == null) return; + try { IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt(); - if (gatt == null) { - postCallbackFailure(callback, - AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE); - } - gatt.stopMultiAdvertising(wrapper.mLeHandle); + if (gatt != null) gatt.stopMultiAdvertising(wrapper.mLeHandle); + if (wrapper.advertiseStopped()) { mLeAdvertisers.remove(callback); } } catch (RemoteException e) { - Log.e(TAG, "failed to stop advertising", e); - postCallbackFailure(callback, - AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE); + Log.e(TAG, "Failed to stop advertising", e); } } @@ -171,8 +162,8 @@ public final class BluetoothLeAdvertiser { private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; - private final AdvertisementData mAdvertisement; - private final AdvertisementData mScanResponse; + private final AdvertiseData mAdvertisement; + private final AdvertiseData mScanResponse; private final AdvertiseSettings mSettings; private final IBluetoothGatt mBluetoothGatt; @@ -183,7 +174,7 @@ public final class BluetoothLeAdvertiser { private boolean isAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, - AdvertisementData advertiseData, AdvertisementData scanResponse, + AdvertiseData advertiseData, AdvertiseData scanResponse, AdvertiseSettings settings, IBluetoothGatt bluetoothGatt) { mAdvertiseCallback = advertiseCallback; @@ -347,8 +338,12 @@ public final class BluetoothLeAdvertiser { @Override public void onMultiAdvertiseCallback(int status) { + // TODO: This logic needs to be re-visited to account + // for whether the scan has actually been started + // or not. Toggling the isAdvertising does not seem + // correct. synchronized (this) { - if (status == 0) { + if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { isAdvertising = !isAdvertising; if (!isAdvertising) { try { @@ -357,10 +352,11 @@ public final class BluetoothLeAdvertiser { } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } + } else { + mAdvertiseCallback.onStartSuccess(null); } - mAdvertiseCallback.onSuccess(null); } else { - mAdvertiseCallback.onFailure(status); + if (!isAdvertising) mAdvertiseCallback.onStartFailure(status); } notifyAll(); } @@ -398,7 +394,7 @@ public final class BluetoothLeAdvertiser { mHandler.post(new Runnable() { @Override public void run() { - callback.onFailure(error); + callback.onStartFailure(error); } }); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index d5a4728a0f4..7e87edc1279 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -36,14 +36,14 @@ import java.util.UUID; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of BLE devices using {@link ScanFilter}. It can also - * request different types of callbacks for delivering the result. + * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. + * It can also request different types of callbacks for delivering the result. *

        * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of * {@link BluetoothLeScanner}. *

        - * Note most of the scan methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. + * Note: Most of the scan methods here require + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @see ScanFilter */ @@ -58,6 +58,8 @@ public final class BluetoothLeScanner { private final Map mLeScanClients; /** + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management * @hide */ public BluetoothLeScanner(IBluetoothManager bluetoothManager) { @@ -67,14 +69,30 @@ public final class BluetoothLeScanner { mLeScanClients = new HashMap(); } + /** + * Start Bluetooth LE scan with default parameters and no filters. + * The scan results will be delivered through {@code callback}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param callback Callback used to deliver scan results. + * @throws IllegalArgumentException If {@code callback} is null. + */ + public void startScan(final ScanCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback is null"); + } + this.startScan(null, new ScanSettings.Builder().build(), callback); + } + /** * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param filters {@link ScanFilter}s for finding exact BLE devices. - * @param settings Settings for ble scan. - * @param callback Callback when scan results are delivered. + * @param settings Settings for the scan. + * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. */ public void startScan(List filters, ScanSettings settings, @@ -94,7 +112,7 @@ public final class BluetoothLeScanner { gatt = null; } if (gatt == null) { - postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE); + postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); return; } if (!isSettingsConfigAllowedForScan(settings)) { @@ -116,7 +134,7 @@ public final class BluetoothLeScanner { } } catch (RemoteException e) { Log.e(TAG, "GATT service exception when starting scan", e); - postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE); + postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); } } } @@ -138,16 +156,6 @@ public final class BluetoothLeScanner { } } - /** - * Returns available storage size for batch scan results. It's recommended not to use batch scan - * if available storage size is small (less than 1k bytes, for instance). - * - * @hide TODO: unhide when batching is supported in stack. - */ - public int getAvailableBatchStorageSizeBytes() { - throw new UnsupportedOperationException("not impelemented"); - } - /** * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data @@ -155,8 +163,6 @@ public final class BluetoothLeScanner { * * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one * used to start scan. - * - * @hide */ public void flushPendingScanResults(ScanCallback callback) { if (callback == null) { @@ -302,7 +308,7 @@ public final class BluetoothLeScanner { handler.post(new Runnable() { @Override public void run() { - mScanCallback.onAdvertisementUpdate(result); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result); } }); @@ -435,9 +441,9 @@ public final class BluetoothLeScanner { ScanResult result = new ScanResult(device, advData, rssi, scanNanos); if (onFound) { - mScanCallback.onAdvertisementFound(result); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result); } else { - mScanCallback.onAdvertisementLost(result); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result); } } } @@ -452,17 +458,13 @@ public final class BluetoothLeScanner { } private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { - boolean ret = true; - int callbackType; - - callbackType = settings.getCallbackType(); - if (((callbackType == ScanSettings.CALLBACK_TYPE_ON_LOST) || - (callbackType == ScanSettings.CALLBACK_TYPE_ON_FOUND) || - (callbackType == ScanSettings.CALLBACK_TYPE_ON_UPDATE && - settings.getReportDelayNanos() > 0) && - (!mBluetoothAdapter.isOffloadedFilteringSupported()))) { - ret = false; - } - return ret; + final int callbackType = settings.getCallbackType(); + if (( callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES + || (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES + && settings.getReportDelaySeconds() > 0)) + && !mBluetoothAdapter.isOffloadedFilteringSupported()) { + return false; + } + return true; } } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 593f7f8ab3f..b4c1e1769cb 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -19,64 +19,53 @@ package android.bluetooth.le; import java.util.List; /** - * Callback of Bluetooth LE scans. The results of the scans will be delivered through the callbacks. + * Bluetooth LE scan callbacks. + * Scan results are reported using these callbacks. + * + * {@see BluetoothLeScanner#startScan} */ public abstract class ScanCallback { - /** * Fails to start scan as BLE scan with the same settings is already started by the app. */ public static final int SCAN_FAILED_ALREADY_STARTED = 1; + /** * Fails to start scan as app cannot be registered. */ public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2; + /** - * Fails to start scan due to gatt service failure. - */ - public static final int SCAN_FAILED_GATT_SERVICE_FAILURE = 3; - /** - * Fails to start scan due to controller failure. + * Fails to start scan due an internal error */ - public static final int SCAN_FAILED_CONTROLLER_FAILURE = 4; + public static final int SCAN_FAILED_INTERNAL_ERROR = 3; /** * Fails to start power optimized scan as this feature is not supported. */ - public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 5; + public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4; /** - * Callback when a BLE advertisement is found. + * Callback when a BLE advertisement has been found. * + * @param callbackType Determines if this callback was triggered because of first match, + * a lost match indication or a regular scan result. * @param result A Bluetooth LE scan result. */ - public abstract void onAdvertisementUpdate(ScanResult result); - - /** - * Callback when the BLE advertisement is found for the first time. - * - * @param result The Bluetooth LE scan result when the onFound event is triggered. - */ - public abstract void onAdvertisementFound(ScanResult result); - - /** - * Callback when the BLE advertisement was lost. Note a device has to be "found" before it's - * lost. - * - * @param result The Bluetooth scan result that was last found. - */ - public abstract void onAdvertisementLost(ScanResult result); + public void onScanResult(int callbackType, ScanResult result) { + } /** * Callback when batch results are delivered. * * @param results List of scan results that are previously scanned. - * @hide */ - public abstract void onBatchScanResults(List results); + public void onBatchScanResults(List results) { + } /** - * Callback when scan failed. + * Callback when scan could not be started. */ - public abstract void onScanFailed(int errorCode); + public void onScanFailed(int errorCode) { + } } diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index c2e316b78a6..e8a91b81a41 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -45,10 +45,10 @@ import java.util.UUID; public final class ScanFilter implements Parcelable { @Nullable - private final String mLocalName; + private final String mDeviceName; @Nullable - private final String mMacAddress; + private final String mDeviceAddress; @Nullable private final ParcelUuid mServiceUuid; @@ -69,14 +69,14 @@ public final class ScanFilter implements Parcelable { private final int mMinRssi; private final int mMaxRssi; - private ScanFilter(String name, String macAddress, ParcelUuid uuid, + private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, int minRssi, int maxRssi) { - mLocalName = name; + mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; - mMacAddress = macAddress; + mDeviceAddress = deviceAddress; mServiceData = serviceData; mServiceDataMask = serviceDataMask; mManufacturerId = manufacturerId; @@ -93,13 +93,13 @@ public final class ScanFilter implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mLocalName == null ? 0 : 1); - if (mLocalName != null) { - dest.writeString(mLocalName); + dest.writeInt(mDeviceName == null ? 0 : 1); + if (mDeviceName != null) { + dest.writeString(mDeviceName); } - dest.writeInt(mMacAddress == null ? 0 : 1); - if (mMacAddress != null) { - dest.writeString(mMacAddress); + dest.writeInt(mDeviceAddress == null ? 0 : 1); + if (mDeviceAddress != null) { + dest.writeString(mDeviceAddress); } dest.writeInt(mServiceUuid == null ? 0 : 1); if (mServiceUuid != null) { @@ -145,10 +145,10 @@ public final class ScanFilter implements Parcelable { public ScanFilter createFromParcel(Parcel in) { Builder builder = new Builder(); if (in.readInt() == 1) { - builder.setName(in.readString()); + builder.setDeviceName(in.readString()); } if (in.readInt() == 1) { - builder.setMacAddress(in.readString()); + builder.setDeviceAddress(in.readString()); } if (in.readInt() == 1) { ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); @@ -196,11 +196,11 @@ public final class ScanFilter implements Parcelable { }; /** - * Returns the filter set the local name field of Bluetooth advertisement data. + * Returns the filter set the device name field of Bluetooth advertisement data. */ @Nullable - public String getLocalName() { - return mLocalName; + public String getDeviceName() { + return mDeviceName; } /** @@ -218,7 +218,7 @@ public final class ScanFilter implements Parcelable { @Nullable public String getDeviceAddress() { - return mMacAddress; + return mDeviceAddress; } @Nullable @@ -249,14 +249,14 @@ public final class ScanFilter implements Parcelable { } /** - * Returns minimum value of rssi for the scan filter. {@link Integer#MIN_VALUE} if not set. + * Returns minimum value of RSSI for the scan filter. {@link Integer#MIN_VALUE} if not set. */ public int getMinRssi() { return mMinRssi; } /** - * Returns maximum value of the rssi for the scan filter. {@link Integer#MAX_VALUE} if not set. + * Returns maximum value of the RSSI for the scan filter. {@link Integer#MAX_VALUE} if not set. */ public int getMaxRssi() { return mMaxRssi; @@ -272,7 +272,7 @@ public final class ScanFilter implements Parcelable { } BluetoothDevice device = scanResult.getDevice(); // Device match. - if (mMacAddress != null && (device == null || !mMacAddress.equals(device.getAddress()))) { + if (mDeviceAddress != null && (device == null || !mDeviceAddress.equals(device.getAddress()))) { return false; } @@ -286,13 +286,13 @@ public final class ScanFilter implements Parcelable { // Scan record is null but there exist filters on it. if (scanRecord == null - && (mLocalName != null || mServiceUuid != null || mManufacturerData != null + && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null || mServiceData != null)) { return false; } // Local name match. - if (mLocalName != null && !mLocalName.equals(scanRecord.getLocalName())) { + if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) { return false; } @@ -367,7 +367,7 @@ public final class ScanFilter implements Parcelable { @Override public String toString() { - return "BluetoothLeScanFilter [mLocalName=" + mLocalName + ", mMacAddress=" + mMacAddress + return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + mDeviceAddress + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId @@ -378,7 +378,7 @@ public final class ScanFilter implements Parcelable { @Override public int hashCode() { - return Objects.hash(mLocalName, mMacAddress, mManufacturerId, mManufacturerData, + return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, mManufacturerData, mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask, mServiceUuid, mServiceUuidMask); } @@ -392,8 +392,8 @@ public final class ScanFilter implements Parcelable { return false; } ScanFilter other = (ScanFilter) obj; - return Objects.equals(mLocalName, other.mLocalName) && - Objects.equals(mMacAddress, other.mMacAddress) && + return Objects.equals(mDeviceName, other.mDeviceName) && + Objects.equals(mDeviceAddress, other.mDeviceAddress) && mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && @@ -409,8 +409,8 @@ public final class ScanFilter implements Parcelable { */ public static final class Builder { - private String mLocalName; - private String mMacAddress; + private String mDeviceName; + private String mDeviceAddress; private ParcelUuid mServiceUuid; private ParcelUuid mUuidMask; @@ -426,26 +426,26 @@ public final class ScanFilter implements Parcelable { private int mMaxRssi = Integer.MAX_VALUE; /** - * Set filter on local name. + * Set filter on device name. */ - public Builder setName(String localName) { - mLocalName = localName; + public Builder setDeviceName(String deviceName) { + mDeviceName = deviceName; return this; } /** - * Set filter on device mac address. + * Set filter on device address. * - * @param macAddress The device mac address for the filter. It needs to be in the format of - * "01:02:03:AB:CD:EF". The mac address can be validated using + * @param deviceAddress The device Bluetooth address for the filter. It needs to be in + * the format of "01:02:03:AB:CD:EF". The device address can be validated using * {@link BluetoothAdapter#checkBluetoothAddress}. - * @throws IllegalArgumentException If the {@code macAddress} is invalid. + * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ - public Builder setMacAddress(String macAddress) { - if (macAddress != null && !BluetoothAdapter.checkBluetoothAddress(macAddress)) { - throw new IllegalArgumentException("invalid mac address " + macAddress); + public Builder setDeviceAddress(String deviceAddress) { + if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { + throw new IllegalArgumentException("invalid device address " + deviceAddress); } - mMacAddress = macAddress; + mDeviceAddress = deviceAddress; return this; } @@ -564,7 +564,7 @@ public final class ScanFilter implements Parcelable { } /** - * Set the desired rssi range for the filter. A scan result with rssi in the range of + * Set the desired RSSI range for the filter. A scan result with RSSI in the range of * [minRssi, maxRssi] will be consider as a match. */ public Builder setRssiRange(int minRssi, int maxRssi) { @@ -579,7 +579,7 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If the filter cannot be built. */ public ScanFilter build() { - return new ScanFilter(mLocalName, mMacAddress, + return new ScanFilter(mDeviceName, mDeviceAddress, mServiceUuid, mUuidMask, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index bd7304bca9e..fc46e765a3e 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -66,7 +66,7 @@ public final class ScanRecord { private final int mTxPowerLevel; // Local name of the Bluetooth LE device. - private final String mLocalName; + private final String mDeviceName; /** * Returns the advertising flags indicating the discoverable mode and capability of the device. @@ -77,7 +77,7 @@ public final class ScanRecord { } /** - * Returns a list of service uuids within the advertisement that are used to identify the + * Returns a list of service UUIDs within the advertisement that are used to identify the * bluetooth gatt services. */ public List getServiceUuids() { @@ -94,22 +94,21 @@ public final class ScanRecord { /** * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. The first 2 bytes of the data contain the company id. + * field. */ public byte[] getManufacturerSpecificData() { return mManufacturerSpecificData; } /** - * Returns a 16 bit uuid of the service that the service data is associated with. + * Returns a 16-bit UUID of the service that the service data is associated with. */ public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; } /** - * Returns service data. The first two bytes should be a 16 bit service uuid associated with the - * service data. + * Returns service data. */ public byte[] getServiceData() { return mServiceData; @@ -130,8 +129,8 @@ public final class ScanRecord { * Returns the local name of the BLE device. The is a UTF-8 encoded string. */ @Nullable - public String getLocalName() { - return mLocalName; + public String getDeviceName() { + return mDeviceName; } private ScanRecord(List serviceUuids, @@ -144,7 +143,7 @@ public final class ScanRecord { mManufacturerSpecificData = manufacturerSpecificData; mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; - mLocalName = localName; + mDeviceName = localName; mAdvertiseFlags = advertiseFlags; mTxPowerLevel = txPowerLevel; } @@ -155,7 +154,7 @@ public final class ScanRecord { + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) - + ", mTxPowerLevel=" + mTxPowerLevel + ", mLocalName=" + mLocalName + "]"; + + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } /** @@ -223,7 +222,7 @@ public final class ScanRecord { break; case DATA_TYPE_SERVICE_DATA: serviceData = extractBytes(scanRecord, currentPos, dataLength); - // The first two bytes of the service data are service data uuid. + // The first two bytes of the service data are service data UUID. int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); @@ -256,7 +255,7 @@ public final class ScanRecord { } } - // Parse service uuids. + // Parse service UUIDs. private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List serviceUuids) { while (dataLength > 0) { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 7c0f9e12f79..20977026d3b 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -16,54 +16,66 @@ package android.bluetooth.le; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; /** - * Settings for Bluetooth LE scan. + * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} + * to define the parameters for the scan. */ public final class ScanSettings implements Parcelable { /** - * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the - * least power. + * Perform Bluetooth LE scan in low power mode. + * This is the default scan mode as it consumes the least power. */ public static final int SCAN_MODE_LOW_POWER = 0; + /** * Perform Bluetooth LE scan in balanced power mode. + * Scan results are returned at a rate that provides a good trade-off between scan + * frequency and power consumption. */ public static final int SCAN_MODE_BALANCED = 1; + /** - * Scan using highest duty cycle. It's recommended only using this mode when the application is - * running in foreground. + * Scan using highest duty cycle. + * It's recommended to only use this mode when the application is running in the foreground. */ public static final int SCAN_MODE_LOW_LATENCY = 2; /** - * Callback each time when a bluetooth advertisement is found. + * Triggger a callback for every Bluetooth advertisement found that matches the + * filter criteria. If no filter is active, all advertisement packets are reported. */ - public static final int CALLBACK_TYPE_ON_UPDATE = 0; + public static final int CALLBACK_TYPE_ALL_MATCHES = 1; + /** - * Callback when a bluetooth advertisement is found for the first time. + * A result callback is only triggered for the first advertisement packet received that + * matches the filter criteria. */ - public static final int CALLBACK_TYPE_ON_FOUND = 1; + public static final int CALLBACK_TYPE_FIRST_MATCH = 2; + /** - * Callback when a bluetooth advertisement is found for the first time, then lost. + * Receive a callback when advertisements are no longer received from a device that has been + * previously reported by a first match callback. */ - public static final int CALLBACK_TYPE_ON_LOST = 2; + public static final int CALLBACK_TYPE_MATCH_LOST = 4; /** - * Full scan result which contains device mac address, rssi, advertising and scan response and - * scan timestamp. + * Request full scan results which contain the device, rssi, advertising data, scan response + * as well as the scan timestamp. */ public static final int SCAN_RESULT_TYPE_FULL = 0; + /** - * Truncated scan result which contains device mac address, rssi and scan timestamp. Note it's - * possible for an app to get more scan results that it asks if there are multiple apps using - * this type. TODO: decide whether we could unhide this setting. - * + * Request abbreviated scan results which contain the device, rssi and scan timestamp. + *

        Note: It is possible for an application to get more scan results than + * it asked for, if there are multiple apps using this type. * @hide */ - public static final int SCAN_RESULT_TYPE_TRUNCATED = 1; + @SystemApi + public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; // Bluetooth LE scan mode. private int mScanMode; @@ -75,7 +87,7 @@ public final class ScanSettings implements Parcelable { private int mScanResultType; // Time of delay for reporting the scan result - private long mReportDelayNanos; + private long mReportDelaySeconds; public int getScanMode() { return mScanMode; @@ -92,23 +104,23 @@ public final class ScanSettings implements Parcelable { /** * Returns report delay timestamp based on the device clock. */ - public long getReportDelayNanos() { - return mReportDelayNanos; + public long getReportDelaySeconds() { + return mReportDelaySeconds; } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayNanos) { + long reportDelaySeconds) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; - mReportDelayNanos = reportDelayNanos; + mReportDelaySeconds = reportDelaySeconds; } private ScanSettings(Parcel in) { mScanMode = in.readInt(); mCallbackType = in.readInt(); mScanResultType = in.readInt(); - mReportDelayNanos = in.readLong(); + mReportDelaySeconds = in.readLong(); } @Override @@ -116,7 +128,7 @@ public final class ScanSettings implements Parcelable { dest.writeInt(mScanMode); dest.writeInt(mCallbackType); dest.writeInt(mScanResultType); - dest.writeLong(mReportDelayNanos); + dest.writeLong(mReportDelaySeconds); } @Override @@ -142,15 +154,14 @@ public final class ScanSettings implements Parcelable { */ public static final class Builder { private int mScanMode = SCAN_MODE_LOW_POWER; - private int mCallbackType = CALLBACK_TYPE_ON_UPDATE; + private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES; private int mScanResultType = SCAN_RESULT_TYPE_FULL; - private long mReportDelayNanos = 0; + private long mReportDelaySeconds = 0; /** * Set scan mode for Bluetooth LE scan. * - * @param scanMode The scan mode can be one of - * {@link ScanSettings#SCAN_MODE_LOW_POWER}, + * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER}, * {@link ScanSettings#SCAN_MODE_BALANCED} or * {@link ScanSettings#SCAN_MODE_LOW_LATENCY}. * @throws IllegalArgumentException If the {@code scanMode} is invalid. @@ -166,13 +177,13 @@ public final class ScanSettings implements Parcelable { /** * Set callback type for Bluetooth LE scan. * - * @param callbackType The callback type for the scan. Can only be - * {@link ScanSettings#CALLBACK_TYPE_ON_UPDATE}. + * @param callbackType The callback type flags for the scan. * @throws IllegalArgumentException If the {@code callbackType} is invalid. */ public Builder setCallbackType(int callbackType) { - if (callbackType < CALLBACK_TYPE_ON_UPDATE - || callbackType > CALLBACK_TYPE_ON_LOST) { + if (callbackType < CALLBACK_TYPE_ALL_MATCHES + || callbackType > (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST) + || (callbackType & CALLBACK_TYPE_ALL_MATCHES) != CALLBACK_TYPE_ALL_MATCHES) { throw new IllegalArgumentException("invalid callback type - " + callbackType); } mCallbackType = callbackType; @@ -184,14 +195,14 @@ public final class ScanSettings implements Parcelable { * * @param scanResultType Type for scan result, could be either * {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or - * {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}. + * {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}. * @throws IllegalArgumentException If the {@code scanResultType} is invalid. - * * @hide */ + @SystemApi public Builder setScanResultType(int scanResultType) { if (scanResultType < SCAN_RESULT_TYPE_FULL - || scanResultType > SCAN_RESULT_TYPE_TRUNCATED) { + || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) { throw new IllegalArgumentException( "invalid scanResultType - " + scanResultType); } @@ -201,9 +212,13 @@ public final class ScanSettings implements Parcelable { /** * Set report delay timestamp for Bluetooth LE scan. + * @param reportDelaySeconds Set to 0 to be notified of results immediately. + * Values >0 causes the scan results to be queued + * up and delivered after the requested delay or when + * the internal buffers fill up. */ - public Builder setReportDelayNanos(long reportDelayNanos) { - mReportDelayNanos = reportDelayNanos; + public Builder setReportDelaySeconds(long reportDelaySeconds) { + mReportDelaySeconds = reportDelaySeconds; return this; } @@ -212,7 +227,7 @@ public final class ScanSettings implements Parcelable { */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayNanos); + mReportDelaySeconds); } } } diff --git a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java index bf34f1d2381..e0a3a033323 100644 --- a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java @@ -56,20 +56,20 @@ public class ScanFilterTest extends TestCase { @SmallTest public void testsetNameFilter() { - ScanFilter filter = mFilterBuilder.setName("Ped").build(); + ScanFilter filter = mFilterBuilder.setDeviceName("Ped").build(); assertTrue("setName filter fails", filter.matches(mScanResult)); - filter = mFilterBuilder.setName("Pem").build(); + filter = mFilterBuilder.setDeviceName("Pem").build(); assertFalse("setName filter fails", filter.matches(mScanResult)); } @SmallTest public void testDeviceFilter() { - ScanFilter filter = mFilterBuilder.setMacAddress(DEVICE_MAC).build(); + ScanFilter filter = mFilterBuilder.setDeviceAddress(DEVICE_MAC).build(); assertTrue("device filter fails", filter.matches(mScanResult)); - filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build(); + filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build(); assertFalse("device filter fails", filter.matches(mScanResult)); } @@ -134,10 +134,10 @@ public class ScanFilterTest extends TestCase { ScanFilter filter = mFilterBuilder.build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.setName("Ped").build(); + filter = mFilterBuilder.setDeviceName("Ped").build(); testReadWriteParcelForFilter(filter); - filter = mFilterBuilder.setMacAddress("11:22:33:44:55:66").build(); + filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build(); testReadWriteParcelForFilter(filter); filter = mFilterBuilder.setServiceUuid( diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index cece96b14b5..e259bcc9add 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -51,7 +51,7 @@ public class ScanRecordTest extends TestCase { assertTrue(data.getServiceUuids().contains(uuid1)); assertTrue(data.getServiceUuids().contains(uuid2)); - assertEquals("Ped", data.getLocalName()); + assertEquals("Ped", data.getDeviceName()); assertEquals(-20, data.getTxPowerLevel()); assertEquals(224, data.getManufacturerId()); -- GitLab From 1ceb01210f9ee5c2df90e0c0a1f058eede42a03f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 10 Jul 2014 15:28:02 -0700 Subject: [PATCH 0374/1408] Remove MR2 BLE Advertising hidden API from L codebase (1/2). Change-Id: I78bb8b89de56bddb7422da1d1d4468c88a68ea11 --- .../android/bluetooth/BluetoothAdapter.java | 253 +----------------- .../bluetooth/BluetoothAdvScanData.java | 152 ----------- .../android/bluetooth/IBluetoothGatt.aidl | 9 - .../bluetooth/IBluetoothGattCallback.aidl | 1 - .../bluetooth/le/BluetoothLeAdvertiser.java | 5 - .../bluetooth/le/BluetoothLeScanner.java | 5 - 6 files changed, 7 insertions(+), 418 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothAdvScanData.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 97e3fc510d2..faf864550d5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -218,22 +218,6 @@ public final class BluetoothAdapter { public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; - /** - * Broadcast Action: Indicate BLE Advertising is started. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = - "android.bluetooth.adapter.action.ADVERTISING_STARTED"; - - /** - * Broadcast Action: Indicated BLE Advertising is stopped. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = - "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; - /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the current scan mode. Possible values are: @@ -403,8 +387,6 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map mLeScanClients; - private BluetoothAdvScanData mBluetoothAdvScanData = null; - private GattCallbackWrapper mAdvertisingGattCallback; private final Handler mHandler; // Handler to post the advertise callback to run on main thread. private final Object mLock = new Object(); @@ -480,29 +462,6 @@ public final class BluetoothAdapter { address[0], address[1], address[2], address[3], address[4], address[5])); } - /** - * Returns a {@link BluetoothAdvScanData} object representing advertising data. - * Data will be reset when bluetooth service is turned off. - * @hide - */ - public BluetoothAdvScanData getAdvScanData() { - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported - Log.e(TAG, "failed to start, iGatt null"); - return null; - } - if (mBluetoothAdvScanData == null) { - mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); - } - return mBluetoothAdvScanData; - } catch (RemoteException e) { - Log.e(TAG, "failed to get advScanData, error: " + e); - return null; - } - } - /** * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. */ @@ -519,106 +478,6 @@ public final class BluetoothAdapter { return new BluetoothLeScanner(mManagerService); } - /** - * Interface for BLE advertising callback. - * - * @hide - */ - public interface AdvertiseCallback { - /** - * Callback when advertise starts. - * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. - */ - void onAdvertiseStart(int status); - /** - * Callback when advertise stops. - * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. - */ - void onAdvertiseStop(int status); - } - - /** - * Start BLE advertising using current {@link BluetoothAdvScanData}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} - * - * @param callback - {@link AdvertiseCallback} - * @return true if BLE advertising succeeds, false otherwise. - * @hide - */ - public boolean startAdvertising(final AdvertiseCallback callback) { - if (getState() != STATE_ON) return false; - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported. - return false; - } - // Restart/reset advertising packets if advertising is in progress. - if (isAdvertising()) { - // Invalid advertising callback. - if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) { - Log.e(TAG, "failed to restart advertising, invalid callback"); - return false; - } - iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle); - // Run the callback from main thread. - mHandler.post(new Runnable() { - @Override - public void run() { - // callback with status success. - callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS); - } - }); - return true; - } - UUID uuid = UUID.randomUUID(); - GattCallbackWrapper wrapper = - new GattCallbackWrapper(this, null, null, callback); - iGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (!wrapper.advertiseStarted()) { - return false; - } - synchronized (mLock) { - mAdvertisingGattCallback = wrapper; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - - /** - * Stop BLE advertising. The callback has to be the same one used for start advertising. - * - * @param callback - {@link AdvertiseCallback} - * @return true if BLE advertising stops, false otherwise. - * @hide - */ - public boolean stopAdvertising(AdvertiseCallback callback) { - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported - return false; - } - if (mAdvertisingGattCallback == null) { - // no callback. - return false; - } - // Make sure same callback is used for start and stop advertising. - if (callback != mAdvertisingGattCallback.mAdvertiseCallback) { - Log.e(TAG, "must use the same callback for star/stop advertising"); - return false; - } - mAdvertisingGattCallback.stopAdvertising(); - return true; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - /** * Return true if Bluetooth is currently enabled and ready for use. *

        Equivalent to: @@ -1075,23 +934,6 @@ public final class BluetoothAdapter { return false; } - /** - * Returns whether BLE is currently advertising. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * - * @hide - */ - public boolean isAdvertising() { - if (getState() != STATE_ON) return false; - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - return iGatt.isAdvertising(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. @@ -1537,8 +1379,6 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; - // Reset bluetooth adv scan data when Gatt service is down. - mBluetoothAdvScanData = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1822,7 +1662,6 @@ public final class BluetoothAdapter { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; - private final AdvertiseCallback mAdvertiseCallback; private final LeScanCallback mLeScanCb; // mLeHandle 0: not registered @@ -1838,27 +1677,12 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; - mAdvertiseCallback = null; - } - - public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, - UUID[] uuid, AdvertiseCallback callback) { - mBluetoothAdapter = new WeakReference(bluetoothAdapter); - mLeScanCb = leScanCb; - mScanFilter = uuid; - mLeHandle = 0; - mAdvertiseCallback = callback; } public boolean scanStarted() { return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); } - public boolean advertiseStarted() { - // Wait for registeration callback. - return waitForRegisteration(1); - } - private boolean waitForRegisteration(int maxWaitCount) { boolean started = false; synchronized(this) { @@ -1878,27 +1702,6 @@ public final class BluetoothAdapter { return started; } - public void stopAdvertising() { - synchronized (this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); - return; - } - BluetoothAdapter adapter = mBluetoothAdapter.get(); - if (adapter != null) { - try { - IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - iGatt.stopAdvertising(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising" + e); - } - } else { - Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); - } - notifyAll(); - } - } - public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1940,18 +1743,14 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mAdvertiseCallback != null) { - iGatt.startAdvertising(mLeHandle); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); } else { - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); - } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); - } + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -2080,44 +1879,6 @@ public final class BluetoothAdapter { } public void onAdvertiseStateChange(int advertiseState, int status) { - Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); - if (advertiseState == STATE_ADVERTISE_STARTED) { - if (status == ADVERTISE_CALLBACK_SUCCESS) { - mAdvertiseCallback.onAdvertiseStart(status); - } else { - // If status is unsuccessful and advertise state is started, it means stop - // advertising fails. - mAdvertiseCallback.onAdvertiseStop(status); - } - } else { - synchronized (this) { - if (status == ADVERTISE_CALLBACK_SUCCESS) { - BluetoothAdapter adapter = mBluetoothAdapter.get(); - if (adapter != null) { - try { - IBluetoothGatt iGatt = - adapter.getBluetoothManager().getBluetoothGatt(); - Log.d(TAG, "unregistering client " + mLeHandle); - iGatt.unregisterClient(mLeHandle); - // Reset advertise app handle. - mLeHandle = -1; - adapter.mAdvertisingGattCallback = null; - } catch (RemoteException e) { - Log.e(TAG, "Failed to unregister client" + e); - } - } else { - Log.e(TAG, "cannot unregister client, BluetoothAdapter is null"); - } - } - } - if (status == ADVERTISE_CALLBACK_SUCCESS) { - mAdvertiseCallback.onAdvertiseStop(status); - } else{ - // If status is unsuccesful and advertise state is stopped, it means start - // advertising fails. - mAdvertiseCallback.onAdvertiseStart(status); - } - } } @Override diff --git a/framework/java/android/bluetooth/BluetoothAdvScanData.java b/framework/java/android/bluetooth/BluetoothAdvScanData.java deleted file mode 100644 index df2c25629a3..00000000000 --- a/framework/java/android/bluetooth/BluetoothAdvScanData.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import java.util.Collections; -import java.util.List; - - -/** - * This class provides the public APIs to set advertising and scan response data when BLE device - * operates in peripheral mode.
        - * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 - * @hide - */ -public final class BluetoothAdvScanData { - - /** - * Available data types of {@link BluetoothAdvScanData}. - */ - public static final int AD = 0; // Advertising Data - public static final int SCAN_RESPONSE = 1; // Scan Response - public static final int EIR = 2; // Extended Inquiry Response - - private static final String TAG = "BluetoothAdvScanData"; - - /** - * Data type of BluetoothAdvScanData. - */ - private final int mDataType; - /** - * Bluetooth Gatt Service. - */ - private IBluetoothGatt mBluetoothGatt; - - /** - * @param mBluetoothGatt - * @param dataType - */ - public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { - this.mBluetoothGatt = mBluetoothGatt; - this.mDataType = dataType; - } - - /** - * @return advertising data type. - */ - public int getDataType() { - return mDataType; - } - - /** - * Set manufactureCode and manufactureData. - * Returns true if manufacturer data is set, false if there is no enough room to set - * manufacturer data or the data is already set. - * @param manufacturerCode - unique identifier for the manufacturer - * @param manufacturerData - data associated with the specific manufacturer. - */ - public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { - if (mDataType != AD) return false; - try { - return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); - } catch (RemoteException e) { - Log.e(TAG, "Unable to set manufacturer id and data.", e); - return false; - } - } - - /** - * Set service data. Note the service data can only be set when the data type is {@code AD}; - * @param serviceData - */ - public boolean setServiceData(byte[] serviceData) { - - if (mDataType != AD) return false; - if (serviceData == null) return false; - try { - return mBluetoothGatt.setAdvServiceData(serviceData); - } catch (RemoteException e) { - Log.e(TAG, "Unable to set service data.", e); - return false; - } - } - - /** - * Returns an immutable list of service uuids that will be advertised. - */ - public List getServiceUuids() { - try { - return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); - } catch (RemoteException e) { - Log.e(TAG, "Unable to get service uuids.", e); - return null; - } - } - - /** - * Returns manufacturer data. - */ - public byte[] getManufacturerData() { - if (mBluetoothGatt == null) return null; - try { - return mBluetoothGatt.getAdvManufacturerData(); - } catch (RemoteException e) { - Log.e(TAG, "Unable to get manufacturer data.", e); - return null; - } - } - - /** - * Returns service data. - */ - public byte[] getServiceData() { - if (mBluetoothGatt == null) return null; - try { - return mBluetoothGatt.getAdvServiceData(); - } catch (RemoteException e) { - Log.e(TAG, "Unable to get service data.", e); - return null; - } - } - - /** - * Remove manufacturer data based on given manufacturer code. - * @param manufacturerCode - */ - public void removeManufacturerCodeAndData(int manufacturerCode) { - if (mBluetoothGatt != null) { - try { - mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); - } catch (RemoteException e) { - Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e); - } - } - } -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 0f0eee67faf..6d4b9cd45f0 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -48,15 +48,6 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); void clientDisconnect(in int clientIf, in String address); - void startAdvertising(in int appIf); - void stopAdvertising(); - boolean setAdvServiceData(in byte[] serviceData); - byte[] getAdvServiceData(); - boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); - byte[] getAdvManufacturerData(); - List getAdvServiceUuids(); - void removeAdvManufacturerCodeAndData(int manufacturerCode); - boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index af218eb26a9..18e3f54ecf0 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,7 +64,6 @@ oneway interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onAdvertiseStateChange(in int advertiseState, in int status); void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); void onConnectionCongested(in String address, in boolean congested); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index e232512f8eb..af79fcc5aa9 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -331,11 +331,6 @@ public final class BluetoothLeAdvertiser { // no op } - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - @Override public void onMultiAdvertiseCallback(int status) { // TODO: This logic needs to be re-visited to account diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 7e87edc1279..220aa7718c7 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -408,11 +408,6 @@ public final class BluetoothLeScanner { // no op } - @Override - public void onAdvertiseStateChange(int advertiseState, int status) { - // no op - } - @Override public void onMultiAdvertiseCallback(int status) { // no op -- GitLab From a1c4e351713c25c7e9fdecabbfa23407bbae1f73 Mon Sep 17 00:00:00 2001 From: Guang Zhu Date: Thu, 17 Jul 2014 20:24:56 -0700 Subject: [PATCH 0375/1408] add command to enable bt snoop log Change-Id: I22a6482a90c2bb976a5ce44946e9f0fb8b59e6b2 --- .../android/bluetooth/BluetoothInstrumentation.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 22dce393f67..411a3f89496 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -20,6 +20,8 @@ import android.app.Instrumentation; import android.content.Context; import android.os.Bundle; +import junit.framework.Assert; + import java.util.Set; public class BluetoothInstrumentation extends Instrumentation { @@ -70,6 +72,8 @@ public class BluetoothInstrumentation extends Instrumentation { getAddress(); } else if ("getBondedDevices".equals(command)) { getBondedDevices(); + } else if ("enableBtSnoop".equals(command)) { + enableBtSnoop(); } else { finish(null); } @@ -112,6 +116,12 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } + public void enableBtSnoop() { + Assert.assertTrue("failed to enable snoop log", + getBluetoothAdapter().configHciSnoopLog(true)); + finish(mSuccessResult); + } + public void finish(Bundle result) { if (result == null) { result = new Bundle(); -- GitLab From 467c88489a3b727b398d09a9f656e5a747731cde Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 16 Jul 2014 23:02:42 -0700 Subject: [PATCH 0376/1408] LE: Add connection parameter update request API (4/4) Change-Id: Ifb531a1b7ab05888817b96fcc89f85e17df7ffe2 --- .../java/android/bluetooth/BluetoothGatt.java | 53 ++++++++++++++++++- .../android/bluetooth/IBluetoothGatt.aidl | 1 + 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a287b3c4925..f8684e1474b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -92,6 +92,25 @@ public final class BluetoothGatt implements BluetoothProfile { /** A GATT operation failed, errors other than the above */ public static final int GATT_FAILURE = 0x101; + /** + * Connection paramter update - Use the connection paramters recommended by the + * Bluetooth SIG. This is the default value if no connection parameter update + * is requested. + */ + public static final int GATT_CONNECTION_BALANCED = 0; + + /** + * Connection paramter update - Request a high priority, low latency connection. + * An application should only request high priority connection paramters to transfer + * large amounts of data over LE quickly. Once the transfer is complete, the application + * should request {@link BluetoothGatt#GATT_CONNECTION_BALANCED} connectoin parameters + * to reduce energy use. + */ + public static final int GATT_CONNECTION_HIGH_PRIORITY = 1; + + /** Connection paramter update - Request low power, reduced data rate connection parameters. */ + public static final int GATT_CONNECTION_LOW_POWER = 2; + /** * No authentication required. * @hide @@ -738,7 +757,7 @@ public final class BluetoothGatt implements BluetoothProfile { * {@link BluetoothGattCallback#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * - *

        The autoConnect paramter determines whether to actively connect to + *

        The autoConnect parameter determines whether to actively connect to * the remote device, or rather passively scan and finalize the connection * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and @@ -1286,6 +1305,38 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Request a connection parameter update. + * + *

        This function will send a connection parameter update request to the + * remote device. + * + * @param connectionPriority Request a specific connection priority. Must be one of + * {@link BluetoothGatt#GATT_CONNECTION_BALANCED}, + * {@link BluetoothGatt#GATT_CONNECTION_HIGH_PRIORITY} + * or {@link BluetoothGatt#GATT_CONNECTION_LOW_POWER}. + * @throws IllegalArgumentException If the parameters are outside of their + * specified range. + */ + public boolean requestConnectionParameterUpdate(int connectionPriority) { + if (connectionPriority < GATT_CONNECTION_BALANCED || + connectionPriority > GATT_CONNECTION_LOW_POWER) { + throw new IllegalArgumentException("connectionPriority not within valid range"); + } + + if (DBG) Log.d(TAG, "requestConnectionParameterUpdate() - params: " + connectionPriority); + if (mService == null || mClientIf == 0) return false; + + try { + mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + /** * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 6d4b9cd45f0..533be13021d 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -76,6 +76,7 @@ interface IBluetoothGatt { void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); void configureMTU(in int clientIf, in String address, in int mtu); + void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); -- GitLab From 10020cd60014082832731102c958215258e28a93 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 18 Jul 2014 14:38:36 -0700 Subject: [PATCH 0377/1408] Reduce GATT logging output Change-Id: Iefdf101ac849e45bf50c55ce5999364f5fbd24a4 --- .../java/android/bluetooth/BluetoothGatt.java | 38 +++++++++---------- .../bluetooth/BluetoothGattServer.java | 17 +++++---- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index f8684e1474b..27f2011fcb5 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -40,7 +40,7 @@ import java.util.UUID; public final class BluetoothGatt implements BluetoothProfile { private static final String TAG = "BluetoothGatt"; private static final boolean DBG = true; - private static final boolean VDBG = true; + private static final boolean VDBG = false; private final Context mContext; private IBluetoothGatt mService; @@ -213,7 +213,7 @@ public final class BluetoothGatt implements BluetoothProfile { */ public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) { - if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); + if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); if (!address.equals(mDevice.getAddress())) { return; } @@ -230,7 +230,7 @@ public final class BluetoothGatt implements BluetoothProfile { int srvcInstId, ParcelUuid srvcUuid, int inclSrvcType, int inclSrvcInstId, ParcelUuid inclSrvcUuid) { - if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); if (!address.equals(mDevice.getAddress())) { @@ -256,7 +256,7 @@ public final class BluetoothGatt implements BluetoothProfile { int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int charProps) { - if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + + if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { @@ -280,7 +280,7 @@ public final class BluetoothGatt implements BluetoothProfile { int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descUuid) { - if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); + if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); if (!address.equals(mDevice.getAddress())) { return; @@ -324,7 +324,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void onCharacteristicRead(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) { - if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + " UUID=" + charUuid + " Status=" + status); if (!address.equals(mDevice.getAddress())) { @@ -376,7 +376,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void onCharacteristicWrite(String address, int status, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid) { - if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " UUID=" + charUuid + " Status=" + status); if (!address.equals(mDevice.getAddress())) { @@ -428,7 +428,7 @@ public final class BluetoothGatt implements BluetoothProfile { int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) { - if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); + if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { return; @@ -459,7 +459,7 @@ public final class BluetoothGatt implements BluetoothProfile { int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid, byte[] value) { - if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); + if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { return; @@ -513,7 +513,7 @@ public final class BluetoothGatt implements BluetoothProfile { int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid) { - if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); + if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); if (!address.equals(mDevice.getAddress())) { return; @@ -563,7 +563,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ public void onExecuteWrite(String address, int status) { - if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -585,7 +585,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ public void onReadRemoteRssi(String address, int rssi, int status) { - if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + + if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + " rssi=" + rssi + " status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -935,7 +935,7 @@ public final class BluetoothGatt implements BluetoothProfile { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; - if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); + if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); if (mService == null || mClientIf == 0) return false; BluetoothGattService service = characteristic.getService(); @@ -980,7 +980,7 @@ public final class BluetoothGatt implements BluetoothProfile { && (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; - if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); + if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); if (mService == null || mClientIf == 0) return false; BluetoothGattService service = characteristic.getService(); @@ -1023,7 +1023,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the read operation was initiated successfully */ public boolean readDescriptor(BluetoothGattDescriptor descriptor) { - if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); + if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); if (mService == null || mClientIf == 0) return false; BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); @@ -1067,7 +1067,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the write operation was initiated successfully */ public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { - if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); + if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); if (mService == null || mClientIf == 0) return false; BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); @@ -1121,7 +1121,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the reliable write transaction has been initiated */ public boolean beginReliableWrite() { - if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); + if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { @@ -1148,7 +1148,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the request to execute the transaction has been sent */ public boolean executeReliableWrite() { - if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); + if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; synchronized(mDeviceBusy) { @@ -1176,7 +1176,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ public void abortReliableWrite() { - if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); + if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; try { diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index fbbc2f77bb9..5a39dd6b3ce 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -42,6 +42,7 @@ import java.util.UUID; public final class BluetoothGattServer implements BluetoothProfile { private static final String TAG = "BluetoothGattServer"; private static final boolean DBG = true; + private static final boolean VDBG = false; private final Context mContext; private BluetoothAdapter mAdapter; @@ -83,7 +84,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); // no op } @@ -133,7 +134,7 @@ public final class BluetoothGattServer implements BluetoothProfile { ParcelUuid srvcId, int charInstId, ParcelUuid charId) { UUID srvcUuid = srvcId.getUuid(); UUID charUuid = charId.getUuid(); - if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - " + "service=" + srvcUuid + ", characteristic=" + charUuid); BluetoothDevice device = mAdapter.getRemoteDevice(address); @@ -161,7 +162,7 @@ public final class BluetoothGattServer implements BluetoothProfile { UUID srvcUuid = srvcId.getUuid(); UUID charUuid = charId.getUuid(); UUID descrUuid = descrId.getUuid(); - if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - " + if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - " + "service=" + srvcUuid + ", characteristic=" + charUuid + "descriptor=" + descrUuid); @@ -192,7 +193,7 @@ public final class BluetoothGattServer implements BluetoothProfile { int charInstId, ParcelUuid charId, byte[] value) { UUID srvcUuid = srvcId.getUuid(); UUID charUuid = charId.getUuid(); - if (DBG) Log.d(TAG, "onCharacteristicWriteRequest() - " + if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - " + "service=" + srvcUuid + ", characteristic=" + charUuid); BluetoothDevice device = mAdapter.getRemoteDevice(address); @@ -223,7 +224,7 @@ public final class BluetoothGattServer implements BluetoothProfile { UUID srvcUuid = srvcId.getUuid(); UUID charUuid = charId.getUuid(); UUID descrUuid = descrId.getUuid(); - if (DBG) Log.d(TAG, "onDescriptorWriteRequest() - " + if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - " + "service=" + srvcUuid + ", characteristic=" + charUuid + "descriptor=" + descrUuid); @@ -271,7 +272,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @hide */ public void onNotificationSent(String address, int status) { - if (DBG) Log.d(TAG, "onNotificationSent() - " + if (VDBG) Log.d(TAG, "onNotificationSent() - " + "device=" + address + ", status=" + status); BluetoothDevice device = mAdapter.getRemoteDevice(address); @@ -490,7 +491,7 @@ public final class BluetoothGattServer implements BluetoothProfile { */ public boolean sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) { - if (DBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); + if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); if (mService == null || mServerIf == 0) return false; try { @@ -522,7 +523,7 @@ public final class BluetoothGattServer implements BluetoothProfile { */ public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { - if (DBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); + if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); if (mService == null || mServerIf == 0) return false; BluetoothGattService service = characteristic.getService(); -- GitLab From 0afb91d1a96bb0068f9b32ec9a99f8391feb5537 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 16 Jul 2014 22:02:03 -0700 Subject: [PATCH 0378/1408] More API modification of BLE APIs (1/2). Changed include: 1) Add serviceDataUuid to filter so it matches sanRecord and AdvertiseData. 2) Add raw bytes to ScanRecord and make ScanResult take a ScanRecord instead of raw bytes. 3) Change from setServiceUuid(List) to addServiceUuid(ParcelUuid). 4) Added include device name 5) Removed service not registered and added ADVERTISE_DATA_TOO_LARGE. 6) Fixed a few comments. Change-Id: Ibbe07183b1293835c4a84728d1cd2d61e5d627d3 --- .../bluetooth/le/AdvertiseCallback.java | 10 +- .../android/bluetooth/le/AdvertiseData.java | 78 +++++---- .../bluetooth/le/AdvertiseSettings.java | 3 + .../bluetooth/le/BluetoothLeAdvertiser.java | 50 +++--- .../bluetooth/le/BluetoothLeScanner.java | 67 ++++---- .../android/bluetooth/le/ScanCallback.java | 10 +- .../java/android/bluetooth/le/ScanFilter.java | 151 ++++++++---------- .../java/android/bluetooth/le/ScanRecord.java | 36 +++-- .../java/android/bluetooth/le/ScanResult.java | 29 ++-- .../android/bluetooth/le/ScanSettings.java | 34 ++-- .../android/bluetooth/le/ScanFilterTest.java | 18 ++- .../android/bluetooth/le/ScanResultTest.java | 3 +- 12 files changed, 266 insertions(+), 223 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java index 2afbadf47ec..706f469dab0 100644 --- a/framework/java/android/bluetooth/le/AdvertiseCallback.java +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -23,15 +23,15 @@ public abstract class AdvertiseCallback { /** * The requested operation was successful. + * * @hide */ public static final int ADVERTISE_SUCCESS = 0; /** - * Failed to start advertising as the advertisement data contains services that are not - * added to the local Bluetooth GATT server. + * Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes. */ - public static final int ADVERTISE_FAILED_SERVICE_UNKNOWN = 1; + public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; /** * Failed to start advertising because no advertising instance is available. @@ -53,7 +53,6 @@ public abstract class AdvertiseCallback { */ public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; - /** * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertising} indicating * that the advertising has been started successfully. @@ -67,7 +66,8 @@ public abstract class AdvertiseCallback { /** * Callback when advertising could not be started. * - * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for + * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start + * failures. */ public void onStartFailure(int errorCode) { } diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index d0f52b2e14c..f2e48289f66 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -27,10 +27,10 @@ import java.util.Arrays; import java.util.List; /** - * Advertisement data packet container for Bluetooth LE advertising. This represents the data to be + * Advertise data packet container for Bluetooth LE advertising. This represents the data to be * advertised as well as the scan response data for active scans. - * - *

        Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be + *

        + * Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be * advertised. * * @see BluetoothLeAdvertiser @@ -50,18 +50,21 @@ public final class AdvertiseData implements Parcelable { @Nullable private final byte[] mServiceData; - private boolean mIncludeTxPowerLevel; + private final boolean mIncludeTxPowerLevel; + private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, ParcelUuid serviceDataUuid, byte[] serviceData, int manufacturerId, - byte[] manufacturerSpecificData, boolean includeTxPowerLevel) { + byte[] manufacturerSpecificData, boolean includeTxPowerLevel, + boolean includeDeviceName) { mServiceUuids = serviceUuids; mManufacturerId = manufacturerId; mManufacturerSpecificData = manufacturerSpecificData; mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; + mIncludeDeviceName = includeDeviceName; } /** @@ -109,13 +112,20 @@ public final class AdvertiseData implements Parcelable { return mIncludeTxPowerLevel; } + /** + * Whether the device name will be included in the advertisement packet. + */ + public boolean getIncludeDeviceName() { + return mIncludeDeviceName; + } + @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) - + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + "]"; + + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + "]"; } @Override @@ -153,8 +163,12 @@ public final class AdvertiseData implements Parcelable { } } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); + dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); } + /** + * @hide + */ public static final Parcelable.Creator CREATOR = new Creator() { @Override @@ -168,7 +182,9 @@ public final class AdvertiseData implements Parcelable { if (in.readInt() > 0) { List uuids = new ArrayList(); in.readList(uuids, ParcelUuid.class.getClassLoader()); - builder.setServiceUuids(uuids); + for (ParcelUuid uuid : uuids) { + builder.addServiceUuid(uuid); + } } int manufacturerId = in.readInt(); int manufacturerDataLength = in.readInt(); @@ -188,6 +204,7 @@ public final class AdvertiseData implements Parcelable { } } builder.setIncludeTxPowerLevel(in.readByte() == 1); + builder.setIncludeDeviceName(in.readByte() == 1); return builder.build(); } }; @@ -203,8 +220,7 @@ public final class AdvertiseData implements Parcelable { private static final int FLAGS_FIELD_BYTES = 3; @Nullable - private List mServiceUuids; - private boolean mIncludeTxPowerLevel; + private List mServiceUuids = new ArrayList(); private int mManufacturerId; @Nullable private byte[] mManufacturerSpecificData; @@ -212,27 +228,25 @@ public final class AdvertiseData implements Parcelable { private ParcelUuid mServiceDataUuid; @Nullable private byte[] mServiceData; + private boolean mIncludeTxPowerLevel; + private boolean mIncludeDeviceName; /** - * Set the service UUIDs. + * Add a service UUID to advertise data. * - *

        Note: The corresponding Bluetooth Gatt services need to already - * be added on the device (using {@link android.bluetooth.BluetoothGattServer#addService}) prior - * to advertising them. - * - * @param serviceUuids Service UUIDs to be advertised. + * @param serviceUuid A service UUID to be advertised. * @throws IllegalArgumentException If the {@code serviceUuids} are null. */ - public Builder setServiceUuids(List serviceUuids) { - if (serviceUuids == null) { + public Builder addServiceUuid(ParcelUuid serviceUuid) { + if (serviceUuid == null) { throw new IllegalArgumentException("serivceUuids are null"); } - mServiceUuids = serviceUuids; + mServiceUuids.add(serviceUuid); return this; } /** - * Add service data to advertisement. + * Add service data to advertise data. * * @param serviceDataUuid 16-bit UUID of the service the data is associated with * @param serviceData Service data @@ -251,10 +265,10 @@ public final class AdvertiseData implements Parcelable { /** * Set manufacturer specific data. - * - *

        Please refer to the Bluetooth Assigned Numbers document provided by the - * Bluetooth SIG for a list of existing - * company identifiers. + *

        + * Please refer to the Bluetooth Assigned Numbers document provided by the Bluetooth SIG for a list of existing company + * identifiers. * * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. * @param manufacturerSpecificData Manufacturer specific data @@ -275,27 +289,31 @@ public final class AdvertiseData implements Parcelable { } /** - * Whether the transmission power level should be included in the advertising packet. + * Whether the transmission power level should be included in the advertise packet. Tx power + * level field takes 3 bytes in advertise packet. */ public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) { mIncludeTxPowerLevel = includeTxPowerLevel; return this; } + /** + * Set whether the device name should be included in advertise packet. + */ + public Builder setIncludeDeviceName(boolean includeDeviceName) { + mIncludeDeviceName = includeDeviceName; + return this; + } + /** * Build the {@link AdvertiseData}. * - * @throws IllegalArgumentException If the data size is larger than 31 bytes. */ public AdvertiseData build() { - if (totalBytes() > MAX_ADVERTISING_DATA_BYTES) { - throw new IllegalArgumentException( - "advertisement data size is larger than 31 bytes"); - } return new AdvertiseData(mServiceUuids, mServiceDataUuid, mServiceData, mManufacturerId, mManufacturerSpecificData, - mIncludeTxPowerLevel); + mIncludeTxPowerLevel, mIncludeDeviceName); } // Compute the size of the advertisement data. diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 02b4a5b0941..71d7b5bf042 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -139,6 +139,9 @@ public final class AdvertiseSettings implements Parcelable { dest.writeInt(mAdvertiseTimeoutSeconds); } + /** + * @hide + */ public static final Parcelable.Creator CREATOR = new Creator() { @Override diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index af79fcc5aa9..fc53afe7a2a 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -57,6 +57,8 @@ public final class BluetoothLeAdvertiser { /** * Use BluetoothAdapter.getLeAdvertiser() instead. + * + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management * @hide */ public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { @@ -66,9 +68,8 @@ public final class BluetoothLeAdvertiser { } /** - * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be - * broadcasted. Returns immediately, the operation status is delivered through - * {@code callback}. + * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted. + * Returns immediately, the operation status is delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * @@ -83,9 +84,9 @@ public final class BluetoothLeAdvertiser { /** * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the - * operation succeeds. The {@code scanResponse} is returned when a scanning device sends - * an active scan request. This method returns immediately, the operation status is - * delivered through {@code callback}. + * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an + * active scan request. This method returns immediately, the operation status is delivered + * through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * @@ -142,11 +143,13 @@ public final class BluetoothLeAdvertiser { throw new IllegalArgumentException("callback cannot be null"); } AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); - if (wrapper == null) return; + if (wrapper == null) + return; try { IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt(); - if (gatt != null) gatt.stopMultiAdvertising(wrapper.mLeHandle); + if (gatt != null) + gatt.stopMultiAdvertising(wrapper.mClientIf); if (wrapper.advertiseStopped()) { mLeAdvertisers.remove(callback); @@ -167,10 +170,10 @@ public final class BluetoothLeAdvertiser { private final AdvertiseSettings mSettings; private final IBluetoothGatt mBluetoothGatt; - // mLeHandle 0: not registered + // mClientIf 0: not registered // -1: scan stopped // >0: registered and scan started - private int mLeHandle; + private int mClientIf; private boolean isAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, @@ -182,13 +185,13 @@ public final class BluetoothLeAdvertiser { mScanResponse = scanResponse; mSettings = settings; mBluetoothGatt = bluetoothGatt; - mLeHandle = 0; + mClientIf = 0; } public boolean advertiseStarted() { boolean started = false; synchronized (this) { - if (mLeHandle == -1) { + if (mClientIf == -1) { return false; } try { @@ -196,7 +199,7 @@ public final class BluetoothLeAdvertiser { } catch (InterruptedException e) { Log.e(TAG, "Callback reg wait interrupted: ", e); } - started = (mLeHandle > 0 && isAdvertising); + started = (mClientIf > 0 && isAdvertising); } return started; } @@ -220,18 +223,18 @@ public final class BluetoothLeAdvertiser { Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; + mClientIf = clientIf; try { - mBluetoothGatt.startMultiAdvertising(mLeHandle, mAdvertisement, + mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, mScanResponse, mSettings); } catch (RemoteException e) { Log.e(TAG, "fail to start le advertise: " + e); - mLeHandle = -1; + mClientIf = -1; notifyAll(); } } else { // registration failed - mLeHandle = -1; + mClientIf = -1; notifyAll(); } } @@ -334,16 +337,16 @@ public final class BluetoothLeAdvertiser { @Override public void onMultiAdvertiseCallback(int status) { // TODO: This logic needs to be re-visited to account - // for whether the scan has actually been started - // or not. Toggling the isAdvertising does not seem - // correct. + // for whether the scan has actually been started + // or not. Toggling the isAdvertising does not seem + // correct. synchronized (this) { if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { isAdvertising = !isAdvertising; if (!isAdvertising) { try { - mBluetoothGatt.unregisterClient(mLeHandle); - mLeHandle = -1; + mBluetoothGatt.unregisterClient(mClientIf); + mClientIf = -1; } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } @@ -351,7 +354,8 @@ public final class BluetoothLeAdvertiser { mAdvertiseCallback.onStartSuccess(null); } } else { - if (!isAdvertising) mAdvertiseCallback.onStartFailure(status); + if (!isAdvertising) + mAdvertiseCallback.onStartFailure(status); } notifyAll(); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 220aa7718c7..980f717a2f6 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -36,8 +36,8 @@ import java.util.UUID; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. - * It can also request different types of callbacks for delivering the result. + * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. It + * can also request different types of callbacks for delivering the result. *

        * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of * {@link BluetoothLeScanner}. @@ -59,7 +59,8 @@ public final class BluetoothLeScanner { /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. - * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management + * + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. * @hide */ public BluetoothLeScanner(IBluetoothManager bluetoothManager) { @@ -70,8 +71,8 @@ public final class BluetoothLeScanner { } /** - * Start Bluetooth LE scan with default parameters and no filters. - * The scan results will be delivered through {@code callback}. + * Start Bluetooth LE scan with default parameters and no filters. The scan results will be + * delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * @@ -191,7 +192,7 @@ public final class BluetoothLeScanner { // mLeHandle 0: not registered // -1: scan stopped // > 0: registered and scan started - private int mLeHandle; + private int mClientIf; public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, @@ -200,12 +201,12 @@ public final class BluetoothLeScanner { mFilters = filters; mSettings = settings; mScanCallback = scanCallback; - mLeHandle = 0; + mClientIf = 0; } public boolean scanStarted() { synchronized (this) { - if (mLeHandle == -1) { + if (mClientIf == -1) { return false; } try { @@ -214,34 +215,34 @@ public final class BluetoothLeScanner { Log.e(TAG, "Callback reg wait interrupted: " + e); } } - return mLeHandle > 0; + return mClientIf > 0; } public void stopLeScan() { synchronized (this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + if (mClientIf <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mClientIf); return; } try { - mBluetoothGatt.stopScan(mLeHandle, false); - mBluetoothGatt.unregisterClient(mLeHandle); + mBluetoothGatt.stopScan(mClientIf, false); + mBluetoothGatt.unregisterClient(mClientIf); } catch (RemoteException e) { Log.e(TAG, "Failed to stop scan and unregister", e); } - mLeHandle = -1; + mClientIf = -1; notifyAll(); } } void flushPendingBatchResults() { synchronized (this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + if (mClientIf <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mClientIf); return; } try { - mBluetoothGatt.flushPendingBatchResults(mLeHandle, false); + mBluetoothGatt.flushPendingBatchResults(mClientIf, false); } catch (RemoteException e) { Log.e(TAG, "Failed to get pending scan results", e); } @@ -257,22 +258,22 @@ public final class BluetoothLeScanner { " clientIf=" + clientIf); synchronized (this) { - if (mLeHandle == -1) { + if (mClientIf == -1) { if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); } if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; + mClientIf = clientIf; try { - mBluetoothGatt.startScanWithFilters(mLeHandle, false, mSettings, mFilters); + mBluetoothGatt.startScanWithFilters(mClientIf, false, mSettings, mFilters); } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); - mLeHandle = -1; + mClientIf = -1; } } else { // registration failed - mLeHandle = -1; + mClientIf = -1; } notifyAll(); } @@ -296,14 +297,14 @@ public final class BluetoothLeScanner { // Check null in case the scan has been stopped synchronized (this) { - if (mLeHandle <= 0) + if (mClientIf <= 0) return; } BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( address); long scanNanos = SystemClock.elapsedRealtimeNanos(); - final ScanResult result = new ScanResult(device, advData, rssi, - scanNanos); + final ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(advData), + rssi, scanNanos); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override @@ -433,7 +434,7 @@ public final class BluetoothLeScanner { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( address); long scanNanos = SystemClock.elapsedRealtimeNanos(); - ScanResult result = new ScanResult(device, advData, rssi, + ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(advData), rssi, scanNanos); if (onFound) { mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result); @@ -453,13 +454,15 @@ public final class BluetoothLeScanner { } private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { + if (mBluetoothAdapter.isOffloadedFilteringSupported()) { + return true; + } final int callbackType = settings.getCallbackType(); - if (( callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES - || (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES - && settings.getReportDelaySeconds() > 0)) - && !mBluetoothAdapter.isOffloadedFilteringSupported()) { - return false; + // Only support regular scan if no offloaded filter support. + if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES + && settings.getReportDelayMillis() == 0) { + return true; } - return true; + return false; } } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index b4c1e1769cb..5b373845a79 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -19,10 +19,9 @@ package android.bluetooth.le; import java.util.List; /** - * Bluetooth LE scan callbacks. - * Scan results are reported using these callbacks. + * Bluetooth LE scan callbacks. Scan results are reported using these callbacks. * - * {@see BluetoothLeScanner#startScan} + * @see BluetoothLeScanner#startScan */ public abstract class ScanCallback { /** @@ -48,8 +47,8 @@ public abstract class ScanCallback { /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines if this callback was triggered because of first match, - * a lost match indication or a regular scan result. + * @param callbackType Determines if this callback was triggered because of first match, a lost + * match indication or a regular scan result. * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { @@ -65,6 +64,7 @@ public abstract class ScanCallback { /** * Callback when scan could not be started. + * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure. */ public void onScanFailed(int errorCode) { } diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index e8a91b81a41..0ae11b0cd99 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -29,17 +29,17 @@ import java.util.Objects; import java.util.UUID; /** - * {@link ScanFilter} abstracts different scan filters across Bluetooth Advertisement packet fields. + * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to + * restrict scan results to only those that are of interest to them. *

        * Current filtering on the following fields are supported: *

      • Service UUIDs which identify the bluetooth gatt services running on the device. *
      • Name of remote Bluetooth LE device. *
      • Mac address of the remote device. - *
      • Rssi which indicates the received power level. *
      • Service data which is the data associated with a service. *
      • Manufacturer specific data which is the data associated with a particular manufacturer. * - * @see ScanRecord + * @see ScanResult * @see BluetoothLeScanner */ public final class ScanFilter implements Parcelable { @@ -55,6 +55,8 @@ public final class ScanFilter implements Parcelable { @Nullable private final ParcelUuid mServiceUuidMask; + @Nullable + private final ParcelUuid mServiceDataUuid; @Nullable private final byte[] mServiceData; @Nullable @@ -66,24 +68,20 @@ public final class ScanFilter implements Parcelable { @Nullable private final byte[] mManufacturerDataMask; - private final int mMinRssi; - private final int mMaxRssi; - private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, - ParcelUuid uuidMask, byte[] serviceData, byte[] serviceDataMask, - int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, - int minRssi, int maxRssi) { + ParcelUuid uuidMask, ParcelUuid serviceDataUuid, + byte[] serviceData, byte[] serviceDataMask, + int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; mDeviceAddress = deviceAddress; + mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = serviceDataMask; mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; - mMinRssi = minRssi; - mMaxRssi = maxRssi; } @Override @@ -109,12 +107,16 @@ public final class ScanFilter implements Parcelable { dest.writeParcelable(mServiceUuidMask, flags); } } - dest.writeInt(mServiceData == null ? 0 : mServiceData.length); - if (mServiceData != null) { - dest.writeByteArray(mServiceData); - dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); - if (mServiceDataMask != null) { - dest.writeByteArray(mServiceDataMask); + dest.writeInt(mServiceDataUuid == null? 0 : 1); + if (mServiceDataUuid != null) { + dest.writeParcelable(mServiceDataUuid, flags); + dest.writeInt(mServiceData == null ? 0 : mServiceData.length); + if (mServiceData != null) { + dest.writeByteArray(mServiceData); + dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); + if (mServiceDataMask != null) { + dest.writeByteArray(mServiceDataMask); + } } } dest.writeInt(mManufacturerId); @@ -126,12 +128,12 @@ public final class ScanFilter implements Parcelable { dest.writeByteArray(mManufacturerDataMask); } } - dest.writeInt(mMinRssi); - dest.writeInt(mMaxRssi); } /** - * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} form parcel. + * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. + * + * @hide */ public static final Creator CREATOR = new Creator() { @@ -159,17 +161,21 @@ public final class ScanFilter implements Parcelable { builder.setServiceUuid(uuid, uuidMask); } } - - int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.setServiceData(serviceData); - int serviceDataMaskLength = in.readInt(); - if (serviceDataMaskLength > 0) { - byte[] serviceDataMask = new byte[serviceDataMaskLength]; - in.readByteArray(serviceDataMask); - builder.setServiceData(serviceData, serviceDataMask); + if (in.readInt() == 1) { + ParcelUuid servcieDataUuid = + in.readParcelable(ParcelUuid.class.getClassLoader()); + int serviceDataLength = in.readInt(); + if (serviceDataLength > 0) { + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.setServiceData(servcieDataUuid, serviceData); + int serviceDataMaskLength = in.readInt(); + if (serviceDataMaskLength > 0) { + byte[] serviceDataMask = new byte[serviceDataMaskLength]; + in.readByteArray(serviceDataMask); + builder.setServiceData( + servcieDataUuid, serviceData, serviceDataMask); + } } } @@ -188,9 +194,6 @@ public final class ScanFilter implements Parcelable { } } - int minRssi = in.readInt(); - int maxRssi = in.readInt(); - builder.setRssiRange(minRssi, maxRssi); return builder.build(); } }; @@ -248,20 +251,6 @@ public final class ScanFilter implements Parcelable { return mManufacturerDataMask; } - /** - * Returns minimum value of RSSI for the scan filter. {@link Integer#MIN_VALUE} if not set. - */ - public int getMinRssi() { - return mMinRssi; - } - - /** - * Returns maximum value of the RSSI for the scan filter. {@link Integer#MAX_VALUE} if not set. - */ - public int getMaxRssi() { - return mMaxRssi; - } - /** * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match * if it matches all the field filters. @@ -272,17 +261,12 @@ public final class ScanFilter implements Parcelable { } BluetoothDevice device = scanResult.getDevice(); // Device match. - if (mDeviceAddress != null && (device == null || !mDeviceAddress.equals(device.getAddress()))) { - return false; - } - - int rssi = scanResult.getRssi(); - if (rssi < mMinRssi || rssi > mMaxRssi) { + if (mDeviceAddress != null + && (device == null || !mDeviceAddress.equals(device.getAddress()))) { return false; } - byte[] scanRecordBytes = scanResult.getScanRecord(); - ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordBytes); + ScanRecord scanRecord = scanResult.getScanRecord(); // Scan record is null but there exist filters on it. if (scanRecord == null @@ -367,19 +351,19 @@ public final class ScanFilter implements Parcelable { @Override public String toString() { - return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + mDeviceAddress + return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + + mDeviceAddress + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + ", mManufacturerData=" + Arrays.toString(mManufacturerData) - + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) - + ", mMinRssi=" + mMinRssi + ", mMaxRssi=" + mMaxRssi + "]"; + + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]"; } @Override public int hashCode() { return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, mManufacturerData, - mManufacturerDataMask, mMaxRssi, mMinRssi, mServiceData, mServiceDataMask, + mManufacturerDataMask, mServiceData, mServiceDataMask, mServiceUuid, mServiceUuidMask); } @@ -397,7 +381,6 @@ public final class ScanFilter implements Parcelable { mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - mMinRssi == other.mMinRssi && mMaxRssi == other.mMaxRssi && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && @@ -415,6 +398,7 @@ public final class ScanFilter implements Parcelable { private ParcelUuid mServiceUuid; private ParcelUuid mUuidMask; + private ParcelUuid mServiceDataUuid; private byte[] mServiceData; private byte[] mServiceDataMask; @@ -422,9 +406,6 @@ public final class ScanFilter implements Parcelable { private byte[] mManufacturerData; private byte[] mManufacturerDataMask; - private int mMinRssi = Integer.MIN_VALUE; - private int mMaxRssi = Integer.MAX_VALUE; - /** * Set filter on device name. */ @@ -436,8 +417,8 @@ public final class ScanFilter implements Parcelable { /** * Set filter on device address. * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in - * the format of "01:02:03:AB:CD:EF". The device address can be validated using + * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the + * format of "01:02:03:AB:CD:EF". The device address can be validated using * {@link BluetoothAdapter#checkBluetoothAddress}. * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ @@ -477,8 +458,14 @@ public final class ScanFilter implements Parcelable { /** * Set filtering on service data. + * + * @throws IllegalArgumentException If {@code serviceDataUuid} is null. */ - public Builder setServiceData(byte[] serviceData) { + public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + if (serviceDataUuid == null) { + throw new IllegalArgumentException("serviceDataUuid is null"); + } + mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = null; // clear service data mask return this; @@ -490,11 +477,15 @@ public final class ScanFilter implements Parcelable { *

        * The {@code serviceDataMask} must have the same length of the {@code serviceData}. * - * @throws IllegalArgumentException If {@code serviceDataMask} is {@code null} while - * {@code serviceData} is not or {@code serviceDataMask} and {@code serviceData} - * has different length. + * @throws IllegalArgumentException If {@code serviceDataUuid} is null or + * {@code serviceDataMask} is {@code null} while {@code serviceData} is not or + * {@code serviceDataMask} and {@code serviceData} has different length. */ - public Builder setServiceData(byte[] serviceData, byte[] serviceDataMask) { + public Builder setServiceData(ParcelUuid serviceDataUuid, + byte[] serviceData, byte[] serviceDataMask) { + if (serviceDataUuid == null) { + throw new IllegalArgumentException("serviceDataUuid is null"); + } if (mServiceDataMask != null) { if (mServiceData == null) { throw new IllegalArgumentException( @@ -530,8 +521,8 @@ public final class ScanFilter implements Parcelable { } /** - * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it - * needs to match the one in manufacturer data, otherwise set it to 0. + * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs + * to match the one in manufacturer data, otherwise set it to 0. *

        * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}. * @@ -563,16 +554,6 @@ public final class ScanFilter implements Parcelable { return this; } - /** - * Set the desired RSSI range for the filter. A scan result with RSSI in the range of - * [minRssi, maxRssi] will be consider as a match. - */ - public Builder setRssiRange(int minRssi, int maxRssi) { - mMinRssi = minRssi; - mMaxRssi = maxRssi; - return this; - } - /** * Build {@link ScanFilter}. * @@ -581,8 +562,8 @@ public final class ScanFilter implements Parcelable { public ScanFilter build() { return new ScanFilter(mDeviceName, mDeviceAddress, mServiceUuid, mUuidMask, - mServiceData, mServiceDataMask, - mManufacturerId, mManufacturerData, mManufacturerDataMask, mMinRssi, mMaxRssi); + mServiceDataUuid, mServiceData, mServiceDataMask, + mManufacturerId, mManufacturerData, mManufacturerDataMask); } } } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index fc46e765a3e..dd033080173 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -68,6 +68,9 @@ public final class ScanRecord { // Local name of the Bluetooth LE device. private final String mDeviceName; + // Raw bytes of scan record. + private final byte[] mBytes; + /** * Returns the advertising flags indicating the discoverable mode and capability of the device. * Returns -1 if the flag field is not set. @@ -133,11 +136,18 @@ public final class ScanRecord { return mDeviceName; } + /** + * Returns raw bytes of scan record. + */ + public byte[] getBytes() { + return mBytes; + } + private ScanRecord(List serviceUuids, ParcelUuid serviceDataUuid, byte[] serviceData, int manufacturerId, byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, - String localName) { + String localName, byte[] bytes) { mServiceUuids = serviceUuids; mManufacturerId = manufacturerId; mManufacturerSpecificData = manufacturerSpecificData; @@ -146,15 +156,7 @@ public final class ScanRecord { mDeviceName = localName; mAdvertiseFlags = advertiseFlags; mTxPowerLevel = txPowerLevel; - } - - @Override - public String toString() { - return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) - + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; + mBytes = bytes; } /** @@ -166,6 +168,8 @@ public final class ScanRecord { * order. * * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. + * + * @hide */ public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { @@ -248,13 +252,23 @@ public final class ScanRecord { } return new ScanRecord(serviceUuids, serviceDataUuid, serviceData, manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, - localName); + localName, scanRecord); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); return null; } } + @Override + public String toString() { + return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids + + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; + } + + // Parse service UUIDs. private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List serviceUuids) { diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 7e6e8f81c78..9aee20094a8 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -21,7 +21,6 @@ import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; import java.util.Objects; /** @@ -32,7 +31,8 @@ public final class ScanResult implements Parcelable { private BluetoothDevice mDevice; // Scan record, including advertising data and scan response data. - private byte[] mScanRecord; + @Nullable + private ScanRecord mScanRecord; // Received signal strength. private int mRssi; @@ -43,9 +43,12 @@ public final class ScanResult implements Parcelable { /** * Constructor of scan result. * - * @hide + * @param device Remote bluetooth device that is found. + * @param scanRecord Scan record including both advertising data and scan response data. + * @param rssi Received signal strength. + * @param timestampNanos Device timestamp when the scan result was observed. */ - public ScanResult(BluetoothDevice device, byte[] scanRecord, int rssi, + public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos) { mDevice = device; mScanRecord = scanRecord; @@ -66,8 +69,8 @@ public final class ScanResult implements Parcelable { dest.writeInt(0); } if (mScanRecord != null) { - dest.writeInt(1); - dest.writeByteArray(mScanRecord); + dest.writeInt(mScanRecord.getBytes().length); + dest.writeByteArray(mScanRecord.getBytes()); } else { dest.writeInt(0); } @@ -80,7 +83,7 @@ public final class ScanResult implements Parcelable { mDevice = BluetoothDevice.CREATOR.createFromParcel(in); } if (in.readInt() == 1) { - mScanRecord = in.createByteArray(); + mScanRecord = ScanRecord.parseFromBytes(in.createByteArray()); } mRssi = in.readInt(); mTimestampNanos = in.readLong(); @@ -94,16 +97,15 @@ public final class ScanResult implements Parcelable { /** * Returns the remote bluetooth device identified by the bluetooth device address. */ - @Nullable public BluetoothDevice getDevice() { return mDevice; } /** - * Returns the scan record, which can be a combination of advertisement and scan response. + * Returns the scan record, which is a combination of advertisement and scan response. */ @Nullable - public byte[] getScanRecord() { + public ScanRecord getScanRecord() { return mScanRecord; } @@ -136,17 +138,20 @@ public final class ScanResult implements Parcelable { } ScanResult other = (ScanResult) obj; return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.deepEquals(mScanRecord, other.mScanRecord) + Objects.equals(mScanRecord, other.mScanRecord) && (mTimestampNanos == other.mTimestampNanos); } @Override public String toString() { return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + Arrays.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos=" + + mScanRecord.toString() + ", mRssi=" + mRssi + ", mTimestampNanos=" + mTimestampNanos + '}'; } + /** + * @hide + */ public static final Parcelable.Creator CREATOR = new Creator() { @Override public ScanResult createFromParcel(Parcel source) { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 20977026d3b..2f86d09ec08 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -45,7 +45,7 @@ public final class ScanSettings implements Parcelable { public static final int SCAN_MODE_LOW_LATENCY = 2; /** - * Triggger a callback for every Bluetooth advertisement found that matches the + * Trigger a callback for every Bluetooth advertisement found that matches the * filter criteria. If no filter is active, all advertisement packets are reported. */ public static final int CALLBACK_TYPE_ALL_MATCHES = 1; @@ -87,7 +87,7 @@ public final class ScanSettings implements Parcelable { private int mScanResultType; // Time of delay for reporting the scan result - private long mReportDelaySeconds; + private long mReportDelayMillis; public int getScanMode() { return mScanMode; @@ -104,8 +104,8 @@ public final class ScanSettings implements Parcelable { /** * Returns report delay timestamp based on the device clock. */ - public long getReportDelaySeconds() { - return mReportDelaySeconds; + public long getReportDelayMillis() { + return mReportDelayMillis; } private ScanSettings(int scanMode, int callbackType, int scanResultType, @@ -113,14 +113,14 @@ public final class ScanSettings implements Parcelable { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; - mReportDelaySeconds = reportDelaySeconds; + mReportDelayMillis = reportDelaySeconds; } private ScanSettings(Parcel in) { mScanMode = in.readInt(); mCallbackType = in.readInt(); mScanResultType = in.readInt(); - mReportDelaySeconds = in.readLong(); + mReportDelayMillis = in.readLong(); } @Override @@ -128,7 +128,7 @@ public final class ScanSettings implements Parcelable { dest.writeInt(mScanMode); dest.writeInt(mCallbackType); dest.writeInt(mScanResultType); - dest.writeLong(mReportDelaySeconds); + dest.writeLong(mReportDelayMillis); } @Override @@ -136,6 +136,9 @@ public final class ScanSettings implements Parcelable { return 0; } + /** + * @hide + */ public static final Parcelable.Creator CREATOR = new Creator() { @Override @@ -156,7 +159,7 @@ public final class ScanSettings implements Parcelable { private int mScanMode = SCAN_MODE_LOW_POWER; private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES; private int mScanResultType = SCAN_RESULT_TYPE_FULL; - private long mReportDelaySeconds = 0; + private long mReportDelayMillis = 0; /** * Set scan mode for Bluetooth LE scan. @@ -212,13 +215,18 @@ public final class ScanSettings implements Parcelable { /** * Set report delay timestamp for Bluetooth LE scan. - * @param reportDelaySeconds Set to 0 to be notified of results immediately. - * Values >0 causes the scan results to be queued + * @param reportDelayMillis Set to 0 to be notified of results immediately. + * Values > 0 causes the scan results to be queued * up and delivered after the requested delay or when * the internal buffers fill up. + * @throws IllegalArgumentException If {@code reportDelaySeconds} < 0. + * */ - public Builder setReportDelaySeconds(long reportDelaySeconds) { - mReportDelaySeconds = reportDelaySeconds; + public Builder setReportDelayMillis(long reportDelayMillis) { + if (reportDelayMillis < 0) { + throw new IllegalArgumentException("reportDelaySeconds must be > 0"); + } + mReportDelayMillis = reportDelayMillis; return this; } @@ -227,7 +235,7 @@ public final class ScanSettings implements Parcelable { */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelaySeconds); + mReportDelayMillis); } } } diff --git a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java index e0a3a033323..25ea2271429 100644 --- a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java @@ -18,6 +18,8 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; import android.os.Parcel; import android.os.ParcelUuid; import android.test.suitebuilder.annotation.SmallTest; @@ -50,7 +52,8 @@ public class ScanFilterTest extends TestCase { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); - mScanResult = new ScanResult(device, scanRecord, -10, 1397545200000000L); + mScanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), + -10, 1397545200000000L); mFilterBuilder = new ScanFilter.Builder(); } @@ -94,17 +97,18 @@ public class ScanFilterTest extends TestCase { public void testsetServiceDataFilter() { byte[] setServiceData = new byte[] { 0x0b, 0x11, 0x50, 0x64 }; - ScanFilter filter = mFilterBuilder.setServiceData(setServiceData).build(); + ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); assertTrue("service data filter fails", filter.matches(mScanResult)); byte[] nonMatchData = new byte[] { 0x0b, 0x01, 0x50, 0x64 }; - filter = mFilterBuilder.setServiceData(nonMatchData).build(); + filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build(); assertFalse("service data filter fails", filter.matches(mScanResult)); byte[] mask = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.setServiceData(nonMatchData, mask).build(); + filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build(); assertTrue("partial service data filter fails", filter.matches(mScanResult)); } @@ -152,12 +156,14 @@ public class ScanFilterTest extends TestCase { byte[] setServiceData = new byte[] { 0x0b, 0x11, 0x50, 0x64 }; - filter = mFilterBuilder.setServiceData(setServiceData).build(); + ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); testReadWriteParcelForFilter(filter); byte[] serviceDataMask = new byte[] { (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.setServiceData(setServiceData, serviceDataMask).build(); + filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData, serviceDataMask) + .build(); testReadWriteParcelForFilter(filter); byte[] manufacturerData = new byte[] { diff --git a/framework/tests/src/android/bluetooth/le/ScanResultTest.java b/framework/tests/src/android/bluetooth/le/ScanResultTest.java index 241e88fb3d4..01d5c593bf2 100644 --- a/framework/tests/src/android/bluetooth/le/ScanResultTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanResultTest.java @@ -43,7 +43,8 @@ public class ScanResultTest extends TestCase { int rssi = -10; long timestampMicros = 10000L; - ScanResult result = new ScanResult(device, scanRecord, rssi, timestampMicros); + ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), rssi, + timestampMicros); Parcel parcel = Parcel.obtain(); result.writeToParcel(parcel, 0); // Need to reset parcel data position to the beginning. -- GitLab From b4799c5c2cfabc5e7da05261d054b0a30d42184f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 23 Jul 2014 00:33:44 -0700 Subject: [PATCH 0379/1408] Fix parcel issues with empty service uuid. Added tests. b/16490816 Change-Id: If26a4731f22e83df4f5ded2ad76901de6c49a010 --- .../android/bluetooth/le/AdvertiseData.java | 79 ++++++---- .../bluetooth/le/AdvertiseDataTest.java | 144 ++++++++++++++++++ .../android/bluetooth/le/ScanFilterTest.java | 1 - 3 files changed, 193 insertions(+), 31 deletions(-) create mode 100644 framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index f2e48289f66..34fecfa6098 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.os.Parcelable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * Advertise data packet container for Bluetooth LE advertising. This represents the data to be @@ -119,13 +120,44 @@ public final class AdvertiseData implements Parcelable { return mIncludeDeviceName; } + /** + * @hide + */ + @Override + public int hashCode() { + return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData, + mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + } + + /** + * @hide + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + AdvertiseData other = (AdvertiseData) obj; + return Objects.equals(mServiceUuids, other.mServiceUuids) && + mManufacturerId == other.mManufacturerId && + Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && + Objects.deepEquals(mServiceData, other.mServiceData) && + mIncludeDeviceName == other.mIncludeDeviceName && + mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; + } + @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) - + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + "]"; + + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + + mIncludeDeviceName + "]"; } @Override @@ -135,32 +167,23 @@ public final class AdvertiseData implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - if (mServiceUuids == null) { - dest.writeInt(0); - } else { - dest.writeInt(mServiceUuids.size()); - dest.writeList(mServiceUuids); - } + dest.writeList(mServiceUuids); dest.writeInt(mManufacturerId); if (mManufacturerSpecificData == null) { dest.writeInt(0); } else { + dest.writeInt(1); dest.writeInt(mManufacturerSpecificData.length); dest.writeByteArray(mManufacturerSpecificData); } - - if (mServiceDataUuid == null) { + dest.writeParcelable(mServiceDataUuid, flags); + if (mServiceData == null) { dest.writeInt(0); } else { dest.writeInt(1); - dest.writeParcelable(mServiceDataUuid, flags); - if (mServiceData == null) { - dest.writeInt(0); - } else { - dest.writeInt(mServiceData.length); - dest.writeByteArray(mServiceData); - } + dest.writeInt(mServiceData.length); + dest.writeByteArray(mServiceData); } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); @@ -179,29 +202,26 @@ public final class AdvertiseData implements Parcelable { @Override public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); - if (in.readInt() > 0) { - List uuids = new ArrayList(); - in.readList(uuids, ParcelUuid.class.getClassLoader()); + List uuids = in.readArrayList(ParcelUuid.class.getClassLoader()); + if (uuids != null) { for (ParcelUuid uuid : uuids) { builder.addServiceUuid(uuid); } } int manufacturerId = in.readInt(); - int manufacturerDataLength = in.readInt(); - if (manufacturerDataLength > 0) { + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); byte[] manufacturerData = new byte[manufacturerDataLength]; in.readByteArray(manufacturerData); builder.setManufacturerData(manufacturerId, manufacturerData); } + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); if (in.readInt() == 1) { - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.setServiceData(serviceDataUuid, serviceData); - } + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.setServiceData(serviceDataUuid, serviceData); } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); @@ -221,7 +241,7 @@ public final class AdvertiseData implements Parcelable { @Nullable private List mServiceUuids = new ArrayList(); - private int mManufacturerId; + private int mManufacturerId = -1; @Nullable private byte[] mManufacturerSpecificData; @Nullable @@ -307,7 +327,6 @@ public final class AdvertiseData implements Parcelable { /** * Build the {@link AdvertiseData}. - * */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, diff --git a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java new file mode 100644 index 00000000000..5e451ca7b7d --- /dev/null +++ b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 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.le; + +import android.os.Parcel; +import android.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link AdvertiseData}. + *

        + * To run the test, use adb shell am instrument -e class 'android.bluetooth.le.AdvertiseDataTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class AdvertiseDataTest extends TestCase { + + private AdvertiseData.Builder mAdvertiseDataBuilder; + + @Override + protected void setUp() throws Exception { + mAdvertiseDataBuilder = new AdvertiseData.Builder(); + } + + @SmallTest + public void testEmptyData() { + Parcel parcel = Parcel.obtain(); + AdvertiseData data = mAdvertiseDataBuilder.build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testEmptyServiceUuid() { + Parcel parcel = Parcel.obtain(); + AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true).build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testEmptyManufacturerData() { + Parcel parcel = Parcel.obtain(); + int manufacturerId = 50; + byte[] manufacturerData = new byte[0]; + AdvertiseData data = + mAdvertiseDataBuilder.setIncludeDeviceName(true) + .setManufacturerData(manufacturerId, manufacturerData).build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testEmptyServiceData() { + Parcel parcel = Parcel.obtain(); + ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + byte[] serviceData = new byte[0]; + AdvertiseData data = + mAdvertiseDataBuilder.setIncludeDeviceName(true) + .setServiceData(uuid, serviceData).build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testServiceUuid() { + Parcel parcel = Parcel.obtain(); + ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + + AdvertiseData data = + mAdvertiseDataBuilder.setIncludeDeviceName(true) + .addServiceUuid(uuid).addServiceUuid(uuid2).build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testManufacturerData() { + Parcel parcel = Parcel.obtain(); + ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + + int manufacturerId = 50; + byte[] manufacturerData = new byte[] { + (byte) 0xF0, 0x00, 0x02, 0x15 }; + AdvertiseData data = + mAdvertiseDataBuilder.setIncludeDeviceName(true) + .addServiceUuid(uuid).addServiceUuid(uuid2) + .setManufacturerData(manufacturerId, manufacturerData).build(); + + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } + + @SmallTest + public void testServiceData() { + Parcel parcel = Parcel.obtain(); + ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + byte[] serviceData = new byte[] { + (byte) 0xF0, 0x00, 0x02, 0x15 }; + AdvertiseData data = + mAdvertiseDataBuilder.setIncludeDeviceName(true) + .setServiceData(uuid, serviceData).build(); + data.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + AdvertiseData dataFromParcel = + AdvertiseData.CREATOR.createFromParcel(parcel); + assertEquals(data, dataFromParcel); + } +} diff --git a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java index 25ea2271429..81f4baf99ab 100644 --- a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java @@ -186,7 +186,6 @@ public class ScanFilterTest extends TestCase { parcel.setDataPosition(0); ScanFilter filterFromParcel = ScanFilter.CREATOR.createFromParcel(parcel); - System.out.println(filterFromParcel); assertEquals(filter, filterFromParcel); } } -- GitLab From 53c97aee16c15ca9a3932bde283e604812d74fc4 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 22 Jul 2014 17:00:09 -0700 Subject: [PATCH 0380/1408] Bug 15564216: Report Bluetooth tx/rx/idle activity info and energy reporting Change-Id: I66fd83d8d59fbd93dec8886dfd313a81575e38a5 --- .../BluetoothActivityEnergyInfo.aidl | 19 +++ .../BluetoothActivityEnergyInfo.java | 141 ++++++++++++++++++ .../android/bluetooth/BluetoothAdapter.java | 43 ++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 + 4 files changed, 207 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl create mode 100644 framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl new file mode 100644 index 00000000000..60cbf9f3133 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 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; + +parcelable BluetoothActivityEnergyInfo; diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java new file mode 100644 index 00000000000..ce87329ee43 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 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.os.Parcel; +import android.os.Parcelable; + +/** + * Record of energy and activity information from controller and + * underlying bt stack state.Timestamp the record with system + * time + * @hide + */ +public final class BluetoothActivityEnergyInfo implements Parcelable { + private final int mBluetoothStackState; + private final int mControllerTxTimeMs; + private final int mControllerRxTimeMs; + private final int mControllerIdleTimeMs; + private final int mControllerEnergyUsed; + private final long timestamp; + + public static final int BT_STACK_STATE_INVALID = 0; + public static final int BT_STACK_STATE_STATE_ACTIVE = 1; + public static final int BT_STACK_STATE_STATE_SCANNING = 2; + public static final int BT_STACK_STATE_STATE_IDLE = 3; + + public BluetoothActivityEnergyInfo(int stackState, int txTime, int rxTime, + int idleTime, int energyUsed) { + mBluetoothStackState = stackState; + mControllerTxTimeMs = txTime; + mControllerRxTimeMs = rxTime; + mControllerIdleTimeMs = idleTime; + mControllerEnergyUsed = energyUsed; + timestamp = System.currentTimeMillis(); + } + + @Override + public String toString() { + return "BluetoothActivityEnergyInfo{" + + " timestamp=" + timestamp + + " mBluetoothStackState=" + mBluetoothStackState + + " mControllerTxTimeMs=" + mControllerTxTimeMs + + " mControllerRxTimeMs=" + mControllerRxTimeMs + + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + + " mControllerEnergyUsed=" + mControllerEnergyUsed + + " }"; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { + int stackState = in.readInt(); + int txTime = in.readInt(); + int rxTime = in.readInt(); + int idleTime = in.readInt(); + int energyUsed = in.readInt(); + return new BluetoothActivityEnergyInfo(stackState, txTime, rxTime, + idleTime, energyUsed); + } + public BluetoothActivityEnergyInfo[] newArray(int size) { + return new BluetoothActivityEnergyInfo[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mBluetoothStackState); + out.writeInt(mControllerTxTimeMs); + out.writeInt(mControllerRxTimeMs); + out.writeInt(mControllerIdleTimeMs); + out.writeInt(mControllerEnergyUsed); + } + + public int describeContents() { + return 0; + } + + /** + * @return bt stack reported state + */ + public int getBluetoothStackState() { + return mBluetoothStackState; + } + + /** + * @return tx time in ms + */ + public int getControllerTxTimeMillis() { + return mControllerTxTimeMs; + } + + /** + * @return rx time in ms + */ + public int getControllerRxTimeMillis() { + return mControllerRxTimeMs; + } + + /** + * @return idle time in ms + */ + public int getControllerIdleTimeMillis() { + return mControllerIdleTimeMs; + } + + /** + * product of current(mA), voltage(V) and time(ms) + * @return energy used + */ + public int getControllerEnergyUsed() { + return mControllerEnergyUsed; + } + /** + * @return timestamp(wall clock) of record creation + */ + public long getTimeStamp() { + return timestamp; + } + + /** + * @return if the record is valid + */ + public boolean isValid() { + return ((getControllerTxTimeMillis() !=0) || + (getControllerRxTimeMillis() !=0) || + (getControllerIdleTimeMillis() !=0)); + } +} diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index faf864550d5..453d60c6cd6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -377,6 +377,12 @@ public final class BluetoothAdapter { private static final int ADDRESS_LENGTH = 17; + private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30; + /** @hide */ + public static final int ACTIVITY_ENERGY_INFO_CACHED = 0; + /** @hide */ + public static final int ACTIVITY_ENERGY_INFO_REFRESHED = 1; + /** * Lazily initialized singleton. Guaranteed final after first object * constructed. @@ -934,6 +940,43 @@ public final class BluetoothAdapter { return false; } + /** + * Return the record of {@link BluetoothActivityEnergyInfo} object that + * has the activity and energy info. This can be used to ascertain what + * the controller has been up to, since the last sample. + * @param updateType Type of info, cached vs refreshed. + * + * @return a record with {@link BluetoothActivityEnergyInfo} or null if + * report is unavailable or unsupported + * @hide + */ + public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { + if (getState() != STATE_ON) return null; + try { + BluetoothActivityEnergyInfo record; + if (!mService.isActivityAndEnergyReportingSupported()) { + return null; + } + synchronized(this) { + if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) { + mService.getActivityEnergyInfoFromController(); + wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS); + } + record = mService.reportActivityInfo(); + if (record.isValid()) { + return record; + } else { + return null; + } + } + } catch (InterruptedException e) { + Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e); + } catch (RemoteException e) { + Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); + } + return null; + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index d334b9193c2..ca558033dde 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -18,6 +18,7 @@ package android.bluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothStateChangeCallback; +import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothDevice; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; @@ -88,4 +89,7 @@ interface IBluetooth boolean isMultiAdvertisementSupported(); boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); + boolean isActivityAndEnergyReportingSupported(); + void getActivityEnergyInfoFromController(); + BluetoothActivityEnergyInfo reportActivityInfo(); } -- GitLab From e1dca0a47808184ed20f8e93e67df3dfd724a110 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 23 Jul 2014 20:33:31 -0700 Subject: [PATCH 0381/1408] Move AdvertiseData length check to BluetoothAdvertiser. This will keep AdvertiseData simple and avoid coupling with BluetoothAdapter. Change-Id: I3609652bdea1c6e757bac7d2dada7f8ddf20a5e3 --- .../android/bluetooth/le/AdvertiseData.java | 51 +------------- .../bluetooth/le/BluetoothLeAdvertiser.java | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 34fecfa6098..b137eeb0ce7 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -202,6 +202,7 @@ public final class AdvertiseData implements Parcelable { @Override public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); + @SuppressWarnings("unchecked") List uuids = in.readArrayList(ParcelUuid.class.getClassLoader()); if (uuids != null) { for (ParcelUuid uuid : uuids) { @@ -233,12 +234,6 @@ public final class AdvertiseData implements Parcelable { * Builder for {@link AdvertiseData}. */ public static final class Builder { - private static final int MAX_ADVERTISING_DATA_BYTES = 31; - // Each fields need one byte for field length and another byte for field type. - private static final int OVERHEAD_BYTES_PER_FIELD = 2; - // Flags field will be set by system. - private static final int FLAGS_FIELD_BYTES = 3; - @Nullable private List mServiceUuids = new ArrayList(); private int mManufacturerId = -1; @@ -334,49 +329,5 @@ public final class AdvertiseData implements Parcelable { mServiceData, mManufacturerId, mManufacturerSpecificData, mIncludeTxPowerLevel, mIncludeDeviceName); } - - // Compute the size of the advertisement data. - private int totalBytes() { - int size = FLAGS_FIELD_BYTES; // flags field is always set. - if (mServiceUuids != null) { - int num16BitUuids = 0; - int num32BitUuids = 0; - int num128BitUuids = 0; - for (ParcelUuid uuid : mServiceUuids) { - if (BluetoothUuid.is16BitUuid(uuid)) { - ++num16BitUuids; - } else if (BluetoothUuid.is32BitUuid(uuid)) { - ++num32BitUuids; - } else { - ++num128BitUuids; - } - } - // 16 bit service uuids are grouped into one field when doing advertising. - if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; - } - // 32 bit service uuids are grouped into one field when doing advertising. - if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; - } - // 128 bit service uuids are grouped into one field when doing advertising. - if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; - } - } - if (mServiceData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mServiceData.length; - } - if (mManufacturerSpecificData != null) { - size += OVERHEAD_BYTES_PER_FIELD + mManufacturerSpecificData.length; - } - if (mIncludeTxPowerLevel) { - size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. - } - return size; - } } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index fc53afe7a2a..93d4349ba5b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -18,6 +18,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothManager; @@ -49,6 +50,14 @@ public final class BluetoothLeAdvertiser { private static final String TAG = "BluetoothLeAdvertiser"; + private static final int MAX_ADVERTISING_DATA_BYTES = 31; + // Each fields need one byte for field length and another byte for field type. + private static final int OVERHEAD_BYTES_PER_FIELD = 2; + // Flags field will be set by system. + private static final int FLAGS_FIELD_BYTES = 3; + private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; + private static final int SERVICE_DATA_UUID_LENGTH = 2; + private final IBluetoothManager mBluetoothManager; private final Handler mHandler; private BluetoothAdapter mBluetoothAdapter; @@ -101,6 +110,11 @@ public final class BluetoothLeAdvertiser { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } + if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES || + totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) { + postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); + return; + } if (mLeAdvertisers.containsKey(callback)) { postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); return; @@ -159,6 +173,62 @@ public final class BluetoothLeAdvertiser { } } + // Compute the size of the advertise data. + private int totalBytes(AdvertiseData data) { + if (data == null) { + return 0; + } + int size = FLAGS_FIELD_BYTES; // flags field is always set. + if (data.getServiceUuids() != null) { + int num16BitUuids = 0; + int num32BitUuids = 0; + int num128BitUuids = 0; + for (ParcelUuid uuid : data.getServiceUuids()) { + if (BluetoothUuid.is16BitUuid(uuid)) { + ++num16BitUuids; + } else if (BluetoothUuid.is32BitUuid(uuid)) { + ++num32BitUuids; + } else { + ++num128BitUuids; + } + } + // 16 bit service uuids are grouped into one field when doing advertising. + if (num16BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + } + // 32 bit service uuids are grouped into one field when doing advertising. + if (num32BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + } + // 128 bit service uuids are grouped into one field when doing advertising. + if (num128BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + } + } + if (data.getServiceDataUuid() != null) { + size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH + + byteLength(data.getServiceData()); + } + if (data.getManufacturerId() > 0) { + size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + + byteLength(data.getManufacturerSpecificData()); + } + if (data.getIncludeTxPowerLevel()) { + size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. + } + if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) { + size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length(); + } + return size; + } + + private int byteLength(byte[] array) { + return array == null ? 0 : array.length; + } + /** * Bluetooth GATT interface callbacks for advertising. */ -- GitLab From 010675f9b7b2879c78ba8042ec9edf3137b1349c Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 24 Jul 2014 12:22:24 -0700 Subject: [PATCH 0382/1408] cleanup : delete BLE advertising related hidden state references in Bluetooth Adapter Change-Id: Ia58a46392157e274f8fc4696e8e25af480eb2d2a --- .../android/bluetooth/BluetoothAdapter.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 453d60c6cd6..b1cbb133231 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -354,27 +354,9 @@ public final class BluetoothAdapter { /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; - /** States for Bluetooth LE advertising */ - /** @hide */ - public static final int STATE_ADVERTISE_STARTING = 0; - /** @hide */ - public static final int STATE_ADVERTISE_STARTED = 1; - /** @hide */ - public static final int STATE_ADVERTISE_STOPPING = 2; - /** @hide */ - public static final int STATE_ADVERTISE_STOPPED = 3; - /** - * Force stopping advertising without callback in case the advertising app dies. - * @hide - */ - public static final int STATE_ADVERTISE_FORCE_STOPPING = 4; - /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; - /** @hide */ - public static final int ADVERTISE_CALLBACK_SUCCESS = 0; - private static final int ADDRESS_LENGTH = 17; private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30; -- GitLab From 4c2da32c9fc3ce1ef25ff3f91906722e66c831d9 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 23 Jul 2014 23:34:00 -0700 Subject: [PATCH 0383/1408] Use Scanner for legacy LE scans(1/2). Also removed different scan methods from IBluetoothGatt and make BluetoothLeScanner and BluetoothLEAdvertiser singleton. Change-Id: Ifa2e950b50f100f5507a6dace3bd70db18d7f9ca --- .../android/bluetooth/BluetoothAdapter.java | 364 +++++------------- .../android/bluetooth/IBluetoothGatt.aidl | 6 +- .../bluetooth/le/BluetoothLeScanner.java | 3 +- 3 files changed, 95 insertions(+), 278 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b1cbb133231..42b8dbf1a7f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -21,6 +21,8 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; @@ -371,12 +373,14 @@ public final class BluetoothAdapter { */ private static BluetoothAdapter sAdapter; + private static BluetoothLeScanner sBluetoothLeScanner; + private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; + private final IBluetoothManager mManagerService; private IBluetooth mService; - private final Map mLeScanClients; - private final Handler mHandler; // Handler to post the advertise callback to run on main thread. private final Object mLock = new Object(); + private final Map mLeScanClients; /** * Get a handle to the default local Bluetooth adapter. @@ -411,8 +415,7 @@ public final class BluetoothAdapter { mService = managerService.registerAdapter(mManagerCallback); } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; - mLeScanClients = new HashMap(); - mHandler = new Handler(Looper.getMainLooper()); + mLeScanClients = new HashMap(); } /** @@ -451,19 +454,40 @@ public final class BluetoothAdapter { } /** - * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. + * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations, or + * null if Bluetooth LE Advertising is not support on this device. + *

        + * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported + * on this device before calling this method. */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - // TODO: Return null if this feature is not supported by hardware. - return new BluetoothLeAdvertiser(mManagerService); + if (getState() != STATE_ON) { + return null; + } + if (!isMultipleAdvertisementSupported()) { + return null; + } + synchronized(mLock) { + if (sBluetoothLeAdvertiser == null) { + sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); + } + } + return sBluetoothLeAdvertiser; } /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ public BluetoothLeScanner getBluetoothLeScanner() { - // TODO: Return null if BLE scan is not supported by hardware. - return new BluetoothLeScanner(mManagerService); + if (getState() != STATE_ON) { + return null; + } + synchronized(mLock) { + if (sBluetoothLeScanner == null) { + sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); + } + } + return sBluetoothLeScanner; } /** @@ -1625,13 +1649,17 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) { + public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); - if (callback == null) { if (DBG) Log.e(TAG, "startLeScan: null callback"); return false; } + BluetoothLeScanner scanner = getBluetoothLeScanner(); + if (scanner == null) { + if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); + return false; + } synchronized(mLeScanClients) { if (mLeScanClients.containsKey(callback)) { @@ -1646,13 +1674,50 @@ public final class BluetoothAdapter { return false; } - UUID uuid = UUID.randomUUID(); - GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids); - iGatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.scanStarted()) { - mLeScanClients.put(callback, wrapper); - return true; + ScanCallback scanCallback = new ScanCallback() { + @Override + public void onScanResult(int callbackType, ScanResult result) { + if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { + // Should not happen. + Log.e(TAG, "LE Scan has already started"); + return; + } + ScanRecord scanRecord = result.getScanRecord(); + if (scanRecord == null) { + return; + } + if (serviceUuids != null) { + List uuids = new ArrayList(); + for (UUID uuid : serviceUuids) { + uuids.add(new ParcelUuid(uuid)); + } + List scanServiceUuids = scanRecord.getServiceUuids(); + if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) { + if (DBG) Log.d(TAG, "uuids does not match"); + return; + } + } + callback.onLeScan(result.getDevice(), result.getRssi(), + scanRecord.getBytes()); + } + }; + ScanSettings settings = new ScanSettings.Builder() + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + + List filters = new ArrayList(); + if (serviceUuids != null && serviceUuids.length > 0) { + // Note scan filter does not support matching an UUID array so we put one + // UUID to hardware and match the whole array in callback. + ScanFilter filter = new ScanFilter.Builder().setServiceUuid( + new ParcelUuid(serviceUuids[0])).build(); + filters.add(filter); } + scanner.startScan(filters, settings, scanCallback); + + mLeScanClients.put(callback, scanCallback); + return true; + } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -1672,264 +1737,17 @@ public final class BluetoothAdapter { @Deprecated public void stopLeScan(LeScanCallback callback) { if (DBG) Log.d(TAG, "stopLeScan()"); - GattCallbackWrapper wrapper; - synchronized(mLeScanClients) { - wrapper = mLeScanClients.remove(callback); - if (wrapper == null) return; - } - wrapper.stopLeScan(); - } - - /** - * Bluetooth GATT interface callbacks - */ - private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { - private static final int LE_CALLBACK_REG_TIMEOUT = 2000; - private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; - - private final LeScanCallback mLeScanCb; - - // mLeHandle 0: not registered - // -1: scan stopped - // >0: registered and scan started - private int mLeHandle; - private final UUID[] mScanFilter; - private WeakReference mBluetoothAdapter; - - public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, - LeScanCallback leScanCb, UUID[] uuid) { - mBluetoothAdapter = new WeakReference(bluetoothAdapter); - mLeScanCb = leScanCb; - mScanFilter = uuid; - mLeHandle = 0; - } - - public boolean scanStarted() { - return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); - } - - private boolean waitForRegisteration(int maxWaitCount) { - boolean started = false; - synchronized(this) { - if (mLeHandle == -1) return false; - int count = 0; - // wait for callback registration and LE scan to start - while (mLeHandle == 0 && count < maxWaitCount) { - try { - wait(LE_CALLBACK_REG_TIMEOUT); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); - } - count++; - } - started = (mLeHandle > 0); - } - return started; - } - - public void stopLeScan() { - synchronized(this) { - if (mLeHandle <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); - return; - } - BluetoothAdapter adapter = mBluetoothAdapter.get(); - if (adapter != null) { - try { - IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - iGatt.stopScan(mLeHandle, false); - iGatt.unregisterClient(mLeHandle); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop scan and unregister" + e); - } - } else { - Log.e(TAG, "stopLeScan, BluetoothAdapter is null"); - } - mLeHandle = -1; - notifyAll(); - } - } - - /** - * Application interface registered - app is ready to go - */ - public void onClientRegistered(int status, int clientIf) { - if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + - " clientIf=" + clientIf); - synchronized(this) { - if (mLeHandle == -1) { - if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); - } - - if (status == BluetoothGatt.GATT_SUCCESS) { - mLeHandle = clientIf; - IBluetoothGatt iGatt = null; - try { - BluetoothAdapter adapter = mBluetoothAdapter.get(); - if (adapter != null) { - iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); - } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); - } - } else { - Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); - mLeHandle = -1; - } - } catch (RemoteException e) { - Log.e(TAG, "fail to start le scan: " + e); - mLeHandle = -1; - } - if (mLeHandle == -1) { - // registration succeeded but start scan or advertise failed - if (iGatt != null) { - try { - iGatt.unregisterClient(mLeHandle); - } catch (RemoteException e) { - Log.e(TAG, "fail to unregister callback: " + mLeHandle + - " error: " + e); - } - } - } - } else { - // registration failed - mLeHandle = -1; - } - notifyAll(); - } - } - - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op + BluetoothLeScanner scanner = getBluetoothLeScanner(); + if (scanner == null) { + return; } - - /** - * Callback reporting an LE scan result. - * @hide - */ - public void onScanResult(String address, int rssi, byte[] advData) { - if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - // Check null in case the scan has been stopped - synchronized(this) { - if (mLeHandle <= 0) return; + synchronized (mLeScanClients) { + ScanCallback scanCallback = mLeScanClients.remove(callback); + if (scanCallback == null) { + if (DBG) Log.d(TAG, "scan not started yet"); + return; } - try { - BluetoothAdapter adapter = mBluetoothAdapter.get(); - if (adapter == null) { - Log.d(TAG, "onScanResult, BluetoothAdapter null"); - return; - } - mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - public void onSearchComplete(String address, int status) { - // no op - } - - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - public void onExecuteWrite(String address, int status) { - // no op - } - - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - public void onAdvertiseStateChange(int advertiseState, int status) { - } - - @Override - public void onMultiAdvertiseCallback(int status) { - // no op - } - - @Override - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - - @Override - public void onConnectionCongested(String address, boolean congested) { - // no op - } - - @Override - public void onBatchScanResults(List results) { - // no op - } - - @Override - public void onFoundOrLost(boolean onFound, String address,int rssi, - byte[] advData) { - // no op + scanner.stopScan(scanCallback); } } } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 533be13021d..edf823e60d3 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -33,10 +33,8 @@ import android.bluetooth.IBluetoothGattServerCallback; interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); - void startScan(in int appIf, in boolean isServer); - void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); - void startScanWithFilters(in int appIf, in boolean isServer, - in ScanSettings settings, in List filters); + void startScan(in int appIf, in boolean isServer, in ScanSettings settings, + in List filters); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 980f717a2f6..8e7d400898d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -151,6 +151,7 @@ public final class BluetoothLeScanner { synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { + if (DBG) Log.d(TAG, "could not find callback wrapper"); return; } wrapper.stopLeScan(); @@ -266,7 +267,7 @@ public final class BluetoothLeScanner { if (status == BluetoothGatt.GATT_SUCCESS) { mClientIf = clientIf; try { - mBluetoothGatt.startScanWithFilters(mClientIf, false, mSettings, mFilters); + mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters); } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; -- GitLab From 8679ce4a8badf10a65b80c6c16bbe80577a72933 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 24 Jul 2014 17:06:57 -0700 Subject: [PATCH 0384/1408] Fix bug of ScanSettings. Add unit test. b/16318637. Change-Id: I3fbc1212c1712faa0c29132f3dc9cfc1d58af26b --- .../android/bluetooth/le/ScanSettings.java | 65 +++++++++++-------- .../bluetooth/le/ScanSettingsTest.java | 64 ++++++++++++++++++ 2 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 framework/tests/src/android/bluetooth/le/ScanSettingsTest.java diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 2f86d09ec08..b2ee6a82c94 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -21,38 +21,37 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} - * to define the parameters for the scan. + * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the + * parameters for the scan. */ public final class ScanSettings implements Parcelable { /** - * Perform Bluetooth LE scan in low power mode. - * This is the default scan mode as it consumes the least power. + * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the + * least power. */ public static final int SCAN_MODE_LOW_POWER = 0; /** - * Perform Bluetooth LE scan in balanced power mode. - * Scan results are returned at a rate that provides a good trade-off between scan - * frequency and power consumption. + * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that + * provides a good trade-off between scan frequency and power consumption. */ public static final int SCAN_MODE_BALANCED = 1; /** - * Scan using highest duty cycle. - * It's recommended to only use this mode when the application is running in the foreground. + * Scan using highest duty cycle. It's recommended to only use this mode when the application is + * running in the foreground. */ public static final int SCAN_MODE_LOW_LATENCY = 2; /** - * Trigger a callback for every Bluetooth advertisement found that matches the - * filter criteria. If no filter is active, all advertisement packets are reported. + * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. + * If no filter is active, all advertisement packets are reported. */ public static final int CALLBACK_TYPE_ALL_MATCHES = 1; /** - * A result callback is only triggered for the first advertisement packet received that - * matches the filter criteria. + * A result callback is only triggered for the first advertisement packet received that matches + * the filter criteria. */ public static final int CALLBACK_TYPE_FIRST_MATCH = 2; @@ -63,15 +62,17 @@ public final class ScanSettings implements Parcelable { public static final int CALLBACK_TYPE_MATCH_LOST = 4; /** - * Request full scan results which contain the device, rssi, advertising data, scan response - * as well as the scan timestamp. + * Request full scan results which contain the device, rssi, advertising data, scan response as + * well as the scan timestamp. */ public static final int SCAN_RESULT_TYPE_FULL = 0; /** * Request abbreviated scan results which contain the device, rssi and scan timestamp. - *

        Note: It is possible for an application to get more scan results than - * it asked for, if there are multiple apps using this type. + *

        + * Note: It is possible for an application to get more scan results than it asked for, if + * there are multiple apps using this type. + * * @hide */ @SystemApi @@ -109,11 +110,11 @@ public final class ScanSettings implements Parcelable { } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelaySeconds) { + long reportDelayMillis) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; - mReportDelayMillis = reportDelaySeconds; + mReportDelayMillis = reportDelayMillis; } private ScanSettings(Parcel in) { @@ -184,15 +185,24 @@ public final class ScanSettings implements Parcelable { * @throws IllegalArgumentException If the {@code callbackType} is invalid. */ public Builder setCallbackType(int callbackType) { - if (callbackType < CALLBACK_TYPE_ALL_MATCHES - || callbackType > (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST) - || (callbackType & CALLBACK_TYPE_ALL_MATCHES) != CALLBACK_TYPE_ALL_MATCHES) { + + if (!isValidCallbackType(callbackType)) { throw new IllegalArgumentException("invalid callback type - " + callbackType); } mCallbackType = callbackType; return this; } + // Returns true if the callbackType is valid. + private boolean isValidCallbackType(int callbackType) { + if (callbackType == CALLBACK_TYPE_ALL_MATCHES || + callbackType == CALLBACK_TYPE_FIRST_MATCH || + callbackType == CALLBACK_TYPE_MATCH_LOST) { + return true; + } + return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST); + } + /** * Set scan result type for Bluetooth LE scan. * @@ -215,16 +225,15 @@ public final class ScanSettings implements Parcelable { /** * Set report delay timestamp for Bluetooth LE scan. - * @param reportDelayMillis Set to 0 to be notified of results immediately. - * Values > 0 causes the scan results to be queued - * up and delivered after the requested delay or when - * the internal buffers fill up. - * @throws IllegalArgumentException If {@code reportDelaySeconds} < 0. * + * @param reportDelayMillis Set to 0 to be notified of results immediately. Values > 0 + * causes the scan results to be queued up and delivered after the requested + * delay or when the internal buffers fill up. + * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. */ public Builder setReportDelayMillis(long reportDelayMillis) { if (reportDelayMillis < 0) { - throw new IllegalArgumentException("reportDelaySeconds must be > 0"); + throw new IllegalArgumentException("reportDelayMillis must be > 0"); } mReportDelayMillis = reportDelayMillis; return this; diff --git a/framework/tests/src/android/bluetooth/le/ScanSettingsTest.java b/framework/tests/src/android/bluetooth/le/ScanSettingsTest.java new file mode 100644 index 00000000000..7c42c3b4677 --- /dev/null +++ b/framework/tests/src/android/bluetooth/le/ScanSettingsTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 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.le; + +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Test for Bluetooth LE {@link ScanSettings}. + */ +public class ScanSettingsTest extends TestCase { + + @SmallTest + public void testCallbackType() { + ScanSettings.Builder builder = new ScanSettings.Builder(); + builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES); + builder.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH); + builder.setCallbackType(ScanSettings.CALLBACK_TYPE_MATCH_LOST); + builder.setCallbackType( + ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST); + try { + builder.setCallbackType( + ScanSettings.CALLBACK_TYPE_ALL_MATCHES | ScanSettings.CALLBACK_TYPE_MATCH_LOST); + fail("should have thrown IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // nothing to do + } + + try { + builder.setCallbackType( + ScanSettings.CALLBACK_TYPE_ALL_MATCHES | + ScanSettings.CALLBACK_TYPE_FIRST_MATCH); + fail("should have thrown IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // nothing to do + } + + try { + builder.setCallbackType( + ScanSettings.CALLBACK_TYPE_ALL_MATCHES | + ScanSettings.CALLBACK_TYPE_FIRST_MATCH | + ScanSettings.CALLBACK_TYPE_MATCH_LOST); + fail("should have thrown IllegalArgumentException!"); + } catch (IllegalArgumentException e) { + // nothing to do + } + + } +} -- GitLab From 44523493d26508d1898fa4c1704ac720373eac2f Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 25 Jul 2014 15:14:55 -0700 Subject: [PATCH 0385/1408] Split manu id and manu data. b/16407380 Change-Id: I39f6ce0ab4cccadec809f0a4f518f5d2c77a90b7 --- .../java/android/bluetooth/le/ScanFilter.java | 35 ++++++++++++++----- .../java/android/bluetooth/le/ScanRecord.java | 13 +++---- .../java/android/bluetooth/le/ScanResult.java | 4 +-- .../android/bluetooth/le/ScanRecordTest.java | 9 +++-- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 0ae11b0cd99..2ce18b0a83e 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -107,7 +107,7 @@ public final class ScanFilter implements Parcelable { dest.writeParcelable(mServiceUuidMask, flags); } } - dest.writeInt(mServiceDataUuid == null? 0 : 1); + dest.writeInt(mServiceDataUuid == null ? 0 : 1); if (mServiceDataUuid != null) { dest.writeParcelable(mServiceDataUuid, flags); dest.writeInt(mServiceData == null ? 0 : mServiceData.length); @@ -234,6 +234,14 @@ public final class ScanFilter implements Parcelable { return mServiceDataMask; } + /** + * @hide + */ + @Nullable + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + /** * Returns the manufacturer id. -1 if the manufacturer filter is not set. */ @@ -287,15 +295,21 @@ public final class ScanFilter implements Parcelable { } // Service data match - if (mServiceData != null && - !matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData())) { - return false; + if (mServiceData != null) { + if (!Objects.equals(mServiceDataUuid, scanRecord.getServiceDataUuid()) || + !matchesPartialData(mServiceData, mServiceDataMask, + scanRecord.getServiceData())) { + return false; + } } // Manufacturer data match. - if (mManufacturerData != null && !matchesPartialData(mManufacturerData, - mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { - return false; + if (mManufacturerData != null) { + if (mManufacturerId != scanRecord.getManufacturerId() || + !matchesPartialData(mManufacturerData, + mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { + return false; + } } // All filters match. return true; @@ -353,7 +367,8 @@ public final class ScanFilter implements Parcelable { public String toString() { return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + mDeviceAddress - + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceData=" + + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + ", mManufacturerData=" + Arrays.toString(mManufacturerData) @@ -363,7 +378,7 @@ public final class ScanFilter implements Parcelable { @Override public int hashCode() { return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, mManufacturerData, - mManufacturerDataMask, mServiceData, mServiceDataMask, + mManufacturerDataMask, mServiceDataUuid, mServiceData, mServiceDataMask, mServiceUuid, mServiceUuidMask); } @@ -381,6 +396,7 @@ public final class ScanFilter implements Parcelable { mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && + Objects.deepEquals(mServiceDataUuid, other.mServiceDataUuid) && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && @@ -498,6 +514,7 @@ public final class ScanFilter implements Parcelable { "size mismatch for service data and service data mask"); } } + mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = serviceDataMask; return this; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index dd033080173..e564c7d66dd 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -225,20 +225,21 @@ public final class ScanRecord { txPowerLevel = scanRecord[currentPos]; break; case DATA_TYPE_SERVICE_DATA: - serviceData = extractBytes(scanRecord, currentPos, dataLength); - // The first two bytes of the service data are service data UUID. + // The first two bytes of the service data are service data UUID in little + // endian. The rest bytes are service data. int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); + serviceData = extractBytes(scanRecord, currentPos + 2, dataLength - 2); break; case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: - manufacturerSpecificData = extractBytes(scanRecord, currentPos, - dataLength); // The first two bytes of the manufacturer specific data are // manufacturer ids in little endian. - manufacturerId = ((manufacturerSpecificData[1] & 0xFF) << 8) + - (manufacturerSpecificData[0] & 0xFF); + manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + + (scanRecord[currentPos] & 0xFF); + manufacturerSpecificData = extractBytes(scanRecord, currentPos + 2, + dataLength - 2); break; default: // Just ignore, we don't handle such data type. diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 9aee20094a8..a0bdaffd866 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -69,7 +69,7 @@ public final class ScanResult implements Parcelable { dest.writeInt(0); } if (mScanRecord != null) { - dest.writeInt(mScanRecord.getBytes().length); + dest.writeInt(1); dest.writeByteArray(mScanRecord.getBytes()); } else { dest.writeInt(0); @@ -145,7 +145,7 @@ public final class ScanResult implements Parcelable { @Override public String toString() { return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + mScanRecord.toString() + ", mRssi=" + mRssi + ", mTimestampNanos=" + + Objects.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos=" + mTimestampNanos + '}'; } diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index e259bcc9add..ccdd90ae457 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -27,8 +27,7 @@ import java.util.Arrays; /** * Unit test cases for {@link ScanRecord}. *

        - * To run this test, use adb shell am instrument -e class - * 'android.bluetooth.ScanRecordTest' -w + * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanRecordTest' -w * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' */ public class ScanRecordTest extends TestCase { @@ -54,13 +53,13 @@ public class ScanRecordTest extends TestCase { assertEquals("Ped", data.getDeviceName()); assertEquals(-20, data.getTxPowerLevel()); - assertEquals(224, data.getManufacturerId()); + assertEquals(0x00e0, data.getManufacturerId()); assertArrayEquals(new byte[] { - (byte) 0xe0, 0x00, 0x02, 0x15 }, data.getManufacturerSpecificData()); + 0x02, 0x15 }, data.getManufacturerSpecificData()); assertEquals(uuid2, data.getServiceDataUuid()); assertArrayEquals(new byte[] { - 0x0b, 0x11, 0x50, 0x64 }, data.getServiceData()); + 0x50, 0x64 }, data.getServiceData()); } // Assert two byte arrays are equal. -- GitLab From 3c6b1fa59a38fe9f93fc78498623a819983d8896 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 25 Jul 2014 15:16:11 -0700 Subject: [PATCH 0386/1408] LE: Do not invoke callback if retrying read/write operation When a descriptor read/write operation fails with insufficient authentication/encryption, it will automatically be tried again with elevated security requirements. The first (failed) attempt should not be reported to the application requesting the read/write, since it will get the 2nd callback once the retry succeeds/fails. Change-Id: Ia27b91afa63b6f9ffdc597d59c24d29f3d8a1008 --- framework/java/android/bluetooth/BluetoothGatt.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 27f2011fcb5..1fe43ec77c8 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -491,6 +491,7 @@ public final class BluetoothGatt implements BluetoothProfile { mService.readDescriptor(mClientIf, address, srvcType, srvcInstId, srvcUuid, charInstId, charUuid, descrInstId, descrUuid, AUTHENTICATION_MITM); + return; } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -544,6 +545,7 @@ public final class BluetoothGatt implements BluetoothProfile { srvcType, srvcInstId, srvcUuid, charInstId, charUuid, descrInstId, descrUuid, characteristic.getWriteType(), AUTHENTICATION_MITM, descriptor.getValue()); + return; } catch (RemoteException e) { Log.e(TAG,"",e); } -- GitLab From b6ff2e28732f5402aa208197ef088fcee4e16d51 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 28 Jul 2014 21:46:44 -0700 Subject: [PATCH 0387/1408] Make scan filter accepts prefix. Add tests. Change-Id: I714dd6d95ad2bc874dce1106b325762b19159119 --- .../java/android/bluetooth/le/ScanFilter.java | 51 ++++++++------ .../android/bluetooth/le/ScanFilterTest.java | 66 +++++++++++++------ 2 files changed, 77 insertions(+), 40 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 2ce18b0a83e..30aaf2e4cc2 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -110,21 +110,27 @@ public final class ScanFilter implements Parcelable { dest.writeInt(mServiceDataUuid == null ? 0 : 1); if (mServiceDataUuid != null) { dest.writeParcelable(mServiceDataUuid, flags); - dest.writeInt(mServiceData == null ? 0 : mServiceData.length); + dest.writeInt(mServiceData == null ? 0 : 1); if (mServiceData != null) { + dest.writeInt(mServiceData.length); dest.writeByteArray(mServiceData); - dest.writeInt(mServiceDataMask == null ? 0 : mServiceDataMask.length); + + dest.writeInt(mServiceDataMask == null ? 0 : 1); if (mServiceDataMask != null) { + dest.writeInt(mServiceDataMask.length); dest.writeByteArray(mServiceDataMask); } } } dest.writeInt(mManufacturerId); - dest.writeInt(mManufacturerData == null ? 0 : mManufacturerData.length); + dest.writeInt(mManufacturerData == null ? 0 : 1); if (mManufacturerData != null) { + dest.writeInt(mManufacturerData.length); dest.writeByteArray(mManufacturerData); - dest.writeInt(mManufacturerDataMask == null ? 0 : mManufacturerDataMask.length); + + dest.writeInt(mManufacturerDataMask == null ? 0 : 1); if (mManufacturerDataMask != null) { + dest.writeInt(mManufacturerDataMask.length); dest.writeByteArray(mManufacturerDataMask); } } @@ -164,29 +170,31 @@ public final class ScanFilter implements Parcelable { if (in.readInt() == 1) { ParcelUuid servcieDataUuid = in.readParcelable(ParcelUuid.class.getClassLoader()); - int serviceDataLength = in.readInt(); - if (serviceDataLength > 0) { + if (in.readInt() == 1) { + int serviceDataLength = in.readInt(); byte[] serviceData = new byte[serviceDataLength]; in.readByteArray(serviceData); - builder.setServiceData(servcieDataUuid, serviceData); - int serviceDataMaskLength = in.readInt(); - if (serviceDataMaskLength > 0) { + if (in.readInt() == 0) { + builder.setServiceData(servcieDataUuid, serviceData); + } else { + int serviceDataMaskLength = in.readInt(); byte[] serviceDataMask = new byte[serviceDataMaskLength]; in.readByteArray(serviceDataMask); builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); + servcieDataUuid, serviceData, serviceDataMask); } } } int manufacturerId = in.readInt(); - int manufacturerDataLength = in.readInt(); - if (manufacturerDataLength > 0) { + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); byte[] manufacturerData = new byte[manufacturerDataLength]; in.readByteArray(manufacturerData); - builder.setManufacturerData(manufacturerId, manufacturerData); - int manufacturerDataMaskLength = in.readInt(); - if (manufacturerDataMaskLength > 0) { + if (in.readInt() == 0) { + builder.setManufacturerData(manufacturerId, manufacturerData); + } else { + int manufacturerDataMaskLength = in.readInt(); byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; in.readByteArray(manufacturerDataMask); builder.setManufacturerData(manufacturerId, manufacturerData, @@ -349,12 +357,17 @@ public final class ScanFilter implements Parcelable { // Check whether the data pattern matches the parsed data. private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { - if (dataMask == null) { - return Arrays.equals(data, parsedData); - } - if (parsedData == null) { + if (parsedData == null || parsedData.length < data.length) { return false; } + if (dataMask == null) { + for (int i = 0; i < data.length; ++i) { + if (parsedData[i] != data[i]) { + return false; + } + } + return true; + } for (int i = 0; i < data.length; ++i) { if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { return false; diff --git a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java index 81f4baf99ab..35da4bceb62 100644 --- a/framework/tests/src/android/bluetooth/le/ScanFilterTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanFilterTest.java @@ -96,38 +96,56 @@ public class ScanFilterTest extends TestCase { @SmallTest public void testsetServiceDataFilter() { byte[] setServiceData = new byte[] { - 0x0b, 0x11, 0x50, 0x64 }; + 0x50, 0x64 }; ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); assertTrue("service data filter fails", filter.matches(mScanResult)); - byte[] nonMatchData = new byte[] { - 0x0b, 0x01, 0x50, 0x64 }; - filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build(); - assertFalse("service data filter fails", filter.matches(mScanResult)); + byte[] emptyData = new byte[0]; + filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build(); + assertTrue("service data filter fails", filter.matches(mScanResult)); + byte[] prefixData = new byte[] { + 0x50 }; + filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build(); + assertTrue("service data filter fails", filter.matches(mScanResult)); + + byte[] nonMatchData = new byte[] { + 0x51, 0x64 }; byte[] mask = new byte[] { - (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; + (byte) 0x00, (byte) 0xFF }; filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build(); assertTrue("partial service data filter fails", filter.matches(mScanResult)); + + filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build(); + assertFalse("service data filter fails", filter.matches(mScanResult)); } @SmallTest public void testManufacturerSpecificData() { byte[] setManufacturerData = new byte[] { - (byte) 0xE0, 0x00, 0x02, 0x15 }; - int manufacturerId = 224; + 0x02, 0x15 }; + int manufacturerId = 0xE0; ScanFilter filter = mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build(); - assertTrue("setManufacturerData filter fails", filter.matches(mScanResult)); + assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); + + byte[] emptyData = new byte[0]; + filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build(); + assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); + + byte[] prefixData = new byte[] { + 0x02 }; + filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build(); + assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); + // Test data mask byte[] nonMatchData = new byte[] { - (byte) 0xF0, 0x00, 0x02, 0x15 }; + 0x02, 0x14 }; filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build(); - assertFalse("setManufacturerData filter fails", filter.matches(mScanResult)); - + assertFalse("manufacturer data filter fails", filter.matches(mScanResult)); byte[] mask = new byte[] { - (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + (byte) 0xFF, (byte) 0x00 }; filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build(); assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult)); @@ -153,27 +171,33 @@ public class ScanFilterTest extends TestCase { ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); testReadWriteParcelForFilter(filter); - byte[] setServiceData = new byte[] { - 0x0b, 0x11, 0x50, 0x64 }; + byte[] serviceData = new byte[] { + 0x50, 0x64 }; ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); + filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build(); + testReadWriteParcelForFilter(filter); + + filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build(); testReadWriteParcelForFilter(filter); byte[] serviceDataMask = new byte[] { - (byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData, serviceDataMask) + (byte) 0xFF, (byte) 0xFF }; + filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask) .build(); testReadWriteParcelForFilter(filter); byte[] manufacturerData = new byte[] { - (byte) 0xE0, 0x00, 0x02, 0x15 }; - int manufacturerId = 224; + 0x02, 0x15 }; + int manufacturerId = 0xE0; filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build(); testReadWriteParcelForFilter(filter); + filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build(); + testReadWriteParcelForFilter(filter); + byte[] manufacturerDataMask = new byte[] { - (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF + (byte) 0xFF, (byte) 0xFF }; filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData, manufacturerDataMask).build(); -- GitLab From d4a751b36d10c5d3b6055b1fab9ce7fbd3784351 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 29 Jul 2014 13:42:30 -0700 Subject: [PATCH 0388/1408] Fix crash in Stopping/Starting BLE advertising after BluetoothAdapter cycling Bug 16528460 Change-Id: Iad852bc93d307c5ca5f7e42f24248aa02c105e8a --- .../android/bluetooth/le/BluetoothLeAdvertiser.java | 10 +++++++++- .../java/android/bluetooth/le/BluetoothLeScanner.java | 11 +++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 93d4349ba5b..4d128e7439d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -107,6 +107,7 @@ public final class BluetoothLeAdvertiser { public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { + checkAdapterState(); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } @@ -153,13 +154,13 @@ public final class BluetoothLeAdvertiser { * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. */ public void stopAdvertising(final AdvertiseCallback callback) { + checkAdapterState(); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); if (wrapper == null) return; - try { IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt(); if (gatt != null) @@ -459,6 +460,13 @@ public final class BluetoothLeAdvertiser { } } + //TODO: move this api to a common util class. + private void checkAdapterState() { + if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { + throw new IllegalStateException("BT Adapter is not turned ON"); + } + } + private void postCallbackFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 8e7d400898d..f100ddc4760 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -80,6 +80,7 @@ public final class BluetoothLeScanner { * @throws IllegalArgumentException If {@code callback} is null. */ public void startScan(final ScanCallback callback) { + checkAdapterState(); if (callback == null) { throw new IllegalArgumentException("callback is null"); } @@ -98,6 +99,7 @@ public final class BluetoothLeScanner { */ public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { + checkAdapterState(); if (settings == null || callback == null) { throw new IllegalArgumentException("settings or callback is null"); } @@ -148,6 +150,7 @@ public final class BluetoothLeScanner { * @param callback */ public void stopScan(ScanCallback callback) { + checkAdapterState(); synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { @@ -167,6 +170,7 @@ public final class BluetoothLeScanner { * used to start scan. */ public void flushPendingScanResults(ScanCallback callback) { + checkAdapterState(); if (callback == null) { throw new IllegalArgumentException("callback cannot be null!"); } @@ -445,6 +449,13 @@ public final class BluetoothLeScanner { } } + //TODO: move this api to a common util class. + private void checkAdapterState() { + if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { + throw new IllegalStateException("BT Adapter is not turned ON"); + } + } + private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { @Override -- GitLab From 1398207096da4b56f1558cf31a90c2ca70a867fc Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 29 Jul 2014 12:33:03 -0700 Subject: [PATCH 0389/1408] Service data and manufacturer data can be repeated fields. b/16635899 Change-Id: I73f1f4effd3f0e38cd427297eb9d22f3ba285d61 --- .../android/bluetooth/le/AdvertiseData.java | 162 ++++++++---------- .../bluetooth/le/BluetoothLeAdvertiser.java | 8 +- .../java/android/bluetooth/le/ScanFilter.java | 19 +- .../java/android/bluetooth/le/ScanRecord.java | 89 +++++----- .../java/android/bluetooth/le/Utils.java | 126 ++++++++++++++ .../bluetooth/le/AdvertiseDataTest.java | 8 +- .../android/bluetooth/le/ScanRecordTest.java | 8 +- 7 files changed, 261 insertions(+), 159 deletions(-) create mode 100644 framework/java/android/bluetooth/le/Utils.java diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index b137eeb0ce7..843cd846f0f 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -17,14 +17,15 @@ package android.bluetooth.le; import android.annotation.Nullable; -import android.bluetooth.BluetoothUuid; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.SparseArray; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -42,27 +43,18 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; - - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; - + private final SparseArray mManufacturerSpecificData; + private final Map mServiceData; private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, boolean includeTxPowerLevel, + SparseArray manufacturerData, + Map serviceData, + boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; + mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; mIncludeDeviceName = includeDeviceName; @@ -77,32 +69,17 @@ public final class AdvertiseData implements Parcelable { } /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. + * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The + * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ - public int getManufacturerId() { - return mManufacturerId; - } - - /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. The first 2 bytes of the data contain the company id. - */ - public byte[] getManufacturerSpecificData() { + public SparseArray getManufacturerSpecificData() { return mManufacturerSpecificData; } /** - * Returns a 16-bit UUID of the service that the service data is associated with. - */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** - * Returns service data. + * Returns a map of 16-bit UUID and its corresponding service data. */ - public byte[] getServiceData() { + public Map getServiceData() { return mServiceData; } @@ -125,8 +102,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData, - mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, + mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -142,20 +119,17 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && - mManufacturerId == other.mManufacturerId && - Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) && - Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && - Objects.deepEquals(mServiceData, other.mServiceData) && + Utils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + Utils.equals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override public String toString() { - return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" - + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" + + Utils.toString(mManufacturerSpecificData) + ", mServiceData=" + + Utils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + mIncludeDeviceName + "]"; } @@ -169,21 +143,30 @@ public final class AdvertiseData implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeList(mServiceUuids); - dest.writeInt(mManufacturerId); - if (mManufacturerSpecificData == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(mManufacturerSpecificData.length); - dest.writeByteArray(mManufacturerSpecificData); + // mManufacturerSpecificData could not be null. + dest.writeInt(mManufacturerSpecificData.size()); + for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { + dest.writeInt(mManufacturerSpecificData.keyAt(i)); + byte[] data = mManufacturerSpecificData.valueAt(i); + if (data == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(data.length); + dest.writeByteArray(data); + } } - dest.writeParcelable(mServiceDataUuid, flags); - if (mServiceData == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(mServiceData.length); - dest.writeByteArray(mServiceData); + dest.writeInt(mServiceData.size()); + for (ParcelUuid uuid : mServiceData.keySet()) { + dest.writeParcelable(uuid, flags); + byte[] data = mServiceData.get(uuid); + if (data == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(data.length); + dest.writeByteArray(data); + } } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); @@ -209,20 +192,26 @@ public final class AdvertiseData implements Parcelable { builder.addServiceUuid(uuid); } } - int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.setManufacturerData(manufacturerId, manufacturerData); + int manufacturerSize = in.readInt(); + for (int i = 0; i < manufacturerSize; ++i) { + int manufacturerId = in.readInt(); + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.addManufacturerData(manufacturerId, manufacturerData); + } } - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.setServiceData(serviceDataUuid, serviceData); + int serviceDataSize = in.readInt(); + for (int i = 0; i < serviceDataSize; ++i) { + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + if (in.readInt() == 1) { + int serviceDataLength = in.readInt(); + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.addServiceData(serviceDataUuid, serviceData); + } } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); @@ -236,13 +225,8 @@ public final class AdvertiseData implements Parcelable { public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); - private int mManufacturerId = -1; - @Nullable - private byte[] mManufacturerSpecificData; - @Nullable - private ParcelUuid mServiceDataUuid; - @Nullable - private byte[] mServiceData; + private SparseArray mManufacturerSpecificData = new SparseArray(); + private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; private boolean mIncludeDeviceName; @@ -268,18 +252,17 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is * empty. */ - public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null || serviceData == null) { throw new IllegalArgumentException( "serviceDataUuid or serviceDataUuid is null"); } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; + mServiceData.put(serviceDataUuid, serviceData); return this; } /** - * Set manufacturer specific data. + * Add manufacturer specific data. *

        * Please refer to the Bluetooth Assigned Numbers document provided by the Bluetooth SIG for a list of existing company @@ -290,7 +273,7 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code manufacturerId} is negative or * {@code manufacturerSpecificData} is null. */ - public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { if (manufacturerId < 0) { throw new IllegalArgumentException( "invalid manufacturerId - " + manufacturerId); @@ -298,8 +281,7 @@ public final class AdvertiseData implements Parcelable { if (manufacturerSpecificData == null) { throw new IllegalArgumentException("manufacturerSpecificData is null"); } - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; + mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); return this; } @@ -324,9 +306,7 @@ public final class AdvertiseData implements Parcelable { * Build the {@link AdvertiseData}. */ public AdvertiseData build() { - return new AdvertiseData(mServiceUuids, - mServiceDataUuid, - mServiceData, mManufacturerId, mManufacturerSpecificData, + return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, mIncludeDeviceName); } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 4d128e7439d..8879da73287 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -209,13 +209,13 @@ public final class BluetoothLeAdvertiser { num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } - if (data.getServiceDataUuid() != null) { + for (ParcelUuid uuid : data.getServiceData().keySet()) { size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH - + byteLength(data.getServiceData()); + + byteLength(data.getServiceData().get(uuid)); } - if (data.getManufacturerId() > 0) { + for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + - byteLength(data.getManufacturerSpecificData()); + byteLength(data.getManufacturerSpecificData().valueAt(i)); } if (data.getIncludeTxPowerLevel()) { size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 30aaf2e4cc2..d1b93d234b5 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -181,7 +181,7 @@ public final class ScanFilter implements Parcelable { byte[] serviceDataMask = new byte[serviceDataMaskLength]; in.readByteArray(serviceDataMask); builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); + servcieDataUuid, serviceData, serviceDataMask); } } } @@ -242,9 +242,6 @@ public final class ScanFilter implements Parcelable { return mServiceDataMask; } - /** - * @hide - */ @Nullable public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; @@ -303,19 +300,17 @@ public final class ScanFilter implements Parcelable { } // Service data match - if (mServiceData != null) { - if (!Objects.equals(mServiceDataUuid, scanRecord.getServiceDataUuid()) || - !matchesPartialData(mServiceData, mServiceDataMask, - scanRecord.getServiceData())) { + if (mServiceDataUuid != null) { + if (!matchesPartialData(mServiceData, mServiceDataMask, + scanRecord.getServiceData(mServiceDataUuid))) { return false; } } // Manufacturer data match. - if (mManufacturerData != null) { - if (mManufacturerId != scanRecord.getManufacturerId() || - !matchesPartialData(mManufacturerData, - mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { + if (mManufacturerId >= 0) { + if (!matchesPartialData(mManufacturerData, mManufacturerDataMask, + scanRecord.getManufacturerSpecificData(mManufacturerId))) { return false; } } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index e564c7d66dd..e7f33ff1168 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -19,11 +19,14 @@ package android.bluetooth.le; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; +import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * Represents a scan record from Bluetooth LE scan. @@ -53,14 +56,9 @@ public final class ScanRecord { @Nullable private final List mServiceUuids; - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; + private final SparseArray mManufacturerSpecificData; - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; + private final Map mServiceData; // Transmission power level(in dB). private final int mTxPowerLevel; @@ -81,40 +79,46 @@ public final class ScanRecord { /** * Returns a list of service UUIDs within the advertisement that are used to identify the - * bluetooth gatt services. + * bluetooth GATT services. */ public List getServiceUuids() { return mServiceUuids; } /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. + * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific + * data. */ - public int getManufacturerId() { - return mManufacturerId; + public SparseArray getManufacturerSpecificData() { + return mManufacturerSpecificData; } /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. + * Returns the manufacturer specific data associated with the manufacturer id. Returns + * {@code null} if the {@code manufacturerId} is not found. */ - public byte[] getManufacturerSpecificData() { - return mManufacturerSpecificData; + @Nullable + public byte[] getManufacturerSpecificData(int manufacturerId) { + return mManufacturerSpecificData.get(manufacturerId); } /** - * Returns a 16-bit UUID of the service that the service data is associated with. + * Returns a map of service UUID and its corresponding service data. */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; + public Map getServiceData() { + return mServiceData; } /** - * Returns service data. + * Returns the service data byte array associated with the {@code serviceUuid}. Returns + * {@code null} if the {@code serviceDataUuid} is not found. */ - public byte[] getServiceData() { - return mServiceData; + @Nullable + public byte[] getServiceData(ParcelUuid serviceDataUuid) { + if (serviceDataUuid == null) { + return null; + } + return mServiceData.get(serviceDataUuid); } /** @@ -144,14 +148,12 @@ public final class ScanRecord { } private ScanRecord(List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, + SparseArray manufacturerData, + Map serviceData, + int advertiseFlags, int txPowerLevel, String localName, byte[] bytes) { mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; + mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mDeviceName = localName; mAdvertiseFlags = advertiseFlags; @@ -168,7 +170,6 @@ public final class ScanRecord { * order. * * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - * * @hide */ public static ScanRecord parseFromBytes(byte[] scanRecord) { @@ -181,10 +182,9 @@ public final class ScanRecord { List serviceUuids = new ArrayList(); String localName = null; int txPowerLevel = Integer.MIN_VALUE; - ParcelUuid serviceDataUuid = null; - byte[] serviceData = null; - int manufacturerId = -1; - byte[] manufacturerSpecificData = null; + + SparseArray manufacturerData = new SparseArray(); + Map serviceData = new ArrayMap(); try { while (currentPos < scanRecord.length) { @@ -230,16 +230,20 @@ public final class ScanRecord { int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); - serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); - serviceData = extractBytes(scanRecord, currentPos + 2, dataLength - 2); + ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( + serviceDataUuidBytes); + byte[] serviceDataArray = extractBytes(scanRecord, + currentPos + serviceUuidLength, dataLength - serviceUuidLength); + serviceData.put(serviceDataUuid, serviceDataArray); break; case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // The first two bytes of the manufacturer specific data are // manufacturer ids in little endian. - manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + + int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + (scanRecord[currentPos] & 0xFF); - manufacturerSpecificData = extractBytes(scanRecord, currentPos + 2, + byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, dataLength - 2); + manufacturerData.put(manufacturerId, manufacturerDataBytes); break; default: // Just ignore, we don't handle such data type. @@ -251,9 +255,8 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - return new ScanRecord(serviceUuids, serviceDataUuid, serviceData, - manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, - localName, scanRecord); + return new ScanRecord(serviceUuids, manufacturerData, serviceData, + advertiseFlag, txPowerLevel, localName, scanRecord); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); return null; @@ -263,13 +266,11 @@ public final class ScanRecord { @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + + ", mManufacturerSpecificData=" + Utils.toString(mManufacturerSpecificData) + + ", mServiceData=" + Utils.toString(mServiceData) + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } - // Parse service UUIDs. private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List serviceUuids) { diff --git a/framework/java/android/bluetooth/le/Utils.java b/framework/java/android/bluetooth/le/Utils.java new file mode 100644 index 00000000000..8598dd7772b --- /dev/null +++ b/framework/java/android/bluetooth/le/Utils.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 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.le; + +import android.util.SparseArray; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Helper class for Bluetooth LE utils. + * + * @hide + */ +public class Utils { + + /** + * Returns a string composed from a {@link SparseArray}. + */ + static String toString(SparseArray array) { + if (array == null) { + return "null"; + } + if (array.size() == 0) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('{'); + for (int i = 0; i < array.size(); ++i) { + buffer.append(array.keyAt(i)).append("=").append(array.valueAt(i)); + } + buffer.append('}'); + return buffer.toString(); + } + + /** + * Returns a string composed from a {@link Map}. + */ + static String toString(Map map) { + if (map == null) { + return "null"; + } + if (map.isEmpty()) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('{'); + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + Object key = entry.getKey(); + buffer.append(key).append("=").append(Arrays.toString(map.get(key))); + if (it.hasNext()) { + buffer.append(", "); + } + } + buffer.append('}'); + return buffer.toString(); + } + + /** + * Check whether two {@link SparseArray} equal. + */ + static boolean equals(SparseArray array, SparseArray otherArray) { + if (array == otherArray) { + return true; + } + if (array == null || otherArray == null) { + return false; + } + if (array.size() != otherArray.size()) { + return false; + } + + // Keys are guaranteed in ascending order when indices are in ascending order. + for (int i = 0; i < array.size(); ++i) { + if (array.keyAt(i) != otherArray.keyAt(i) || + !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { + return false; + } + } + return true; + } + + /** + * Check whether two {@link Map} equal. + */ + static boolean equals(Map map, Map otherMap) { + if (map == otherMap) { + return true; + } + if (map == null || otherMap == null) { + return false; + } + if (map.size() != otherMap.size()) { + return false; + } + Set keys = map.keySet(); + if (!keys.equals(otherMap.keySet())) { + return false; + } + for (T key : keys) { + if (!Objects.deepEquals(map.get(key), otherMap.get(key))) { + return false; + } + } + return true; + } +} diff --git a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java index 5e451ca7b7d..e58d905357e 100644 --- a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java +++ b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java @@ -66,7 +66,7 @@ public class AdvertiseDataTest extends TestCase { byte[] manufacturerData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setManufacturerData(manufacturerId, manufacturerData).build(); + .addManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -81,7 +81,7 @@ public class AdvertiseDataTest extends TestCase { byte[] serviceData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setServiceData(uuid, serviceData).build(); + .addServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -117,7 +117,7 @@ public class AdvertiseDataTest extends TestCase { AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) .addServiceUuid(uuid).addServiceUuid(uuid2) - .setManufacturerData(manufacturerId, manufacturerData).build(); + .addManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); @@ -134,7 +134,7 @@ public class AdvertiseDataTest extends TestCase { (byte) 0xF0, 0x00, 0x02, 0x15 }; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setServiceData(uuid, serviceData).build(); + .addServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index ccdd90ae457..8b3db7e4d93 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -53,13 +53,13 @@ public class ScanRecordTest extends TestCase { assertEquals("Ped", data.getDeviceName()); assertEquals(-20, data.getTxPowerLevel()); - assertEquals(0x00e0, data.getManufacturerId()); + assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null); assertArrayEquals(new byte[] { - 0x02, 0x15 }, data.getManufacturerSpecificData()); + 0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0)); - assertEquals(uuid2, data.getServiceDataUuid()); + assertTrue(data.getServiceData().containsKey(uuid2)); assertArrayEquals(new byte[] { - 0x50, 0x64 }, data.getServiceData()); + 0x50, 0x64 }, data.getServiceData().get(uuid2)); } // Assert two byte arrays are equal. -- GitLab From a1b8375689f9c33aa913b942bf7de033adcf5bec Mon Sep 17 00:00:00 2001 From: Sungsoo Lim Date: Fri, 1 Aug 2014 01:24:14 +0000 Subject: [PATCH 0390/1408] Revert "Service data and manufacturer data can be repeated fields." This reverts commit 26dc9b99b17ca9d8ce3632c48e8b6c1450ee53e9. Change-Id: I1846d0a6491c5b2ba5905411bd98f5d17909b705 --- .../android/bluetooth/le/AdvertiseData.java | 162 ++++++++++-------- .../bluetooth/le/BluetoothLeAdvertiser.java | 8 +- .../java/android/bluetooth/le/ScanFilter.java | 19 +- .../java/android/bluetooth/le/ScanRecord.java | 89 +++++----- .../java/android/bluetooth/le/Utils.java | 126 -------------- .../bluetooth/le/AdvertiseDataTest.java | 8 +- .../android/bluetooth/le/ScanRecordTest.java | 8 +- 7 files changed, 159 insertions(+), 261 deletions(-) delete mode 100644 framework/java/android/bluetooth/le/Utils.java diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 843cd846f0f..b137eeb0ce7 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -17,15 +17,14 @@ package android.bluetooth.le; import android.annotation.Nullable; +import android.bluetooth.BluetoothUuid; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; -import android.util.ArrayMap; -import android.util.SparseArray; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -43,18 +42,27 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; - private final SparseArray mManufacturerSpecificData; - private final Map mServiceData; + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; + + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; + private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, - SparseArray manufacturerData, - Map serviceData, - boolean includeTxPowerLevel, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; - mManufacturerSpecificData = manufacturerData; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; mIncludeDeviceName = includeDeviceName; @@ -69,17 +77,32 @@ public final class AdvertiseData implements Parcelable { } /** - * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The - * manufacturer id is a non-negative number assigned by Bluetooth SIG. + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. */ - public SparseArray getManufacturerSpecificData() { + public int getManufacturerId() { + return mManufacturerId; + } + + /** + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. The first 2 bytes of the data contain the company id. + */ + public byte[] getManufacturerSpecificData() { return mManufacturerSpecificData; } /** - * Returns a map of 16-bit UUID and its corresponding service data. + * Returns a 16-bit UUID of the service that the service data is associated with. + */ + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; + } + + /** + * Returns service data. */ - public Map getServiceData() { + public byte[] getServiceData() { return mServiceData; } @@ -102,8 +125,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, - mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData, + mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -119,17 +142,20 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && - Utils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && - Utils.equals(mServiceData, other.mServiceData) && + mManufacturerId == other.mManufacturerId && + Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && + Objects.deepEquals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override public String toString() { - return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" - + Utils.toString(mManufacturerSpecificData) + ", mServiceData=" - + Utils.toString(mServiceData) + return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" + + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + mIncludeDeviceName + "]"; } @@ -143,30 +169,21 @@ public final class AdvertiseData implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeList(mServiceUuids); - // mManufacturerSpecificData could not be null. - dest.writeInt(mManufacturerSpecificData.size()); - for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { - dest.writeInt(mManufacturerSpecificData.keyAt(i)); - byte[] data = mManufacturerSpecificData.valueAt(i); - if (data == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(data.length); - dest.writeByteArray(data); - } + dest.writeInt(mManufacturerId); + if (mManufacturerSpecificData == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(mManufacturerSpecificData.length); + dest.writeByteArray(mManufacturerSpecificData); } - dest.writeInt(mServiceData.size()); - for (ParcelUuid uuid : mServiceData.keySet()) { - dest.writeParcelable(uuid, flags); - byte[] data = mServiceData.get(uuid); - if (data == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(data.length); - dest.writeByteArray(data); - } + dest.writeParcelable(mServiceDataUuid, flags); + if (mServiceData == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(mServiceData.length); + dest.writeByteArray(mServiceData); } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); @@ -192,26 +209,20 @@ public final class AdvertiseData implements Parcelable { builder.addServiceUuid(uuid); } } - int manufacturerSize = in.readInt(); - for (int i = 0; i < manufacturerSize; ++i) { - int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.addManufacturerData(manufacturerId, manufacturerData); - } + int manufacturerId = in.readInt(); + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.setManufacturerData(manufacturerId, manufacturerData); } - int serviceDataSize = in.readInt(); - for (int i = 0; i < serviceDataSize; ++i) { - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.addServiceData(serviceDataUuid, serviceData); - } + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + if (in.readInt() == 1) { + int serviceDataLength = in.readInt(); + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.setServiceData(serviceDataUuid, serviceData); } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); @@ -225,8 +236,13 @@ public final class AdvertiseData implements Parcelable { public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); - private SparseArray mManufacturerSpecificData = new SparseArray(); - private Map mServiceData = new ArrayMap(); + private int mManufacturerId = -1; + @Nullable + private byte[] mManufacturerSpecificData; + @Nullable + private ParcelUuid mServiceDataUuid; + @Nullable + private byte[] mServiceData; private boolean mIncludeTxPowerLevel; private boolean mIncludeDeviceName; @@ -252,17 +268,18 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is * empty. */ - public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null || serviceData == null) { throw new IllegalArgumentException( "serviceDataUuid or serviceDataUuid is null"); } - mServiceData.put(serviceDataUuid, serviceData); + mServiceDataUuid = serviceDataUuid; + mServiceData = serviceData; return this; } /** - * Add manufacturer specific data. + * Set manufacturer specific data. *

        * Please refer to the Bluetooth Assigned Numbers document provided by the Bluetooth SIG for a list of existing company @@ -273,7 +290,7 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code manufacturerId} is negative or * {@code manufacturerSpecificData} is null. */ - public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { if (manufacturerId < 0) { throw new IllegalArgumentException( "invalid manufacturerId - " + manufacturerId); @@ -281,7 +298,8 @@ public final class AdvertiseData implements Parcelable { if (manufacturerSpecificData == null) { throw new IllegalArgumentException("manufacturerSpecificData is null"); } - mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; return this; } @@ -306,7 +324,9 @@ public final class AdvertiseData implements Parcelable { * Build the {@link AdvertiseData}. */ public AdvertiseData build() { - return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, + return new AdvertiseData(mServiceUuids, + mServiceDataUuid, + mServiceData, mManufacturerId, mManufacturerSpecificData, mIncludeTxPowerLevel, mIncludeDeviceName); } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 8879da73287..4d128e7439d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -209,13 +209,13 @@ public final class BluetoothLeAdvertiser { num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } - for (ParcelUuid uuid : data.getServiceData().keySet()) { + if (data.getServiceDataUuid() != null) { size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH - + byteLength(data.getServiceData().get(uuid)); + + byteLength(data.getServiceData()); } - for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { + if (data.getManufacturerId() > 0) { size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + - byteLength(data.getManufacturerSpecificData().valueAt(i)); + byteLength(data.getManufacturerSpecificData()); } if (data.getIncludeTxPowerLevel()) { size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index d1b93d234b5..30aaf2e4cc2 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -181,7 +181,7 @@ public final class ScanFilter implements Parcelable { byte[] serviceDataMask = new byte[serviceDataMaskLength]; in.readByteArray(serviceDataMask); builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); + servcieDataUuid, serviceData, serviceDataMask); } } } @@ -242,6 +242,9 @@ public final class ScanFilter implements Parcelable { return mServiceDataMask; } + /** + * @hide + */ @Nullable public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; @@ -300,17 +303,19 @@ public final class ScanFilter implements Parcelable { } // Service data match - if (mServiceDataUuid != null) { - if (!matchesPartialData(mServiceData, mServiceDataMask, - scanRecord.getServiceData(mServiceDataUuid))) { + if (mServiceData != null) { + if (!Objects.equals(mServiceDataUuid, scanRecord.getServiceDataUuid()) || + !matchesPartialData(mServiceData, mServiceDataMask, + scanRecord.getServiceData())) { return false; } } // Manufacturer data match. - if (mManufacturerId >= 0) { - if (!matchesPartialData(mManufacturerData, mManufacturerDataMask, - scanRecord.getManufacturerSpecificData(mManufacturerId))) { + if (mManufacturerData != null) { + if (mManufacturerId != scanRecord.getManufacturerId() || + !matchesPartialData(mManufacturerData, + mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { return false; } } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index e7f33ff1168..e564c7d66dd 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -19,14 +19,11 @@ package android.bluetooth.le; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; -import android.util.ArrayMap; import android.util.Log; -import android.util.SparseArray; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; /** * Represents a scan record from Bluetooth LE scan. @@ -56,9 +53,14 @@ public final class ScanRecord { @Nullable private final List mServiceUuids; - private final SparseArray mManufacturerSpecificData; + private final int mManufacturerId; + @Nullable + private final byte[] mManufacturerSpecificData; - private final Map mServiceData; + @Nullable + private final ParcelUuid mServiceDataUuid; + @Nullable + private final byte[] mServiceData; // Transmission power level(in dB). private final int mTxPowerLevel; @@ -79,46 +81,40 @@ public final class ScanRecord { /** * Returns a list of service UUIDs within the advertisement that are used to identify the - * bluetooth GATT services. + * bluetooth gatt services. */ public List getServiceUuids() { return mServiceUuids; } /** - * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific - * data. + * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth + * SIG. */ - public SparseArray getManufacturerSpecificData() { - return mManufacturerSpecificData; + public int getManufacturerId() { + return mManufacturerId; } /** - * Returns the manufacturer specific data associated with the manufacturer id. Returns - * {@code null} if the {@code manufacturerId} is not found. + * Returns the manufacturer specific data which is the content of manufacturer specific data + * field. */ - @Nullable - public byte[] getManufacturerSpecificData(int manufacturerId) { - return mManufacturerSpecificData.get(manufacturerId); + public byte[] getManufacturerSpecificData() { + return mManufacturerSpecificData; } /** - * Returns a map of service UUID and its corresponding service data. + * Returns a 16-bit UUID of the service that the service data is associated with. */ - public Map getServiceData() { - return mServiceData; + public ParcelUuid getServiceDataUuid() { + return mServiceDataUuid; } /** - * Returns the service data byte array associated with the {@code serviceUuid}. Returns - * {@code null} if the {@code serviceDataUuid} is not found. + * Returns service data. */ - @Nullable - public byte[] getServiceData(ParcelUuid serviceDataUuid) { - if (serviceDataUuid == null) { - return null; - } - return mServiceData.get(serviceDataUuid); + public byte[] getServiceData() { + return mServiceData; } /** @@ -148,12 +144,14 @@ public final class ScanRecord { } private ScanRecord(List serviceUuids, - SparseArray manufacturerData, - Map serviceData, - int advertiseFlags, int txPowerLevel, + ParcelUuid serviceDataUuid, byte[] serviceData, + int manufacturerId, + byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, String localName, byte[] bytes) { mServiceUuids = serviceUuids; - mManufacturerSpecificData = manufacturerData; + mManufacturerId = manufacturerId; + mManufacturerSpecificData = manufacturerSpecificData; + mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mDeviceName = localName; mAdvertiseFlags = advertiseFlags; @@ -170,6 +168,7 @@ public final class ScanRecord { * order. * * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. + * * @hide */ public static ScanRecord parseFromBytes(byte[] scanRecord) { @@ -182,9 +181,10 @@ public final class ScanRecord { List serviceUuids = new ArrayList(); String localName = null; int txPowerLevel = Integer.MIN_VALUE; - - SparseArray manufacturerData = new SparseArray(); - Map serviceData = new ArrayMap(); + ParcelUuid serviceDataUuid = null; + byte[] serviceData = null; + int manufacturerId = -1; + byte[] manufacturerSpecificData = null; try { while (currentPos < scanRecord.length) { @@ -230,20 +230,16 @@ public final class ScanRecord { int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); - ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( - serviceDataUuidBytes); - byte[] serviceDataArray = extractBytes(scanRecord, - currentPos + serviceUuidLength, dataLength - serviceUuidLength); - serviceData.put(serviceDataUuid, serviceDataArray); + serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); + serviceData = extractBytes(scanRecord, currentPos + 2, dataLength - 2); break; case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // The first two bytes of the manufacturer specific data are // manufacturer ids in little endian. - int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + + manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + (scanRecord[currentPos] & 0xFF); - byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, + manufacturerSpecificData = extractBytes(scanRecord, currentPos + 2, dataLength - 2); - manufacturerData.put(manufacturerId, manufacturerDataBytes); break; default: // Just ignore, we don't handle such data type. @@ -255,8 +251,9 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - return new ScanRecord(serviceUuids, manufacturerData, serviceData, - advertiseFlag, txPowerLevel, localName, scanRecord); + return new ScanRecord(serviceUuids, serviceDataUuid, serviceData, + manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, + localName, scanRecord); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); return null; @@ -266,11 +263,13 @@ public final class ScanRecord { @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerSpecificData=" + Utils.toString(mManufacturerSpecificData) - + ", mServiceData=" + Utils.toString(mServiceData) + + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" + + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" + + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } + // Parse service UUIDs. private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List serviceUuids) { diff --git a/framework/java/android/bluetooth/le/Utils.java b/framework/java/android/bluetooth/le/Utils.java deleted file mode 100644 index 8598dd7772b..00000000000 --- a/framework/java/android/bluetooth/le/Utils.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -import android.util.SparseArray; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Helper class for Bluetooth LE utils. - * - * @hide - */ -public class Utils { - - /** - * Returns a string composed from a {@link SparseArray}. - */ - static String toString(SparseArray array) { - if (array == null) { - return "null"; - } - if (array.size() == 0) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - for (int i = 0; i < array.size(); ++i) { - buffer.append(array.keyAt(i)).append("=").append(array.valueAt(i)); - } - buffer.append('}'); - return buffer.toString(); - } - - /** - * Returns a string composed from a {@link Map}. - */ - static String toString(Map map) { - if (map == null) { - return "null"; - } - if (map.isEmpty()) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - Iterator> it = map.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - Object key = entry.getKey(); - buffer.append(key).append("=").append(Arrays.toString(map.get(key))); - if (it.hasNext()) { - buffer.append(", "); - } - } - buffer.append('}'); - return buffer.toString(); - } - - /** - * Check whether two {@link SparseArray} equal. - */ - static boolean equals(SparseArray array, SparseArray otherArray) { - if (array == otherArray) { - return true; - } - if (array == null || otherArray == null) { - return false; - } - if (array.size() != otherArray.size()) { - return false; - } - - // Keys are guaranteed in ascending order when indices are in ascending order. - for (int i = 0; i < array.size(); ++i) { - if (array.keyAt(i) != otherArray.keyAt(i) || - !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { - return false; - } - } - return true; - } - - /** - * Check whether two {@link Map} equal. - */ - static boolean equals(Map map, Map otherMap) { - if (map == otherMap) { - return true; - } - if (map == null || otherMap == null) { - return false; - } - if (map.size() != otherMap.size()) { - return false; - } - Set keys = map.keySet(); - if (!keys.equals(otherMap.keySet())) { - return false; - } - for (T key : keys) { - if (!Objects.deepEquals(map.get(key), otherMap.get(key))) { - return false; - } - } - return true; - } -} diff --git a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java index e58d905357e..5e451ca7b7d 100644 --- a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java +++ b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java @@ -66,7 +66,7 @@ public class AdvertiseDataTest extends TestCase { byte[] manufacturerData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addManufacturerData(manufacturerId, manufacturerData).build(); + .setManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -81,7 +81,7 @@ public class AdvertiseDataTest extends TestCase { byte[] serviceData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceData(uuid, serviceData).build(); + .setServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -117,7 +117,7 @@ public class AdvertiseDataTest extends TestCase { AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) .addServiceUuid(uuid).addServiceUuid(uuid2) - .addManufacturerData(manufacturerId, manufacturerData).build(); + .setManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); @@ -134,7 +134,7 @@ public class AdvertiseDataTest extends TestCase { (byte) 0xF0, 0x00, 0x02, 0x15 }; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceData(uuid, serviceData).build(); + .setServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index 8b3db7e4d93..ccdd90ae457 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -53,13 +53,13 @@ public class ScanRecordTest extends TestCase { assertEquals("Ped", data.getDeviceName()); assertEquals(-20, data.getTxPowerLevel()); - assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null); + assertEquals(0x00e0, data.getManufacturerId()); assertArrayEquals(new byte[] { - 0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0)); + 0x02, 0x15 }, data.getManufacturerSpecificData()); - assertTrue(data.getServiceData().containsKey(uuid2)); + assertEquals(uuid2, data.getServiceDataUuid()); assertArrayEquals(new byte[] { - 0x50, 0x64 }, data.getServiceData().get(uuid2)); + 0x50, 0x64 }, data.getServiceData()); } // Assert two byte arrays are equal. -- GitLab From f59709e667cc5e4a4a8927d42fba4a80494c491c Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 1 Aug 2014 11:12:37 -0700 Subject: [PATCH 0391/1408] Revert "Revert "Service data and manufacturer data can be repeated fields."" This reverts commit 14c797702543bb5ced989565d90abcfa55c7db46. --- .../android/bluetooth/le/AdvertiseData.java | 162 ++++++++---------- .../bluetooth/le/BluetoothLeAdvertiser.java | 8 +- .../java/android/bluetooth/le/ScanFilter.java | 19 +- .../java/android/bluetooth/le/ScanRecord.java | 89 +++++----- .../java/android/bluetooth/le/Utils.java | 126 ++++++++++++++ .../bluetooth/le/AdvertiseDataTest.java | 8 +- .../android/bluetooth/le/ScanRecordTest.java | 8 +- 7 files changed, 261 insertions(+), 159 deletions(-) create mode 100644 framework/java/android/bluetooth/le/Utils.java diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index b137eeb0ce7..843cd846f0f 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -17,14 +17,15 @@ package android.bluetooth.le; import android.annotation.Nullable; -import android.bluetooth.BluetoothUuid; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.SparseArray; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -42,27 +43,18 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; - - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; - + private final SparseArray mManufacturerSpecificData; + private final Map mServiceData; private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, boolean includeTxPowerLevel, + SparseArray manufacturerData, + Map serviceData, + boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; + mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; mIncludeDeviceName = includeDeviceName; @@ -77,32 +69,17 @@ public final class AdvertiseData implements Parcelable { } /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. + * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The + * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ - public int getManufacturerId() { - return mManufacturerId; - } - - /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. The first 2 bytes of the data contain the company id. - */ - public byte[] getManufacturerSpecificData() { + public SparseArray getManufacturerSpecificData() { return mManufacturerSpecificData; } /** - * Returns a 16-bit UUID of the service that the service data is associated with. - */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** - * Returns service data. + * Returns a map of 16-bit UUID and its corresponding service data. */ - public byte[] getServiceData() { + public Map getServiceData() { return mServiceData; } @@ -125,8 +102,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData, - mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, + mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -142,20 +119,17 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && - mManufacturerId == other.mManufacturerId && - Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) && - Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && - Objects.deepEquals(mServiceData, other.mServiceData) && + Utils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + Utils.equals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override public String toString() { - return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId=" - + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" + + Utils.toString(mManufacturerSpecificData) + ", mServiceData=" + + Utils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + mIncludeDeviceName + "]"; } @@ -169,21 +143,30 @@ public final class AdvertiseData implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeList(mServiceUuids); - dest.writeInt(mManufacturerId); - if (mManufacturerSpecificData == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(mManufacturerSpecificData.length); - dest.writeByteArray(mManufacturerSpecificData); + // mManufacturerSpecificData could not be null. + dest.writeInt(mManufacturerSpecificData.size()); + for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { + dest.writeInt(mManufacturerSpecificData.keyAt(i)); + byte[] data = mManufacturerSpecificData.valueAt(i); + if (data == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(data.length); + dest.writeByteArray(data); + } } - dest.writeParcelable(mServiceDataUuid, flags); - if (mServiceData == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(mServiceData.length); - dest.writeByteArray(mServiceData); + dest.writeInt(mServiceData.size()); + for (ParcelUuid uuid : mServiceData.keySet()) { + dest.writeParcelable(uuid, flags); + byte[] data = mServiceData.get(uuid); + if (data == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeInt(data.length); + dest.writeByteArray(data); + } } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); @@ -209,20 +192,26 @@ public final class AdvertiseData implements Parcelable { builder.addServiceUuid(uuid); } } - int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.setManufacturerData(manufacturerId, manufacturerData); + int manufacturerSize = in.readInt(); + for (int i = 0; i < manufacturerSize; ++i) { + int manufacturerId = in.readInt(); + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + builder.addManufacturerData(manufacturerId, manufacturerData); + } } - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.setServiceData(serviceDataUuid, serviceData); + int serviceDataSize = in.readInt(); + for (int i = 0; i < serviceDataSize; ++i) { + ParcelUuid serviceDataUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + if (in.readInt() == 1) { + int serviceDataLength = in.readInt(); + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + builder.addServiceData(serviceDataUuid, serviceData); + } } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); @@ -236,13 +225,8 @@ public final class AdvertiseData implements Parcelable { public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); - private int mManufacturerId = -1; - @Nullable - private byte[] mManufacturerSpecificData; - @Nullable - private ParcelUuid mServiceDataUuid; - @Nullable - private byte[] mServiceData; + private SparseArray mManufacturerSpecificData = new SparseArray(); + private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; private boolean mIncludeDeviceName; @@ -268,18 +252,17 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is * empty. */ - public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { + public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null || serviceData == null) { throw new IllegalArgumentException( "serviceDataUuid or serviceDataUuid is null"); } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; + mServiceData.put(serviceDataUuid, serviceData); return this; } /** - * Set manufacturer specific data. + * Add manufacturer specific data. *

        * Please refer to the Bluetooth Assigned Numbers document provided by the Bluetooth SIG for a list of existing company @@ -290,7 +273,7 @@ public final class AdvertiseData implements Parcelable { * @throws IllegalArgumentException If the {@code manufacturerId} is negative or * {@code manufacturerSpecificData} is null. */ - public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { + public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { if (manufacturerId < 0) { throw new IllegalArgumentException( "invalid manufacturerId - " + manufacturerId); @@ -298,8 +281,7 @@ public final class AdvertiseData implements Parcelable { if (manufacturerSpecificData == null) { throw new IllegalArgumentException("manufacturerSpecificData is null"); } - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; + mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); return this; } @@ -324,9 +306,7 @@ public final class AdvertiseData implements Parcelable { * Build the {@link AdvertiseData}. */ public AdvertiseData build() { - return new AdvertiseData(mServiceUuids, - mServiceDataUuid, - mServiceData, mManufacturerId, mManufacturerSpecificData, + return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, mIncludeDeviceName); } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 4d128e7439d..8879da73287 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -209,13 +209,13 @@ public final class BluetoothLeAdvertiser { num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } - if (data.getServiceDataUuid() != null) { + for (ParcelUuid uuid : data.getServiceData().keySet()) { size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH - + byteLength(data.getServiceData()); + + byteLength(data.getServiceData().get(uuid)); } - if (data.getManufacturerId() > 0) { + for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + - byteLength(data.getManufacturerSpecificData()); + byteLength(data.getManufacturerSpecificData().valueAt(i)); } if (data.getIncludeTxPowerLevel()) { size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 30aaf2e4cc2..d1b93d234b5 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -181,7 +181,7 @@ public final class ScanFilter implements Parcelable { byte[] serviceDataMask = new byte[serviceDataMaskLength]; in.readByteArray(serviceDataMask); builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); + servcieDataUuid, serviceData, serviceDataMask); } } } @@ -242,9 +242,6 @@ public final class ScanFilter implements Parcelable { return mServiceDataMask; } - /** - * @hide - */ @Nullable public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; @@ -303,19 +300,17 @@ public final class ScanFilter implements Parcelable { } // Service data match - if (mServiceData != null) { - if (!Objects.equals(mServiceDataUuid, scanRecord.getServiceDataUuid()) || - !matchesPartialData(mServiceData, mServiceDataMask, - scanRecord.getServiceData())) { + if (mServiceDataUuid != null) { + if (!matchesPartialData(mServiceData, mServiceDataMask, + scanRecord.getServiceData(mServiceDataUuid))) { return false; } } // Manufacturer data match. - if (mManufacturerData != null) { - if (mManufacturerId != scanRecord.getManufacturerId() || - !matchesPartialData(mManufacturerData, - mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) { + if (mManufacturerId >= 0) { + if (!matchesPartialData(mManufacturerData, mManufacturerDataMask, + scanRecord.getManufacturerSpecificData(mManufacturerId))) { return false; } } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index e564c7d66dd..e7f33ff1168 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -19,11 +19,14 @@ package android.bluetooth.le; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; +import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; /** * Represents a scan record from Bluetooth LE scan. @@ -53,14 +56,9 @@ public final class ScanRecord { @Nullable private final List mServiceUuids; - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerSpecificData; + private final SparseArray mManufacturerSpecificData; - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; + private final Map mServiceData; // Transmission power level(in dB). private final int mTxPowerLevel; @@ -81,40 +79,46 @@ public final class ScanRecord { /** * Returns a list of service UUIDs within the advertisement that are used to identify the - * bluetooth gatt services. + * bluetooth GATT services. */ public List getServiceUuids() { return mServiceUuids; } /** - * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth - * SIG. + * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific + * data. */ - public int getManufacturerId() { - return mManufacturerId; + public SparseArray getManufacturerSpecificData() { + return mManufacturerSpecificData; } /** - * Returns the manufacturer specific data which is the content of manufacturer specific data - * field. + * Returns the manufacturer specific data associated with the manufacturer id. Returns + * {@code null} if the {@code manufacturerId} is not found. */ - public byte[] getManufacturerSpecificData() { - return mManufacturerSpecificData; + @Nullable + public byte[] getManufacturerSpecificData(int manufacturerId) { + return mManufacturerSpecificData.get(manufacturerId); } /** - * Returns a 16-bit UUID of the service that the service data is associated with. + * Returns a map of service UUID and its corresponding service data. */ - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; + public Map getServiceData() { + return mServiceData; } /** - * Returns service data. + * Returns the service data byte array associated with the {@code serviceUuid}. Returns + * {@code null} if the {@code serviceDataUuid} is not found. */ - public byte[] getServiceData() { - return mServiceData; + @Nullable + public byte[] getServiceData(ParcelUuid serviceDataUuid) { + if (serviceDataUuid == null) { + return null; + } + return mServiceData.get(serviceDataUuid); } /** @@ -144,14 +148,12 @@ public final class ScanRecord { } private ScanRecord(List serviceUuids, - ParcelUuid serviceDataUuid, byte[] serviceData, - int manufacturerId, - byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel, + SparseArray manufacturerData, + Map serviceData, + int advertiseFlags, int txPowerLevel, String localName, byte[] bytes) { mServiceUuids = serviceUuids; - mManufacturerId = manufacturerId; - mManufacturerSpecificData = manufacturerSpecificData; - mServiceDataUuid = serviceDataUuid; + mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mDeviceName = localName; mAdvertiseFlags = advertiseFlags; @@ -168,7 +170,6 @@ public final class ScanRecord { * order. * * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - * * @hide */ public static ScanRecord parseFromBytes(byte[] scanRecord) { @@ -181,10 +182,9 @@ public final class ScanRecord { List serviceUuids = new ArrayList(); String localName = null; int txPowerLevel = Integer.MIN_VALUE; - ParcelUuid serviceDataUuid = null; - byte[] serviceData = null; - int manufacturerId = -1; - byte[] manufacturerSpecificData = null; + + SparseArray manufacturerData = new SparseArray(); + Map serviceData = new ArrayMap(); try { while (currentPos < scanRecord.length) { @@ -230,16 +230,20 @@ public final class ScanRecord { int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); - serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes); - serviceData = extractBytes(scanRecord, currentPos + 2, dataLength - 2); + ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( + serviceDataUuidBytes); + byte[] serviceDataArray = extractBytes(scanRecord, + currentPos + serviceUuidLength, dataLength - serviceUuidLength); + serviceData.put(serviceDataUuid, serviceDataArray); break; case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // The first two bytes of the manufacturer specific data are // manufacturer ids in little endian. - manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + + int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + (scanRecord[currentPos] & 0xFF); - manufacturerSpecificData = extractBytes(scanRecord, currentPos + 2, + byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, dataLength - 2); + manufacturerData.put(manufacturerId, manufacturerDataBytes); break; default: // Just ignore, we don't handle such data type. @@ -251,9 +255,8 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - return new ScanRecord(serviceUuids, serviceDataUuid, serviceData, - manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel, - localName, scanRecord); + return new ScanRecord(serviceUuids, manufacturerData, serviceData, + advertiseFlag, txPowerLevel, localName, scanRecord); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); return null; @@ -263,13 +266,11 @@ public final class ScanRecord { @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData=" - + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid=" - + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData) + + ", mManufacturerSpecificData=" + Utils.toString(mManufacturerSpecificData) + + ", mServiceData=" + Utils.toString(mServiceData) + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } - // Parse service UUIDs. private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, int uuidLength, List serviceUuids) { diff --git a/framework/java/android/bluetooth/le/Utils.java b/framework/java/android/bluetooth/le/Utils.java new file mode 100644 index 00000000000..8598dd7772b --- /dev/null +++ b/framework/java/android/bluetooth/le/Utils.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 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.le; + +import android.util.SparseArray; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Helper class for Bluetooth LE utils. + * + * @hide + */ +public class Utils { + + /** + * Returns a string composed from a {@link SparseArray}. + */ + static String toString(SparseArray array) { + if (array == null) { + return "null"; + } + if (array.size() == 0) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('{'); + for (int i = 0; i < array.size(); ++i) { + buffer.append(array.keyAt(i)).append("=").append(array.valueAt(i)); + } + buffer.append('}'); + return buffer.toString(); + } + + /** + * Returns a string composed from a {@link Map}. + */ + static String toString(Map map) { + if (map == null) { + return "null"; + } + if (map.isEmpty()) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('{'); + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + Object key = entry.getKey(); + buffer.append(key).append("=").append(Arrays.toString(map.get(key))); + if (it.hasNext()) { + buffer.append(", "); + } + } + buffer.append('}'); + return buffer.toString(); + } + + /** + * Check whether two {@link SparseArray} equal. + */ + static boolean equals(SparseArray array, SparseArray otherArray) { + if (array == otherArray) { + return true; + } + if (array == null || otherArray == null) { + return false; + } + if (array.size() != otherArray.size()) { + return false; + } + + // Keys are guaranteed in ascending order when indices are in ascending order. + for (int i = 0; i < array.size(); ++i) { + if (array.keyAt(i) != otherArray.keyAt(i) || + !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { + return false; + } + } + return true; + } + + /** + * Check whether two {@link Map} equal. + */ + static boolean equals(Map map, Map otherMap) { + if (map == otherMap) { + return true; + } + if (map == null || otherMap == null) { + return false; + } + if (map.size() != otherMap.size()) { + return false; + } + Set keys = map.keySet(); + if (!keys.equals(otherMap.keySet())) { + return false; + } + for (T key : keys) { + if (!Objects.deepEquals(map.get(key), otherMap.get(key))) { + return false; + } + } + return true; + } +} diff --git a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java index 5e451ca7b7d..e58d905357e 100644 --- a/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java +++ b/framework/tests/src/android/bluetooth/le/AdvertiseDataTest.java @@ -66,7 +66,7 @@ public class AdvertiseDataTest extends TestCase { byte[] manufacturerData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setManufacturerData(manufacturerId, manufacturerData).build(); + .addManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -81,7 +81,7 @@ public class AdvertiseDataTest extends TestCase { byte[] serviceData = new byte[0]; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setServiceData(uuid, serviceData).build(); + .addServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = @@ -117,7 +117,7 @@ public class AdvertiseDataTest extends TestCase { AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) .addServiceUuid(uuid).addServiceUuid(uuid2) - .setManufacturerData(manufacturerId, manufacturerData).build(); + .addManufacturerData(manufacturerId, manufacturerData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); @@ -134,7 +134,7 @@ public class AdvertiseDataTest extends TestCase { (byte) 0xF0, 0x00, 0x02, 0x15 }; AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true) - .setServiceData(uuid, serviceData).build(); + .addServiceData(uuid, serviceData).build(); data.writeToParcel(parcel, 0); parcel.setDataPosition(0); AdvertiseData dataFromParcel = diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index ccdd90ae457..8b3db7e4d93 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -53,13 +53,13 @@ public class ScanRecordTest extends TestCase { assertEquals("Ped", data.getDeviceName()); assertEquals(-20, data.getTxPowerLevel()); - assertEquals(0x00e0, data.getManufacturerId()); + assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null); assertArrayEquals(new byte[] { - 0x02, 0x15 }, data.getManufacturerSpecificData()); + 0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0)); - assertEquals(uuid2, data.getServiceDataUuid()); + assertTrue(data.getServiceData().containsKey(uuid2)); assertArrayEquals(new byte[] { - 0x50, 0x64 }, data.getServiceData()); + 0x50, 0x64 }, data.getServiceData().get(uuid2)); } // Assert two byte arrays are equal. -- GitLab From 1490c398ab899e93c507c2d4b46f673bd9689abd Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 29 Jul 2014 21:34:25 -0700 Subject: [PATCH 0392/1408] Add a default impelementation of IBluetoothGattCallback(1/2). Also cleaned up a few TODOs and fixed bug. b/16410260 AdvertiseCallback null onSuccess callback Change-Id: I75a582c03be1e2ef6964c2cbeba42aaaf9f9e17c --- .../java/android/bluetooth/BluetoothGatt.java | 44 +---- .../BluetoothGattCallbackWrapper.java | 131 ++++++++++++++ .../bluetooth/IBluetoothGattCallback.aidl | 6 +- .../bluetooth/le/BluetoothLeAdvertiser.java | 165 +++--------------- .../bluetooth/le/BluetoothLeScanner.java | 118 +------------ 5 files changed, 165 insertions(+), 299 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1fe43ec77c8..59d7956b542 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.bluetooth.le.ScanResult; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; @@ -130,10 +129,10 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_MITM = 2; /** - * Bluetooth GATT interface callbacks + * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { + new BluetoothGattCallbackWrapper() { /** * Application interface registered - app is ready to go * @hide @@ -197,14 +196,6 @@ public final class BluetoothGatt implements BluetoothProfile { } } - /** - * Callback reporting an LE scan result. - * @hide - */ - public void onScanResult(String address, int rssi, byte[] advData) { - // no op - } - /** * A new GATT service has been discovered. * The service is added to the internal list and the search @@ -599,23 +590,6 @@ public final class BluetoothGatt implements BluetoothProfile { } } - /** - * Advertise state change callback - * @hide - */ - public void onAdvertiseStateChange(int state, int status) { - if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " - + state + " status=" + status); - } - - /** - * @hide - */ - @Override - public void onMultiAdvertiseCallback(int status) { - // no op. - } - /** * Callback invoked when the MTU for a given connection changes * @hide @@ -647,20 +621,6 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } - - @Override - public void onBatchScanResults(List results) { - // no op - } - - /** - * @hide - */ - @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, - byte[] advData) { - // no op. - } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java new file mode 100644 index 00000000000..0eb9d21cbfa --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 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.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.ScanResult; +import android.os.ParcelUuid; +import android.os.RemoteException; + +import java.util.List; + +/** + * Wrapper class for default implementation of IBluetoothGattCallback. + * + * @hide + */ +public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { + + @Override + public void onClientRegistered(int status, int clientIf) throws RemoteException { + } + + @Override + public void onClientConnectionState(int status, int clientIf, boolean connected, String address) + throws RemoteException { + } + + @Override + public void onScanResult(ScanResult scanResult) throws RemoteException { + } + + @Override + public void onBatchScanResults(List batchResults) throws RemoteException { + } + + @Override + public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) + throws RemoteException { + } + + @Override + public void onGetIncludedService(String address, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int inclSrvcType, int inclSrvcInstId, ParcelUuid inclSrvcUuid) + throws RemoteException { + } + + @Override + public void onGetCharacteristic(String address, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int charProps) + throws RemoteException { + } + + @Override + public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid) + throws RemoteException { + } + + @Override + public void onSearchComplete(String address, int status) throws RemoteException { + } + + @Override + public void onCharacteristicRead(String address, int status, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) + throws RemoteException { + } + + @Override + public void onCharacteristicWrite(String address, int status, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid) throws RemoteException { + } + + @Override + public void onExecuteWrite(String address, int status) throws RemoteException { + } + + @Override + public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, + ParcelUuid descrUuid, byte[] value) throws RemoteException { + } + + @Override + public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId, + ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, + ParcelUuid descrUuid) throws RemoteException { + } + + @Override + public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) throws RemoteException { + } + + @Override + public void onReadRemoteRssi(String address, int rssi, int status) throws RemoteException { + } + + @Override + public void onMultiAdvertiseCallback(int status, boolean isStart, + AdvertiseSettings advertiseSettings) throws RemoteException { + } + + @Override + public void onConfigureMTU(String address, int mtu, int status) throws RemoteException { + } + + @Override + public void onConnectionCongested(String address, boolean congested) throws RemoteException { + } + + @Override + public void onFoundOrLost(boolean onFound, String address, int rssi, byte[] advData) + throws RemoteException { + } + +} diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 18e3f54ecf0..f14cce0b771 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -16,6 +16,7 @@ package android.bluetooth; import android.os.ParcelUuid; +import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.ScanResult; /** @@ -26,7 +27,7 @@ oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); - void onScanResult(in String address, in int rssi, in byte[] advData); + void onScanResult(in ScanResult scanResult); void onBatchScanResults(in List batchResults); void onGetService(in String address, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid); @@ -64,7 +65,8 @@ oneway interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onMultiAdvertiseCallback(in int status); + void onMultiAdvertiseCallback(in int status, boolean isStart, + in AdvertiseSettings advertiseSettings); void onConfigureMTU(in String address, in int mtu, in int status); void onConnectionCongested(in String address, in boolean congested); void onFoundOrLost(in boolean onFound, in String address, in int rssi, diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 8879da73287..331ebfcf724 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -18,6 +18,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; @@ -233,7 +234,7 @@ public final class BluetoothLeAdvertiser { /** * Bluetooth GATT interface callbacks for advertising. */ - private static class AdvertiseCallbackWrapper extends IBluetoothGattCallback.Stub { + private static class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; private final AdvertiseData mAdvertisement; @@ -245,7 +246,7 @@ public final class BluetoothLeAdvertiser { // -1: scan stopped // >0: registered and scan started private int mClientIf; - private boolean isAdvertising = false; + private boolean mIsAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, AdvertiseData advertiseData, AdvertiseData scanResponse, @@ -270,7 +271,7 @@ public final class BluetoothLeAdvertiser { } catch (InterruptedException e) { Log.e(TAG, "Callback reg wait interrupted: ", e); } - started = (mClientIf > 0 && isAdvertising); + started = (mClientIf > 0 && mIsAdvertising); } return started; } @@ -282,7 +283,7 @@ public final class BluetoothLeAdvertiser { } catch (InterruptedException e) { Log.e(TAG, "Callback reg wait interrupted: " + e); } - return !isAdvertising; + return !mIsAdvertising; } } @@ -312,155 +313,35 @@ public final class BluetoothLeAdvertiser { } @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - - @Override - public void onScanResult(String address, int rssi, byte[] advData) { - // no op - } - - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - // TODO: This logic needs to be re-visited to account - // for whether the scan has actually been started - // or not. Toggling the isAdvertising does not seem - // correct. + public void onMultiAdvertiseCallback(int status, boolean isStart, + AdvertiseSettings settings) { synchronized (this) { - if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { - isAdvertising = !isAdvertising; - if (!isAdvertising) { - try { - mBluetoothGatt.unregisterClient(mClientIf); - mClientIf = -1; - } catch (RemoteException e) { - Log.e(TAG, "remote exception when unregistering", e); - } + if (isStart) { + if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { + // Start success + mAdvertiseCallback.onStartSuccess(settings); + mIsAdvertising = true; } else { - mAdvertiseCallback.onStartSuccess(null); + // Start failure. + mAdvertiseCallback.onStartFailure(status); } } else { - if (!isAdvertising) - mAdvertiseCallback.onStartFailure(status); + // unregister client for stop. + try { + mBluetoothGatt.unregisterClient(mClientIf); + mClientIf = -1; + mIsAdvertising = false; + } catch (RemoteException e) { + Log.e(TAG, "remote exception when unregistering", e); + } } notifyAll(); } } - - /** - * Callback reporting LE ATT MTU. - * - * @hide - */ - @Override - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - - @Override - public void onConnectionCongested(String address, boolean congested) { - // no op - } - - @Override - public void onBatchScanResults(List results) { - // no op - } - - @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, - byte[] advData) { - // no op - } } - //TODO: move this api to a common util class. + // TODO: move this api to a common util class. private void checkAdapterState() { if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index f100ddc4760..45e466fb1c7 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -19,6 +19,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothManager; @@ -186,7 +187,7 @@ public final class BluetoothLeScanner { /** * Bluetooth GATT interface callbacks */ - private static class BleScanCallbackWrapper extends IBluetoothGattCallback.Stub { + private static class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; private final ScanCallback mScanCallback; @@ -284,37 +285,26 @@ public final class BluetoothLeScanner { } } - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op - } - /** * Callback reporting an LE scan result. * * @hide */ @Override - public void onScanResult(String address, int rssi, byte[] advData) { + public void onScanResult(final ScanResult scanResult) { if (DBG) - Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" + rssi); + Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { if (mClientIf <= 0) return; } - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - address); - long scanNanos = SystemClock.elapsedRealtimeNanos(); - final ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(advData), - rssi, scanNanos); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); } }); @@ -331,104 +321,6 @@ public final class BluetoothLeScanner { }); } - @Override - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } - - @Override - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op - } - - @Override - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } - - @Override - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descUuid) { - // no op - } - - @Override - public void onSearchComplete(String address, int status) { - // no op - } - - @Override - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } - - @Override - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } - - @Override - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - // no op - } - - @Override - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid, byte[] value) { - // no op - } - - @Override - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descInstId, ParcelUuid descrUuid) { - // no op - } - - @Override - public void onExecuteWrite(String address, int status) { - // no op - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - - @Override - public void onMultiAdvertiseCallback(int status) { - // no op - } - - @Override - public void onConfigureMTU(String address, int mtu, int status) { - // no op - } - - @Override - public void onConnectionCongested(String address, boolean congested) { - // no op - } - @Override public void onFoundOrLost(boolean onFound, String address, int rssi, byte[] advData) { -- GitLab From bca98d1a48d4caf3214298239b7584713209e213 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 4 Aug 2014 17:51:43 -0700 Subject: [PATCH 0393/1408] Add transport parameter to createBond() function (4/4) Bug: 14063256 Change-Id: I096248b60752992aa23ce5bfbfc47fda95773db2 --- .../android/bluetooth/BluetoothDevice.java | 33 ++++++++++++++++++- .../java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a94bc413df8..860512b9f9d 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -760,7 +760,38 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return sService.createBond(this); + return sService.createBond(this, TRANSPORT_AUTO); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Start the bonding (pairing) process with the remote device using the + * specified transport. + * + *

        This is an asynchronous call, it will return immediately. Register + * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when + * the bonding process completes, and its result. + *

        Android system services will handle the necessary user interactions + * to confirm and complete the bonding process. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * + * @param transport The transport to use for the pairing procedure. + * @return false on immediate error, true if bonding will begin + * @throws IllegalArgumentException if an invalid transport was specified + * @hide + */ + public boolean createBond(int transport) { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); + return false; + } + if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) + { + throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); + } + try { + return sService.createBond(this, transport); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index ca558033dde..19c600cafe9 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -55,7 +55,7 @@ interface IBluetooth int getProfileConnectionState(int profile); BluetoothDevice[] getBondedDevices(); - boolean createBond(in BluetoothDevice device); + boolean createBond(in BluetoothDevice device, in int transport); boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); -- GitLab From f488bf5c6e25f89c2d844e10e3086a1dc6a525aa Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 7 Aug 2014 09:44:20 -0700 Subject: [PATCH 0394/1408] OnLost/OnFound integration - onfound match is done in framework, whereas hw signal is used to report onlost. Bug 16733710 Change-Id: I7b177b7afd730341c336818857436c095735b53b --- .../BluetoothGattCallbackWrapper.java | 3 +- .../bluetooth/IBluetoothGattCallback.aidl | 3 +- .../bluetooth/le/BluetoothLeScanner.java | 31 +++++++++++-------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 0eb9d21cbfa..da992f55f25 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -124,8 +124,7 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, byte[] advData) - throws RemoteException { + public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException { } } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index f14cce0b771..00b6b1b7f54 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -69,6 +69,5 @@ oneway interface IBluetoothGattCallback { in AdvertiseSettings advertiseSettings); void onConfigureMTU(in String address, in int mtu, in int status); void onConnectionCongested(in String address, in boolean congested); - void onFoundOrLost(in boolean onFound, in String address, in int rssi, - in byte[] advData); + void onFoundOrLost(in boolean onFound, in ScanResult scanResult); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 45e466fb1c7..6667cc4c64c 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -322,22 +322,27 @@ public final class BluetoothLeScanner { } @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, - byte[] advData) { + public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { if (DBG) { - Log.d(TAG, "onFoundOrLost() - Device=" + address); + Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + + " " + scanResult.toString()); } - // ToDo: Fix issue with underlying reporting from chipset - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - address); - long scanNanos = SystemClock.elapsedRealtimeNanos(); - ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(advData), rssi, - scanNanos); - if (onFound) { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result); - } else { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result); + + // Check null in case the scan has been stopped + synchronized (this) { + if (mClientIf <= 0) return; } + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + if (onFound) { + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, scanResult); + } else { + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, scanResult); + } + } + }); } } -- GitLab From 13d898963422390c9f32dbed7b96dd99b932798d Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 30 Jul 2014 15:19:08 -0700 Subject: [PATCH 0395/1408] Add a TLV to support different result storage Change-Id: Ic20a1720b783508cf85b234950b7fd0c79e9e7c3 --- .../android/bluetooth/IBluetoothGatt.aidl | 4 +- .../bluetooth/le/BluetoothLeScanner.java | 52 ++++++++--- .../bluetooth/le/ResultStorageDescriptor.aidl | 23 +++++ .../bluetooth/le/ResultStorageDescriptor.java | 93 +++++++++++++++++++ .../android/bluetooth/le/TruncatedFilter.java | 61 ++++++++++++ 5 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl create mode 100644 framework/java/android/bluetooth/le/ResultStorageDescriptor.java create mode 100644 framework/java/android/bluetooth/le/TruncatedFilter.java diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index edf823e60d3..7070baebcf7 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -21,6 +21,7 @@ import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; +import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -34,7 +35,8 @@ interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); void startScan(in int appIf, in boolean isServer, in ScanSettings settings, - in List filters); + in List filters, + in List scanStorages); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 6667cc4c64c..7c3cbc6163b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,20 +16,19 @@ package android.bluetooth.le; +import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.SystemClock; import android.util.Log; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -100,6 +99,11 @@ public final class BluetoothLeScanner { */ public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { + startScan(filters, settings, callback, null); + } + + private void startScan(List filters, ScanSettings settings, + final ScanCallback callback, List> resultStorages) { checkAdapterState(); if (settings == null || callback == null) { throw new IllegalArgumentException("settings or callback is null"); @@ -125,7 +129,7 @@ public final class BluetoothLeScanner { return; } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, callback); + settings, callback, resultStorages); try { UUID uuid = UUID.randomUUID(); gatt.registerClient(new ParcelUuid(uuid), wrapper); @@ -155,7 +159,8 @@ public final class BluetoothLeScanner { synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { - if (DBG) Log.d(TAG, "could not find callback wrapper"); + if (DBG) + Log.d(TAG, "could not find callback wrapper"); return; } wrapper.stopLeScan(); @@ -184,6 +189,25 @@ public final class BluetoothLeScanner { } } + /** + * Start truncated scan. + * + * @hide + */ + @SystemApi + public void startTruncatedScan(List truncatedFilters, ScanSettings settings, + final ScanCallback callback) { + int filterSize = truncatedFilters.size(); + List scanFilters = new ArrayList(filterSize); + List> scanStorages = + new ArrayList>(filterSize); + for (TruncatedFilter filter : truncatedFilters) { + scanFilters.add(filter.getFilter()); + scanStorages.add(filter.getStorageDescriptors()); + } + startScan(scanFilters, settings, callback, scanStorages); + } + /** * Bluetooth GATT interface callbacks */ @@ -194,6 +218,7 @@ public final class BluetoothLeScanner { private final List mFilters; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; + private List> mResultStorages; // mLeHandle 0: not registered // -1: scan stopped @@ -202,12 +227,13 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, - ScanCallback scanCallback) { + ScanCallback scanCallback, List> resultStorages) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; mScanCallback = scanCallback; mClientIf = 0; + mResultStorages = resultStorages; } public boolean scanStarted() { @@ -272,7 +298,8 @@ public final class BluetoothLeScanner { if (status == BluetoothGatt.GATT_SUCCESS) { mClientIf = clientIf; try { - mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters); + mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, + mResultStorages); } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; @@ -330,23 +357,26 @@ public final class BluetoothLeScanner { // Check null in case the scan has been stopped synchronized (this) { - if (mClientIf <= 0) return; + if (mClientIf <= 0) + return; } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { if (onFound) { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, scanResult); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, + scanResult); } else { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, scanResult); + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, + scanResult); } } }); } } - //TODO: move this api to a common util class. + // TODO: move this api to a common util class. private void checkAdapterState() { if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl b/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl new file mode 100644 index 00000000000..f218a01a5d1 --- /dev/null +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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.le; + +/** + * {@hide} + */ + +parcelable ResultStorageDescriptor; diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java new file mode 100644 index 00000000000..748f97d2bc7 --- /dev/null +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Describes the way to store scan result. + * + * @hide + */ +@SystemApi +public final class ResultStorageDescriptor implements Parcelable { + private int mType; + private int mOffset; + private int mLength; + + public int getType() { + return mType; + } + + public int getOffset() { + return mOffset; + } + + public int getLength() { + return mLength; + } + + /** + * Constructor of {@link ResultStorageDescriptor} + * + * @param type Type of the data. + * @param offset Offset from start of the advertise packet payload. + * @param length Byte length of the data + */ + public ResultStorageDescriptor(int type, int offset, int length) { + mType = type; + mOffset = offset; + mLength = length; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mOffset); + dest.writeInt(mLength); + } + + private ResultStorageDescriptor(Parcel in) { + ReadFromParcel(in); + } + + private void ReadFromParcel(Parcel in) { + mType = in.readInt(); + mOffset = in.readInt(); + mLength = in.readInt(); + } + + public static final Parcelable.Creator + CREATOR = new Creator() { + @Override + public ResultStorageDescriptor createFromParcel(Parcel source) { + return new ResultStorageDescriptor(source); + } + + @Override + public ResultStorageDescriptor[] newArray(int size) { + return new ResultStorageDescriptor[size]; + } + }; +} diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java new file mode 100644 index 00000000000..6a6b3e3c323 --- /dev/null +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * A special scan filter that lets the client decide how the scan record should be stored. + * + * @hide + */ +@SystemApi +public final class TruncatedFilter { + private final ScanFilter mFilter; + private final List mStorageDescriptors; + + /** + * Constructor for {@link TruncatedFilter}. + * + * @param filter Scan filter of the truncated filter. + * @param storageDescriptors Describes how the scan should be stored. + */ + public TruncatedFilter(ScanFilter filter, List storageDescriptors) { + mFilter = filter; + mStorageDescriptors = storageDescriptors; + } + + /** + * Returns the scan filter. + */ + public ScanFilter getFilter() { + return mFilter; + } + + /** + * Returns a list of descriptor for scan result storage. + */ + public List getStorageDescriptors() { + return mStorageDescriptors; + } + + +} -- GitLab From e13f66a39c4f04457646c4ad238fbbe26062a6bd Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 12 Aug 2014 22:16:32 -0700 Subject: [PATCH 0396/1408] Clean up advertise and scan clients upon bluetooth down. Fixes b/16528460 This allows Advertiser and Scanner to be reused after bluetooth recycle, which follows same behavior for BluetoothAdapter. Also prints manufacturer data array for ScanRecord. Change-Id: I78bca40ac294433782a054bf2a00a775dac02d96 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +++ .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 9 +++++++++ .../java/android/bluetooth/le/BluetoothLeScanner.java | 9 +++++++++ framework/java/android/bluetooth/le/Utils.java | 2 +- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 42b8dbf1a7f..f0b609aec1e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1428,6 +1428,9 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 331ebfcf724..f6315ac3e45 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -175,6 +175,15 @@ public final class BluetoothLeAdvertiser { } } + /** + * Cleans up advertise clients. Should be called when bluetooth is down. + * + * @hide + */ + public void cleanup() { + mLeAdvertisers.clear(); + } + // Compute the size of the advertise data. private int totalBytes(AdvertiseData data) { if (data == null) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 7c3cbc6163b..988cea5edb4 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -208,6 +208,15 @@ public final class BluetoothLeScanner { startScan(scanFilters, settings, callback, scanStorages); } + /** + * Cleans up scan clients. Should be called when bluetooth is down. + * + * @hide + */ + public void cleanup() { + mLeScanClients.clear(); + } + /** * Bluetooth GATT interface callbacks */ diff --git a/framework/java/android/bluetooth/le/Utils.java b/framework/java/android/bluetooth/le/Utils.java index 8598dd7772b..ccdae696469 100644 --- a/framework/java/android/bluetooth/le/Utils.java +++ b/framework/java/android/bluetooth/le/Utils.java @@ -44,7 +44,7 @@ public class Utils { StringBuilder buffer = new StringBuilder(); buffer.append('{'); for (int i = 0; i < array.size(); ++i) { - buffer.append(array.keyAt(i)).append("=").append(array.valueAt(i)); + buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i))); } buffer.append('}'); return buffer.toString(); -- GitLab From ffabc5116bd0b29467315cc5abc5c7cfa9385367 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 13 Aug 2014 14:46:58 -0700 Subject: [PATCH 0397/1408] Prevent crash in Gatt service due to null pointer in BluetoothGattCharacteristic from framework Bug 16899517 Change-Id: Ib30ddefee07c2953f9a7faeb4eaa823feeebdc8f --- framework/java/android/bluetooth/BluetoothGattServer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 5a39dd6b3ce..c8df60e800f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -519,6 +519,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @param characteristic The local characteristic that has been updated * @param confirm true to request confirmation from the client (indication), * false to send a notification + * @throws IllegalArgumentException * @return true, if the notification has been triggered successfully */ public boolean notifyCharacteristicChanged(BluetoothDevice device, @@ -529,6 +530,11 @@ public final class BluetoothGattServer implements BluetoothProfile { BluetoothGattService service = characteristic.getService(); if (service == null) return false; + if (characteristic.getValue() == null) { + throw new IllegalArgumentException("Chracteristic value is empty. Use " + + "BluetoothGattCharacteristic#setvalue to update"); + } + try { mService.sendNotification(mServerIf, device.getAddress(), service.getType(), service.getInstanceId(), -- GitLab From 90b13ed753cec24dcb754267741f820cfcd2a438 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 13 Aug 2014 16:52:55 -0700 Subject: [PATCH 0398/1408] Protect from malformed advt with invalid manf_id/data combo Bug: 16979645 Change-Id: Ie7ea00ed20cbad1120c7582bb2bafd44b4295052 --- framework/java/android/bluetooth/le/ScanRecord.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index e7f33ff1168..2f3d06fc4bc 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -257,9 +257,11 @@ public final class ScanRecord { } return new ScanRecord(serviceUuids, manufacturerData, serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); - } catch (IndexOutOfBoundsException e) { + } catch (Exception e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); - return null; + // As the record is invalid, ignore all the parsed results for this packet + // and return an empty record with raw scanRecord bytes in results + return new ScanRecord(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord); } } -- GitLab From 8350757d80242e36552b7e748e23b39c9277a455 Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Wed, 13 Aug 2014 16:31:50 +0900 Subject: [PATCH 0399/1408] Implement MHL RAP transmission. - The creation of this action will be integrated later after the device-id is ready. Bug: 16966458 Change-Id: I7bf46a8570534e2b021afea613b355a55e9f549f -- GitLab From 8a13e00e9ce93bd136c3b684f25b15541dda76aa Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 15 Aug 2014 13:25:30 -0700 Subject: [PATCH 0400/1408] Fix API council review comments. Also hide certain APIs(1/3). b/17006825. Change-Id: Iee64b11920152e0ab3da54decf37fa4a83be5dc7 --- .../android/bluetooth/le/AdvertiseData.java | 3 -- .../bluetooth/le/AdvertiseSettings.java | 47 +++++++++---------- .../android/bluetooth/le/ScanCallback.java | 5 +- .../java/android/bluetooth/le/ScanFilter.java | 2 - .../java/android/bluetooth/le/ScanResult.java | 3 -- .../android/bluetooth/le/ScanSettings.java | 24 ++++++---- 6 files changed, 41 insertions(+), 43 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 843cd846f0f..c7bfae96ad2 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -172,9 +172,6 @@ public final class AdvertiseData implements Parcelable { dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); } - /** - * @hide - */ public static final Parcelable.Creator CREATOR = new Creator() { @Override diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 71d7b5bf042..62c68a45eb0 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -66,13 +66,13 @@ public final class AdvertiseSettings implements Parcelable { public static final int ADVERTISE_TX_POWER_HIGH = 3; /** - * The maximimum limited advertisement duration as specified by the Bluetooth SIG + * The maximum limited advertisement duration as specified by the Bluetooth SIG */ - private static final int LIMITED_ADVERTISING_MAX_DURATION = 180; + private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; private final int mAdvertiseMode; private final int mAdvertiseTxPowerLevel; - private final int mAdvertiseTimeoutSeconds; + private final int mAdvertiseTimeoutMillis; private final boolean mAdvertiseConnectable; private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel, @@ -80,14 +80,14 @@ public final class AdvertiseSettings implements Parcelable { mAdvertiseMode = advertiseMode; mAdvertiseTxPowerLevel = advertiseTxPowerLevel; mAdvertiseConnectable = advertiseConnectable; - mAdvertiseTimeoutSeconds = advertiseTimeout; + mAdvertiseTimeoutMillis = advertiseTimeout; } private AdvertiseSettings(Parcel in) { mAdvertiseMode = in.readInt(); mAdvertiseTxPowerLevel = in.readInt(); mAdvertiseConnectable = in.readInt() != 0 ? true : false; - mAdvertiseTimeoutSeconds = in.readInt(); + mAdvertiseTimeoutMillis = in.readInt(); } /** @@ -107,15 +107,15 @@ public final class AdvertiseSettings implements Parcelable { /** * Returns whether the advertisement will indicate connectable. */ - public boolean getIsConnectable() { + public boolean isConnectable() { return mAdvertiseConnectable; } /** - * Returns the advertising time limit in seconds. + * Returns the advertising time limit in milliseconds. */ public int getTimeout() { - return mAdvertiseTimeoutSeconds; + return mAdvertiseTimeoutMillis; } @Override @@ -123,7 +123,7 @@ public final class AdvertiseSettings implements Parcelable { return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel + ", mAdvertiseConnectable=" + mAdvertiseConnectable - + ", mAdvertiseTimeoutSeconds=" + mAdvertiseTimeoutSeconds + "]"; + + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]"; } @Override @@ -136,12 +136,9 @@ public final class AdvertiseSettings implements Parcelable { dest.writeInt(mAdvertiseMode); dest.writeInt(mAdvertiseTxPowerLevel); dest.writeInt(mAdvertiseConnectable ? 1 : 0); - dest.writeInt(mAdvertiseTimeoutSeconds); + dest.writeInt(mAdvertiseTimeoutMillis); } - /** - * @hide - */ public static final Parcelable.Creator CREATOR = new Creator() { @Override @@ -161,7 +158,7 @@ public final class AdvertiseSettings implements Parcelable { public static final class Builder { private int mMode = ADVERTISE_MODE_LOW_POWER; private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; - private int mTimeoutSeconds = 0; + private int mTimeoutMillis = 0; private boolean mConnectable = true; /** @@ -204,26 +201,26 @@ public final class AdvertiseSettings implements Parcelable { /** * Set whether the advertisement type should be connectable or non-connectable. * - * @param isConnectable Controls whether the advertisment type will be connectable (true) + * @param connectable Controls whether the advertisment type will be connectable (true) * or non-connectable (false). */ - public Builder setIsConnectable(boolean isConnectable) { - mConnectable = isConnectable; + public Builder setConnectable(boolean connectable) { + mConnectable = connectable; return this; } /** * Limit advertising to a given amount of time. - * @param timeoutSeconds Advertising time limit. May not exceed 180 seconds. + * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. * A value of 0 will disable the time limit. - * @throws IllegalArgumentException If the provided timeout is over 180s. + * @throws IllegalArgumentException If the provided timeout is over 180000 ms. */ - public Builder setTimeout(int timeoutSeconds) { - if (timeoutSeconds < 0 || timeoutSeconds > LIMITED_ADVERTISING_MAX_DURATION) { - throw new IllegalArgumentException("timeoutSeconds invalid (must be 0-" - + LIMITED_ADVERTISING_MAX_DURATION + " seconds)"); + public Builder setTimeout(int timeoutMillis) { + if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { + throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" + + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)"); } - mTimeoutSeconds = timeoutSeconds; + mTimeoutMillis = timeoutMillis; return this; } @@ -231,7 +228,7 @@ public final class AdvertiseSettings implements Parcelable { * Build the {@link AdvertiseSettings} object. */ public AdvertiseSettings build() { - return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutSeconds); + return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis); } } } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 5b373845a79..05782a86d81 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -47,8 +47,8 @@ public abstract class ScanCallback { /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines if this callback was triggered because of first match, a lost - * match indication or a regular scan result. + * @param callbackType Determines how this callback was triggered. Currently could only be + * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}. * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { @@ -64,6 +64,7 @@ public abstract class ScanCallback { /** * Callback when scan could not be started. + * * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure. */ public void onScanFailed(int errorCode) { diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index d1b93d234b5..502521893ec 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -138,8 +138,6 @@ public final class ScanFilter implements Parcelable { /** * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. - * - * @hide */ public static final Creator CREATOR = new Creator() { diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index a0bdaffd866..2fdfe7f8a83 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -149,9 +149,6 @@ public final class ScanResult implements Parcelable { + mTimestampNanos + '}'; } - /** - * @hide - */ public static final Parcelable.Creator CREATOR = new Creator() { @Override public ScanResult createFromParcel(Parcel source) { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index b2ee6a82c94..7eae4398e59 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -52,19 +52,28 @@ public final class ScanSettings implements Parcelable { /** * A result callback is only triggered for the first advertisement packet received that matches * the filter criteria. + * + * @hide */ + @SystemApi public static final int CALLBACK_TYPE_FIRST_MATCH = 2; /** * Receive a callback when advertisements are no longer received from a device that has been * previously reported by a first match callback. + * + * @hide */ + @SystemApi public static final int CALLBACK_TYPE_MATCH_LOST = 4; /** * Request full scan results which contain the device, rssi, advertising data, scan response as * well as the scan timestamp. + * + * @hide */ + @SystemApi public static final int SCAN_RESULT_TYPE_FULL = 0; /** @@ -137,9 +146,6 @@ public final class ScanSettings implements Parcelable { return 0; } - /** - * @hide - */ public static final Parcelable.Creator CREATOR = new Creator() { @Override @@ -183,7 +189,9 @@ public final class ScanSettings implements Parcelable { * * @param callbackType The callback type flags for the scan. * @throws IllegalArgumentException If the {@code callbackType} is invalid. + * @hide */ + @SystemApi public Builder setCallbackType(int callbackType) { if (!isValidCallbackType(callbackType)) { @@ -226,14 +234,14 @@ public final class ScanSettings implements Parcelable { /** * Set report delay timestamp for Bluetooth LE scan. * - * @param reportDelayMillis Set to 0 to be notified of results immediately. Values > 0 - * causes the scan results to be queued up and delivered after the requested - * delay or when the internal buffers fill up. + * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of + * results immediately. Values > 0 causes the scan results to be queued up and + * delivered after the requested delay or when the internal buffers fill up. * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. */ - public Builder setReportDelayMillis(long reportDelayMillis) { + public Builder setReportDelay(long reportDelayMillis) { if (reportDelayMillis < 0) { - throw new IllegalArgumentException("reportDelayMillis must be > 0"); + throw new IllegalArgumentException("reportDelay must be > 0"); } mReportDelayMillis = reportDelayMillis; return this; -- GitLab From 8adda7c563ad656ab476249407744ec69ea3ecef Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Mon, 18 Aug 2014 15:31:57 +0200 Subject: [PATCH 0401/1408] Allow SystemUI to control Bluetooth for secondary users Bug: 15818610 Change-Id: I9eaeaffc73f7675957ed3cd6a896ea83a79d6cff --- .../server/bluetooth/BluetoothManagerService.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 07c904883f9..7b64139e9cb 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -121,6 +121,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private int mState; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; + private final int mSystemUiUid; private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); @@ -218,6 +219,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBluetoothPersistedStateOn()) { mEnableExternal = true; } + + int sysUiUid = -1; + try { + sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui", + UserHandle.USER_OWNER); + } catch (PackageManager.NameNotFoundException e) { + Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e); + } + mSystemUiUid = sysUiUid; } /** @@ -1118,7 +1128,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { foregroundUser = ActivityManager.getCurrentUser(); valid = (callingUser == foregroundUser) || - callingAppId == Process.NFC_UID; + callingAppId == Process.NFC_UID || + callingAppId == mSystemUiUid; if (DBG) { Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser -- GitLab From 3a6c35dba660c77a11cefbd98c031137e6fd0530 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 18 Aug 2014 16:08:00 -0700 Subject: [PATCH 0402/1408] Fix race condition for BluetoothLeScanner. Root cause of the issue: Client registeration was done on caller's thread, while onClientRegistered was called from binder thread. There is a slight chance that onClientRegistered was called before registerClient returned, causing a premature notifyAll before wait was called. By forcing acquiring the same lock now it's guaranteed wait would happen before onClientRegistered callback was executed. Bug: 16707401 Change-Id: Ic60adec475085741d14ecd2d2bb462246f88da58 --- .../bluetooth/le/BluetoothLeScanner.java | 62 ++++++++----------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 988cea5edb4..e1d4bbd2b57 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -130,20 +130,7 @@ public final class BluetoothLeScanner { } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, callback, resultStorages); - try { - UUID uuid = UUID.randomUUID(); - gatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.scanStarted()) { - mLeScanClients.put(callback, wrapper); - } else { - postCallbackError(callback, - ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); - return; - } - } catch (RemoteException e) { - Log.e(TAG, "GATT service exception when starting scan", e); - postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); - } + wrapper.startRegisteration(); } } @@ -159,8 +146,7 @@ public final class BluetoothLeScanner { synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { - if (DBG) - Log.d(TAG, "could not find callback wrapper"); + if (DBG) Log.d(TAG, "could not find callback wrapper"); return; } wrapper.stopLeScan(); @@ -220,8 +206,8 @@ public final class BluetoothLeScanner { /** * Bluetooth GATT interface callbacks */ - private static class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { - private static final int REGISTRATION_CALLBACK_TIMEOUT_SECONDS = 5; + private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { + private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; private final ScanCallback mScanCallback; private final List mFilters; @@ -245,18 +231,25 @@ public final class BluetoothLeScanner { mResultStorages = resultStorages; } - public boolean scanStarted() { + public void startRegisteration() { synchronized (this) { - if (mClientIf == -1) { - return false; - } + // Scan stopped. + if (mClientIf == -1) return; try { - wait(REGISTRATION_CALLBACK_TIMEOUT_SECONDS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); + UUID uuid = UUID.randomUUID(); + mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); + wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); + } catch (InterruptedException | RemoteException e) { + Log.e(TAG, "application registeration exception", e); + postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); + } + if (mClientIf > 0) { + mLeScanClients.put(mScanCallback, this); + } else { + postCallbackError(mScanCallback, + ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); } } - return mClientIf > 0; } public void stopLeScan() { @@ -272,7 +265,6 @@ public final class BluetoothLeScanner { Log.e(TAG, "Failed to stop scan and unregister", e); } mClientIf = -1; - notifyAll(); } } @@ -297,11 +289,9 @@ public final class BluetoothLeScanner { public void onClientRegistered(int status, int clientIf) { Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); - synchronized (this) { if (mClientIf == -1) { - if (DBG) - Log.d(TAG, "onClientRegistered LE scan canceled"); + if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); } if (status == BluetoothGatt.GATT_SUCCESS) { @@ -328,17 +318,15 @@ public final class BluetoothLeScanner { */ @Override public void onScanResult(final ScanResult scanResult) { - if (DBG) - Log.d(TAG, "onScanResult() - " + scanResult.toString()); + if (DBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { - if (mClientIf <= 0) - return; + if (mClientIf <= 0) return; } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { - @Override + @Override public void run() { mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); } @@ -350,7 +338,7 @@ public final class BluetoothLeScanner { public void onBatchScanResults(final List results) { Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { - @Override + @Override public void run() { mScanCallback.onBatchScanResults(results); } @@ -394,7 +382,7 @@ public final class BluetoothLeScanner { private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { - @Override + @Override public void run() { callback.onScanFailed(errorCode); } -- GitLab From 8a6588e792fd82c543e085b782c58a3d43ae1849 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 18 Aug 2014 21:58:32 -0700 Subject: [PATCH 0403/1408] Fix threading issue for advertising. Post callback to main thread to execute. In general we should avoid running app's callback method from binder thread as the callback method may block. Also removed callback from advertisers on stop advertising callback. This fixes the issue of not being able to restart adv for limited advertising. Bug: 17045567 Change-Id: I56cd2bdf4b1ad120a0358fa4065ad77be4bff144 --- .../bluetooth/le/BluetoothLeAdvertiser.java | 163 +++++++++--------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index f6315ac3e45..3568f262465 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -21,7 +21,6 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; @@ -30,7 +29,6 @@ import android.os.RemoteException; import android.util.Log; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; @@ -108,41 +106,37 @@ public final class BluetoothLeAdvertiser { public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { - checkAdapterState(); - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); - return; - } - if (mLeAdvertisers.containsKey(callback)) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); - return; - } - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; - } - if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) { - postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); - return; - } - AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - scanResponse, settings, gatt); - UUID uuid = UUID.randomUUID(); - try { - gatt.registerClient(new ParcelUuid(uuid), wrapper); - if (wrapper.advertiseStarted()) { - mLeAdvertisers.put(callback, wrapper); + synchronized (mLeAdvertisers) { + checkAdapterState(); + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) { + postStartFailure(callback, + AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); + return; + } + if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES || + totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) { + postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); + return; } - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising", e); + if (mLeAdvertisers.containsKey(callback)) { + postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); + return; + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; + } + AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, + scanResponse, settings, gatt); + wrapper.startRegisteration(); } } @@ -155,23 +149,14 @@ public final class BluetoothLeAdvertiser { * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. */ public void stopAdvertising(final AdvertiseCallback callback) { - checkAdapterState(); - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); - if (wrapper == null) - return; - try { - IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt(); - if (gatt != null) - gatt.stopMultiAdvertising(wrapper.mClientIf); - - if (wrapper.advertiseStopped()) { - mLeAdvertisers.remove(callback); + synchronized (mLeAdvertisers) { + checkAdapterState(); + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); } - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising", e); + AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); + if (wrapper == null) return; + wrapper.stopAdvertising(); } } @@ -186,9 +171,7 @@ public final class BluetoothLeAdvertiser { // Compute the size of the advertise data. private int totalBytes(AdvertiseData data) { - if (data == null) { - return 0; - } + if (data == null) return 0; int size = FLAGS_FIELD_BYTES; // flags field is always set. if (data.getServiceUuids() != null) { int num16BitUuids = 0; @@ -243,7 +226,7 @@ public final class BluetoothLeAdvertiser { /** * Bluetooth GATT interface callbacks for advertising. */ - private static class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { + private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; private final AdvertiseData mAdvertisement; @@ -269,30 +252,40 @@ public final class BluetoothLeAdvertiser { mClientIf = 0; } - public boolean advertiseStarted() { - boolean started = false; + public void startRegisteration() { synchronized (this) { - if (mClientIf == -1) { - return false; - } + if (mClientIf == -1) return; + try { + UUID uuid = UUID.randomUUID(); + mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: ", e); + } catch (InterruptedException | RemoteException e) { + Log.e(TAG, "Failed to start registeration", e); + } + if (mClientIf > 0 && mIsAdvertising) { + mLeAdvertisers.put(mAdvertiseCallback, this); + } else { + postStartFailure(mAdvertiseCallback, + AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); } - started = (mClientIf > 0 && mIsAdvertising); } - return started; } - public boolean advertiseStopped() { + public void stopAdvertising() { synchronized (this) { try { + mBluetoothGatt.stopMultiAdvertising(mClientIf); wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException e) { - Log.e(TAG, "Callback reg wait interrupted: " + e); + } catch (InterruptedException | RemoteException e) { + Log.e(TAG, "Failed to stop advertising", e); + } + // Advertise callback should have been removed from LeAdvertisers when + // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never + // invoked and wait timeout expires, remove callback here. + if (mLeAdvertisers.containsKey(mAdvertiseCallback)) { + mLeAdvertisers.remove(mAdvertiseCallback); } - return !mIsAdvertising; } } @@ -308,16 +301,14 @@ public final class BluetoothLeAdvertiser { try { mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, mScanResponse, mSettings); + return; } catch (RemoteException e) { - Log.e(TAG, "fail to start le advertise: " + e); - mClientIf = -1; - notifyAll(); + Log.e(TAG, "failed to start advertising", e); } - } else { - // registration failed - mClientIf = -1; - notifyAll(); } + // Registration failed. + mClientIf = -1; + notifyAll(); } } @@ -328,11 +319,11 @@ public final class BluetoothLeAdvertiser { if (isStart) { if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { // Start success - mAdvertiseCallback.onStartSuccess(settings); mIsAdvertising = true; + postStartSuccess(mAdvertiseCallback, settings); } else { // Start failure. - mAdvertiseCallback.onStartFailure(status); + postStartFailure(mAdvertiseCallback, status); } } else { // unregister client for stop. @@ -340,6 +331,7 @@ public final class BluetoothLeAdvertiser { mBluetoothGatt.unregisterClient(mClientIf); mClientIf = -1; mIsAdvertising = false; + mLeAdvertisers.remove(mAdvertiseCallback); } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } @@ -357,12 +349,23 @@ public final class BluetoothLeAdvertiser { } } - private void postCallbackFailure(final AdvertiseCallback callback, final int error) { + private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { - @Override + @Override public void run() { callback.onStartFailure(error); } }); } + + private void postStartSuccess(final AdvertiseCallback callback, + final AdvertiseSettings settings) { + mHandler.post(new Runnable() { + + @Override + public void run() { + callback.onStartSuccess(settings); + } + }); + } } -- GitLab From 6b54db9d5e40f09b561a6666b9d29fb087e5f904 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 19 Aug 2014 17:58:55 -0700 Subject: [PATCH 0404/1408] LE: Rename new MTU and connection paramter update APIs bug 17005342 Change-Id: I23151e1e92fe86896fc2883f81aef82084bb4215 --- .../java/android/bluetooth/BluetoothGatt.java | 32 +++++++++---------- .../bluetooth/BluetoothGattCallback.java | 6 ++-- .../BluetoothGattServerCallback.java | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 59d7956b542..d77a77ba412 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -96,19 +96,19 @@ public final class BluetoothGatt implements BluetoothProfile { * Bluetooth SIG. This is the default value if no connection parameter update * is requested. */ - public static final int GATT_CONNECTION_BALANCED = 0; + public static final int CONNECTION_PRIORITY_BALANCED = 0; /** * Connection paramter update - Request a high priority, low latency connection. * An application should only request high priority connection paramters to transfer * large amounts of data over LE quickly. Once the transfer is complete, the application - * should request {@link BluetoothGatt#GATT_CONNECTION_BALANCED} connectoin parameters + * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters * to reduce energy use. */ - public static final int GATT_CONNECTION_HIGH_PRIORITY = 1; + public static final int CONNECTION_PRIORITY_HIGH = 1; /** Connection paramter update - Request low power, reduced data rate connection parameters. */ - public static final int GATT_CONNECTION_LOW_POWER = 2; + public static final int CONNECTION_PRIORITY_LOW_POWER = 2; /** * No authentication required. @@ -601,7 +601,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } try { - mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status); + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception in callback", ex); } @@ -1239,20 +1239,20 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Configure the MTU used for a given connection. + * Request an MTU size used for a given connection. * *

        When performing a write request operation (write without response), * the data sent is truncated to the MTU size. This function may be used - * to request a larget MTU size to be able to send more data at once. + * to request a larger MTU size to be able to send more data at once. * - *

        A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate + *

        A {@link BluetoothGattCallback#onMtuChanged} callback will indicate * whether this operation was successful. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return true, if the new MTU value has been requested successfully */ - public boolean configureMTU(int mtu) { + public boolean requestMtu(int mtu) { if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() + " mtu: " + mtu); if (mService == null || mClientIf == 0) return false; @@ -1274,19 +1274,19 @@ public final class BluetoothGatt implements BluetoothProfile { * remote device. * * @param connectionPriority Request a specific connection priority. Must be one of - * {@link BluetoothGatt#GATT_CONNECTION_BALANCED}, - * {@link BluetoothGatt#GATT_CONNECTION_HIGH_PRIORITY} - * or {@link BluetoothGatt#GATT_CONNECTION_LOW_POWER}. + * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, + * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} + * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. * @throws IllegalArgumentException If the parameters are outside of their * specified range. */ - public boolean requestConnectionParameterUpdate(int connectionPriority) { - if (connectionPriority < GATT_CONNECTION_BALANCED || - connectionPriority > GATT_CONNECTION_LOW_POWER) { + public boolean requestConnectionPriority(int connectionPriority) { + if (connectionPriority < CONNECTION_PRIORITY_BALANCED || + connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { throw new IllegalArgumentException("connectionPriority not within valid range"); } - if (DBG) Log.d(TAG, "requestConnectionParameterUpdate() - params: " + connectionPriority); + if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority); if (mService == null || mClientIf == 0) return false; try { diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 5817d6844ae..19900ec97e5 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -143,14 +143,14 @@ public abstract class BluetoothGattCallback { * Callback indicating the MTU for a given device connection has changed. * * This callback is triggered in response to the - * {@link BluetoothGatt#configureMTU} function, or in response to a connection + * {@link BluetoothGatt#requestMtu} function, or in response to a connection * event. * - * @param gatt GATT client invoked {@link BluetoothGatt#configureMTU} + * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} * @param mtu The new MTU size * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully */ - public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { } /** diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 3a1b38ee6b3..b0ddc26ef38 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -141,7 +141,7 @@ public abstract class BluetoothGattServerCallback { * notifications. * * @param device The remote device the notification has been sent to - * @param status 0 if the operation was successful + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful */ public void onNotificationSent(BluetoothDevice device, int status) { } -- GitLab From e1a357c1cd7ebbbf19addd646af63da908c9ba2a Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 29 Aug 2014 10:26:13 -0700 Subject: [PATCH 0405/1408] Unregister app when advertise failed after registration. Also moved bluetooth state check to common utils class. Bug:17364837 Change-Id: I25ff7afae723d47f701236d5a68eab8015de9bbb --- .../android/bluetooth/le/AdvertiseData.java | 8 +++---- .../bluetooth/le/BluetoothLeAdvertiser.java | 22 ++++++++++--------- .../bluetooth/le/BluetoothLeScanner.java | 16 ++++---------- .../le/{Utils.java => BluetoothLeUtils.java} | 16 +++++++++++++- .../java/android/bluetooth/le/ScanRecord.java | 4 ++-- 5 files changed, 37 insertions(+), 29 deletions(-) rename framework/java/android/bluetooth/le/{Utils.java => BluetoothLeUtils.java} (87%) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index c7bfae96ad2..ff0db9aeb61 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -119,8 +119,8 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && - Utils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && - Utils.equals(mServiceData, other.mServiceData) && + BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + BluetoothLeUtils.equals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @@ -128,8 +128,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" - + Utils.toString(mManufacturerSpecificData) + ", mServiceData=" - + Utils.toString(mServiceData) + + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" + mIncludeDeviceName + "]"; } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 3568f262465..d46850843f6 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -107,7 +107,7 @@ public final class BluetoothLeAdvertiser { AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { synchronized (mLeAdvertisers) { - checkAdapterState(); + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } @@ -150,7 +150,7 @@ public final class BluetoothLeAdvertiser { */ public void stopAdvertising(final AdvertiseCallback callback) { synchronized (mLeAdvertisers) { - checkAdapterState(); + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } @@ -265,9 +265,18 @@ public final class BluetoothLeAdvertiser { } if (mClientIf > 0 && mIsAdvertising) { mLeAdvertisers.put(mAdvertiseCallback, this); - } else { + } else if (mClientIf <= 0) { + // Post internal error if registration failed. postStartFailure(mAdvertiseCallback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + } else { + // Unregister application if it's already registered but advertise failed. + try { + mBluetoothGatt.unregisterClient(mClientIf); + mClientIf = -1; + } catch (RemoteException e) { + Log.e(TAG, "remote exception when unregistering", e); + } } } } @@ -342,13 +351,6 @@ public final class BluetoothLeAdvertiser { } } - // TODO: move this api to a common util class. - private void checkAdapterState() { - if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { - throw new IllegalStateException("BT Adapter is not turned ON"); - } - } - private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index e1d4bbd2b57..a57c3ca25a9 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -80,11 +80,10 @@ public final class BluetoothLeScanner { * @throws IllegalArgumentException If {@code callback} is null. */ public void startScan(final ScanCallback callback) { - checkAdapterState(); if (callback == null) { throw new IllegalArgumentException("callback is null"); } - this.startScan(null, new ScanSettings.Builder().build(), callback); + startScan(null, new ScanSettings.Builder().build(), callback); } /** @@ -104,7 +103,7 @@ public final class BluetoothLeScanner { private void startScan(List filters, ScanSettings settings, final ScanCallback callback, List> resultStorages) { - checkAdapterState(); + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (settings == null || callback == null) { throw new IllegalArgumentException("settings or callback is null"); } @@ -142,7 +141,7 @@ public final class BluetoothLeScanner { * @param callback */ public void stopScan(ScanCallback callback) { - checkAdapterState(); + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { @@ -162,7 +161,7 @@ public final class BluetoothLeScanner { * used to start scan. */ public void flushPendingScanResults(ScanCallback callback) { - checkAdapterState(); + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null!"); } @@ -373,13 +372,6 @@ public final class BluetoothLeScanner { } } - // TODO: move this api to a common util class. - private void checkAdapterState() { - if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { - throw new IllegalStateException("BT Adapter is not turned ON"); - } - } - private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { @Override diff --git a/framework/java/android/bluetooth/le/Utils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java similarity index 87% rename from framework/java/android/bluetooth/le/Utils.java rename to framework/java/android/bluetooth/le/BluetoothLeUtils.java index ccdae696469..4916bd9cebf 100644 --- a/framework/java/android/bluetooth/le/Utils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.bluetooth.BluetoothAdapter; import android.util.SparseArray; import java.util.Arrays; @@ -29,7 +30,7 @@ import java.util.Set; * * @hide */ -public class Utils { +public class BluetoothLeUtils { /** * Returns a string composed from a {@link SparseArray}. @@ -123,4 +124,17 @@ public class Utils { } return true; } + + /** + * Ensure Bluetooth is turned on. + * + * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not + * {@link BluetoothAdapter#STATE_ON}. + */ + static void checkAdapterStateOn(BluetoothAdapter adapter) { + if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) { + throw new IllegalStateException("BT Adapter is not turned ON"); + } + } + } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 2f3d06fc4bc..f802e8d1295 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -268,8 +268,8 @@ public final class ScanRecord { @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerSpecificData=" + Utils.toString(mManufacturerSpecificData) - + ", mServiceData=" + Utils.toString(mServiceData) + + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } -- GitLab From 6398bb323caca926e6d8a247eba7c1f78a796bda Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Wed, 3 Sep 2014 10:04:00 -0700 Subject: [PATCH 0406/1408] Close the base socket when bindListen fails Bug: 10773872 Change-Id: I0a72b2eb65055fa1959070d2dc32d40a573bd6f2 --- framework/java/android/bluetooth/BluetoothSocket.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index b98e5aed8ff..36997e54484 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -375,6 +375,14 @@ public final class BluetoothSocket implements Closeable { } // else ASSERT(mPort == channel) ret = 0; } catch (IOException e) { + if (mPfd != null) { + try { + mPfd.close(); + } catch (IOException e1) { + Log.e(TAG, "bindListen, close mPfd: " + e1); + } + mPfd = null; + } Log.e(TAG, "bindListen, fail to get port number, exception: " + e); return -1; } -- GitLab From 3816010ca576dc946d13deb3816e9112d63e49d8 Mon Sep 17 00:00:00 2001 From: Edward Jee Date: Fri, 5 Sep 2014 00:29:14 -0700 Subject: [PATCH 0407/1408] Adds APIs for Bluetooth PBAP and MAP access permission. Currently, users' preference in phonebook and call history or message access per each Bluetooth-paired device is stored in Settings application's shared preferences. However, some privileged applications other than Settings need to access such data. So we decided to migrate the data from Settings application's shared preferences to Bluetooth application's. Bug: 17158953 Change-Id: I3771f03eaea3f57cf60d93ab9fd41c9eb3e1e99c --- .../android/bluetooth/BluetoothDevice.java | 131 +++++++++++++----- .../java/android/bluetooth/IBluetooth.aidl | 5 + 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 860512b9f9d..5e50b696b22 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -517,6 +517,27 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + /** + * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, + * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * @hide + */ + public static final int ACCESS_UNKNOWN = 0; + + /** + * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, + * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * @hide + */ + public static final int ACCESS_ALLOWED = 1; + + /** + * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, + * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * @hide + */ + public static final int ACCESS_REJECTED = 2; + /** * No preferrence of physical transport for GATT connections to remote dual-mode devices * @hide @@ -951,39 +972,6 @@ public final class BluetoothDevice implements Parcelable { return null; } - /** - * Get trust state of a remote device. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. - * @hide - */ - public boolean getTrustState() { - //TODO(BT) - /* - try { - return sService.getTrustState(this); - } catch (RemoteException e) { - Log.e(TAG, "", e); - }*/ - return false; - } - - /** - * Set trust state for a remote device. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. - * @param value the trust state value (true or false) - * @hide - */ - public boolean setTrust(boolean value) { - //TODO(BT) - /* - try { - return sService.setTrust(this, value); - } catch (RemoteException e) { - Log.e(TAG, "", e); - }*/ - return false; - } - /** * Returns the supported features (UUIDs) of the remote device. * @@ -1134,6 +1122,82 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Requires {@link android.Manifest.permission#BLUETOOTH}. + * @return Whether the phonebook access is allowed to this device. Can be + * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @hide + */ + public int getPhonebookAccessPermission() { + if (sService == null) { + return ACCESS_UNKNOWN; + } + try { + return sService.getPhonebookAccessPermission(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return ACCESS_UNKNOWN; + } + + /** + * Sets whether the phonebook access is allowed to this device. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or + * {@link #ACCESS_REJECTED}. + * @return Whether the value has been successfully set. + * @hide + */ + public boolean setPhonebookAccessPermission(int value) { + if (sService == null) { + return false; + } + try { + return sService.setPhonebookAccessPermission(this, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + + /** + * Requires {@link android.Manifest.permission#BLUETOOTH}. + * @return Whether the message access is allowed to this device. Can be + * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @hide + */ + public int getMessageAccessPermission() { + if (sService == null) { + return ACCESS_UNKNOWN; + } + try { + return sService.getMessageAccessPermission(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return ACCESS_UNKNOWN; + } + + /** + * Sets whether the message access is allowed to this device. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or + * {@link #ACCESS_REJECTED}. + * @return Whether the value has been successfully set. + * @hide + */ + public boolean setMessageAccessPermission(int value) { + if (sService == null) { + return false; + } + try { + return sService.setMessageAccessPermission(this, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. @@ -1343,5 +1407,4 @@ public final class BluetoothDevice implements Parcelable { } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } - } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 19c600cafe9..cf2a343dc04 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -75,6 +75,11 @@ interface IBluetooth passkey); boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); + int getPhonebookAccessPermission(in BluetoothDevice device); + boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); + int getMessageAccessPermission(in BluetoothDevice device); + boolean setMessageAccessPermission(in BluetoothDevice device, int value); + void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); void registerCallback(in IBluetoothCallback callback); -- GitLab From d50b5615c1364b6aa57eec33318533063a3603d7 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 11 Sep 2014 13:39:16 -0700 Subject: [PATCH 0408/1408] Remove onConnectionCongested callback Bug:17289507 Change-Id: I49fd99d320084c618dcec756b832fb3b6a6aec51 --- .../java/android/bluetooth/BluetoothGatt.java | 3 ++- .../android/bluetooth/BluetoothGattCallback.java | 14 -------------- .../android/bluetooth/BluetoothGattServer.java | 3 ++- .../bluetooth/BluetoothGattServerCallback.java | 14 -------------- 4 files changed, 4 insertions(+), 30 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index d77a77ba412..22762297f92 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; + private Boolean mIsCongested = false; private int mTransport; private static final int CONN_STATE_IDLE = 0; @@ -616,7 +617,7 @@ public final class BluetoothGatt implements BluetoothProfile { + " congested=" + congested); if (!address.equals(mDevice.getAddress())) return; try { - mCallback.onConnectionCongested(BluetoothGatt.this, congested); + mIsCongested = congested; } catch (Exception ex) { Log.w(TAG, "Unhandled exception in callback", ex); } diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 19900ec97e5..a9156205afc 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -152,18 +152,4 @@ public abstract class BluetoothGattCallback { */ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { } - - /** - * Callback indicating that a remote device connection congestestion status has changed. - * - * An application should refrain from sending additional data to a remote device when - * a callback is received with the congested flag set to true. Once the congestion status - * is cleared up, the application will receive an additional callback with the congested - * flag set to false. - * - * @param gatt The GATT client associated with the remote device - * @param congested true, if the connection is currently congested - */ - public void onConnectionCongested(BluetoothGatt gatt, boolean congested) { - } } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index c8df60e800f..a7f117b44c5 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -48,6 +48,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; + private Boolean mIsCongested = false; private Object mServerIfLock = new Object(); private int mServerIf; @@ -297,7 +298,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (device == null) return; try { - mCallback.onConnectionCongested(device, congested); + mIsCongested = congested; } catch (Exception ex) { Log.w(TAG, "Unhandled exception in callback", ex); } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index b0ddc26ef38..1dd06f2ce1c 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -145,18 +145,4 @@ public abstract class BluetoothGattServerCallback { */ public void onNotificationSent(BluetoothDevice device, int status) { } - - /** - * Callback indicating that a remote device connection congestestion status has changed. - * - * An application should refrain from sending additional data (notifications, indications - * etc.) to a remote device when a callback is received with the congested flag set - * to true. Once the congestion status is cleared up, the application will receive an - * additional callback with the congested flag set to false. - * - * @param device The remote device that triggered the congestion state change - * @param congested true, if the connection is currently congested - */ - public void onConnectionCongested(BluetoothDevice device, boolean congested) { - } } -- GitLab From 645b737cbb14933c4ab3da4017559362225c7a93 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Fri, 19 Sep 2014 10:57:54 -0700 Subject: [PATCH 0409/1408] Remove remanents of onConnectionCongested in framwork Bug:17289507 Change-Id: Ia28c39822141edd0e4dc3f623b8c2f280fc5f943 --- .../java/android/bluetooth/BluetoothGatt.java | 16 ---------------- .../BluetoothGattCallbackWrapper.java | 4 ---- .../bluetooth/BluetoothGattServer.java | 19 ------------------- .../bluetooth/IBluetoothGattCallback.aidl | 1 - .../IBluetoothGattServerCallback.aidl | 1 - 5 files changed, 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 22762297f92..c203a8e69e7 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -51,7 +51,6 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; - private Boolean mIsCongested = false; private int mTransport; private static final int CONN_STATE_IDLE = 0; @@ -607,21 +606,6 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } - - /** - * Callback indicating the remote device connection is congested. - * @hide - */ - public void onConnectionCongested(String address, boolean congested) { - if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address - + " congested=" + congested); - if (!address.equals(mDevice.getAddress())) return; - try { - mIsCongested = congested; - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } }; /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index da992f55f25..cdb24f40e2d 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -119,10 +119,6 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { public void onConfigureMTU(String address, int mtu, int status) throws RemoteException { } - @Override - public void onConnectionCongested(String address, boolean congested) throws RemoteException { - } - @Override public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException { } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index a7f117b44c5..e94a8cea216 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -48,7 +48,6 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; - private Boolean mIsCongested = false; private Object mServerIfLock = new Object(); private int mServerIf; @@ -285,24 +284,6 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception: " + ex); } } - - /** - * Callback indicating the remote device connection is congested. - * @hide - */ - public void onConnectionCongested(String address, boolean congested) { - if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address - + " congested=" + congested); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mIsCongested = congested; - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } }; /** diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 00b6b1b7f54..91e62ea65c7 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -68,6 +68,5 @@ oneway interface IBluetoothGattCallback { void onMultiAdvertiseCallback(in int status, boolean isStart, in AdvertiseSettings advertiseSettings); void onConfigureMTU(in String address, in int mtu, in int status); - void onConnectionCongested(in String address, in boolean congested); void onFoundOrLost(in boolean onFound, in ScanResult scanResult); } diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index 6e31da1d31e..5d4d6c63efd 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -59,5 +59,4 @@ oneway interface IBluetoothGattServerCallback { in byte[] value); void onExecuteWrite(in String address, in int transId, in boolean execWrite); void onNotificationSent(in String address, in int status); - void onConnectionCongested(in String address, in boolean congested); } -- GitLab From 52babc2fd3d3010760ef04f02e034919d8997e24 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 25 Sep 2014 19:05:03 -0700 Subject: [PATCH 0410/1408] Add support of advertising through standard instance.(1/4) Use config overlay to check whether peripheral mode is supported. Bug: 17552672 Change-Id: I1081bc84da9fe033426f82ece2ec74c2d663e3aa --- .../android/bluetooth/BluetoothAdapter.java | 18 +++++++++++++++++- .../java/android/bluetooth/IBluetooth.aidl | 1 + .../bluetooth/le/BluetoothLeAdvertiser.java | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f0b609aec1e..210bbdf0548 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -464,7 +464,8 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) { return null; } - if (!isMultipleAdvertisementSupported()) { + if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) { + Log.e(TAG, "bluetooth le advertising not supported"); return null; } synchronized(mLock) { @@ -916,6 +917,21 @@ public final class BluetoothAdapter { return false; } + /** + * Returns whether peripheral mode is supported. + * + * @hide + */ + public boolean isPeripheralModeSupported() { + if (getState() != STATE_ON) return false; + try { + return mService.isPeripheralModeSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get peripheral mode capability: ", e); + } + return false; + } + /** * Return true if offloaded filters are supported * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index cf2a343dc04..992f601a20d 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -92,6 +92,7 @@ interface IBluetooth boolean configHciSnoopLog(boolean enable); boolean isMultiAdvertisementSupported(); + boolean isPeripheralModeSupported(); boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index d46850843f6..a019d5cff2a 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -111,7 +111,8 @@ public final class BluetoothLeAdvertiser { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } - if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) { + if (!mBluetoothAdapter.isMultipleAdvertisementSupported() && + !mBluetoothAdapter.isPeripheralModeSupported()) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); return; -- GitLab From 5d9ea3f257b9c28e5bcf066d0448f47effed6cbf Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 10 Oct 2014 15:03:13 -0700 Subject: [PATCH 0411/1408] Fix issue #17829949: Don't kill Bluetooth service... ...even in extreme low memory condition Bind to Bluetooth with BIND_IMPORTANT, so that it is allowed to go to a higher oom adj level. Fix some problems when this is done from a system or persistent process, where this would go to a level that is *too* high. Instead, introduce a new oom adj level for it that is right below persistent. Change-Id: I002bcc4accc36c8579c4cda161be7d2fba21ba17 --- .../server/bluetooth/BluetoothManagerService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 7b64139e9cb..636228baaee 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -663,7 +663,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); Intent i = new Intent(IBluetooth.class.getName()); if (!doBind(i, mConnection, - Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) { + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT)) { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); } else { mBinding = true; @@ -1050,7 +1051,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); mConnection.setGetNameAddressOnly(false); Intent i = new Intent(IBluetooth.class.getName()); - if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) { + if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT)) { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); } else { mBinding = true; @@ -1153,7 +1155,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_BLUETOOTH_LE)) { Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT); } } else { //If Bluetooth is off, send service down event to proxy objects, and unbind -- GitLab From 68e110a6afc3a6e16fbeb436436d59dfcfd6d67a Mon Sep 17 00:00:00 2001 From: Eric Rowe Date: Wed, 15 Oct 2014 11:24:09 -0700 Subject: [PATCH 0412/1408] Add CHANGE_NETWORK_STATE permission to bt tests Change-Id: Ie0a579de3b7d1163a8fe245eb377f21eeed048a3 --- framework/tests/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index cbfa84d0ef8..e43ba12bcaa 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -20,6 +20,7 @@ + -- GitLab From 1afd3f4e1a3abdceeb9faa38f8445063612897ff Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 15 Oct 2014 16:36:01 -0700 Subject: [PATCH 0413/1408] Update javadoc comments for getBluetoothLeAdvertiser() To clarify that BluetoothLeAdvertiser object will return null when BT is off OR if the hw doesn't support these capabilities bug: 18006072 Change-Id: I635d7971711a3cae7c58f7a0636faf9a03f19970 --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f0b609aec1e..5564af789e1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -454,8 +454,9 @@ public final class BluetoothAdapter { } /** - * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations, or - * null if Bluetooth LE Advertising is not support on this device. + * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. + * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not + * supported on this device. *

        * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported * on this device before calling this method. -- GitLab From d38150d305d18ba152b134e68363ded08674428b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 16 Oct 2014 19:39:52 -0700 Subject: [PATCH 0414/1408] Remove spammy log information for BLE scan. Bug:17574681 Change-Id: Ic5761ad7262200f38f2b67659c37f8a3f01d80d5 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index a57c3ca25a9..93ea299bc8d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -51,6 +51,7 @@ public final class BluetoothLeScanner { private static final String TAG = "BluetoothLeScanner"; private static final boolean DBG = true; + private static final boolean VDBG = false; private final IBluetoothManager mBluetoothManager; private final Handler mHandler; @@ -317,7 +318,7 @@ public final class BluetoothLeScanner { */ @Override public void onScanResult(final ScanResult scanResult) { - if (DBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); + if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { @@ -346,7 +347,7 @@ public final class BluetoothLeScanner { @Override public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { - if (DBG) { + if (VDBG) { Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); } -- GitLab From c0b8ac0a09131e6ca6ad0efbe51e28dcfc0c8098 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 28 Oct 2014 14:06:28 -0700 Subject: [PATCH 0415/1408] Add dumpsys support for bluetooth Bug: 18159457 Change-Id: I8d3f7084a7b089c111fd622ff526630bfdfa7300 --- .../java/android/bluetooth/IBluetooth.aidl | 3 +++ .../bluetooth/BluetoothManagerService.java | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 992f601a20d..cd4535a57f9 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -98,4 +98,7 @@ interface IBluetooth boolean isActivityAndEnergyReportingSupported(); void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); + + // for dumpsys support + String dump(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 636228baaee..ebdd386d1e6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -44,6 +44,10 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -1282,4 +1286,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // todo: notify user to power down and power up phone to make bluetooth work. } } + + @Override + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + writer.println("enabled: " + mEnable); + writer.println("state: " + mState); + writer.println("address: " + mAddress); + writer.println("name: " + mName); + if (mBluetooth == null) { + writer.println("Bluetooth Service not connected"); + } else { + try { + writer.println(mBluetooth.dump()); + } catch (RemoteException re) { + writer.println("RemoteException while calling Bluetooth Service"); + } + } + } } -- GitLab From 716c1fbbe13bb8188c8de7dd9e6da0c86eb389b9 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 29 Oct 2014 12:13:38 -0700 Subject: [PATCH 0416/1408] Fix crash during Bluetooth on/off stress test Bug: 18106938 Change-Id: Icdeb736d89d5926264f2043455ccbc3760fd5d29 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f2e03cf8928..c262baed4f2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1445,7 +1445,7 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; - mLeScanClients.clear(); + if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ -- GitLab From 19d436d6085eddfd1bd031e8991d8dacf58942e3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 22 Aug 2014 10:33:21 -0700 Subject: [PATCH 0417/1408] Add BluetoothDevice field to BluetoothHeadsetClientCall Change-Id: I9bc70eb289fdfb64e315e3508ddeef1cb009cc54 --- .../bluetooth/BluetoothHeadsetClientCall.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index a15bd975ad8..7b5a045e27f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -61,6 +61,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { */ public static final int CALL_STATE_TERMINATED = 7; + private final BluetoothDevice mDevice; private final int mId; private int mState; private String mNumber; @@ -70,8 +71,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { /** * Creates BluetoothHeadsetClientCall instance. */ - public BluetoothHeadsetClientCall(int id, int state, String number, boolean multiParty, - boolean outgoing) { + public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, + boolean multiParty, boolean outgoing) { + mDevice = device; mId = id; mState = state; mNumber = number != null ? number : ""; @@ -113,6 +115,15 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mMultiParty = multiParty; } + /** + * Gets call's device. + * + * @return call device. + */ + public BluetoothDevice getDevice() { + return mDevice; + } + /** * Gets call's Id. * @@ -161,7 +172,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } public String toString() { - StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mId: "); + StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); + builder.append(mDevice); + builder.append(", mId: "); builder.append(mId); builder.append(", mState: "); switch (mState) { @@ -192,8 +205,9 @@ public final class BluetoothHeadsetClientCall implements Parcelable { new Parcelable.Creator() { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { - return new BluetoothHeadsetClientCall(in.readInt(), in.readInt(), - in.readString(), in.readInt() == 1, in.readInt() == 1); + return new BluetoothHeadsetClientCall((BluetoothDevice)in.readParcelable(null), + in.readInt(), in.readInt(), in.readString(), + in.readInt() == 1, in.readInt() == 1); } @Override @@ -204,6 +218,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mDevice, 0); out.writeInt(mId); out.writeInt(mState); out.writeString(mNumber); -- GitLab From 3f294121ea0cbc9951ceda2c6275f08a4d3d21bd Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 29 Oct 2014 12:13:38 -0700 Subject: [PATCH 0418/1408] Fix crash during Bluetooth on/off stress test Bug: 18106938 Change-Id: Icdeb736d89d5926264f2043455ccbc3760fd5d29 (cherry picked from commit 716c1fbbe13bb8188c8de7dd9e6da0c86eb389b9) --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f2e03cf8928..c262baed4f2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1445,7 +1445,7 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; - mLeScanClients.clear(); + if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ -- GitLab From 4714f3a3b3b783993e931b871795e61aec84add0 Mon Sep 17 00:00:00 2001 From: Mathias Jeppsson Date: Tue, 6 Aug 2013 11:41:55 +0200 Subject: [PATCH 0419/1408] Dont call close in in BluetoothA2dp finalize The close in finalize() is pointless, as finalize() will only be called if there are no references to BluetoothA2dp. Until close() is called, BluetoothManagerService will have a reference to BluetoothA2dp, preventing garbage collection and finalize() to be called. This means finalize() is not serving its purpose of cleaning up in cases close() is not called, as finalize() is only called if close() has already been called. Actually calling close in finalize here means unregistering the already unregistered mBluetoothStateChangeCallback which can lead to crashes when pairing/unpairing BTH. A typical crash would look like: *** FATAL EXCEPTION IN SYSTEM PROCESS: BluetoothManager java.lang.NullPointerException at android.os.RemoteCallbackList.unregister(RemoteCallbackList.java:143) at com.android.server.BluetoothManagerService$BluetoothHandler.handleMessage(BluetoothManagerService.java:780) at android.os.Handler.dispatchMessage(Handler.java:99) Change-Id: Ib65962917ecbacf7900d7fe540057e6915f0532d --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 51754901163..0450150f871 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -199,7 +199,8 @@ public final class BluetoothA2dp implements BluetoothProfile { } public void finalize() { - close(); + // The empty finalize needs to be kept or the + // cts signature tests would fail. } /** * Initiate connection to a profile of the remote bluetooth device. -- GitLab From 0f5abcc53cc66624550ba29c2bb5f9d1751a1bcc Mon Sep 17 00:00:00 2001 From: Tom Turney Date: Wed, 12 Nov 2014 16:26:41 -0800 Subject: [PATCH 0420/1408] Feature request: Increasing advertise data size on non-connectable adv. When the advertisement is non-connectable, give back the bytes to the advertiser where the adv flags would have been. This increases the non-connectable advertisement's advertise data from 24 to 27 bytes. Bug:18359570 Change-Id: Ia3cc48dca50cc3c51095ee92a489f143f6d350b1 --- .../android/bluetooth/le/BluetoothLeAdvertiser.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index a019d5cff2a..e76c23b97a7 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -117,8 +117,9 @@ public final class BluetoothLeAdvertiser { AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); return; } - if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) { + boolean isConnectable = settings.isConnectable(); + if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || + totalBytes(scanResponse, isConnectable) > MAX_ADVERTISING_DATA_BYTES) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } @@ -171,9 +172,10 @@ public final class BluetoothLeAdvertiser { } // Compute the size of the advertise data. - private int totalBytes(AdvertiseData data) { + private int totalBytes(AdvertiseData data, boolean isConnectable) { if (data == null) return 0; - int size = FLAGS_FIELD_BYTES; // flags field is always set. + // Flags field is omitted if the advertising is not connectable. + int size = isConnectable ? FLAGS_FIELD_BYTES : 0; if (data.getServiceUuids() != null) { int num16BitUuids = 0; int num32BitUuids = 0; -- GitLab From 947b392d253c7e6bd1408e1c8d585b2efa7ed1c7 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Thu, 20 Nov 2014 21:53:26 -0800 Subject: [PATCH 0421/1408] Add UUID for Pbap client Bug: 18140961 Change-Id: I9cbeb7129b1d30aa7a195ed83821d4a5634283ed --- framework/java/android/bluetooth/BluetoothUuid.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 1e22eb39289..194a53e0f6c 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -66,6 +66,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid PBAP_PCE = + ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAP = -- GitLab From 5682be9ee6f7008af7d4ac0deca2e092d6d76265 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 18 Nov 2014 11:34:26 -0800 Subject: [PATCH 0422/1408] Add server-side callback for change in MTU (4/4) When a client requests to update the LE transport MTU, the server currently does not get notified and can therefor not properly size notifications appropriate to the current MTU. Bug: 18388114 Change-Id: I515bfc2cc9846490d57de71860f41ea9a61fa243 --- .../android/bluetooth/BluetoothGattServer.java | 18 ++++++++++++++++++ .../bluetooth/BluetoothGattServerCallback.java | 12 ++++++++++++ .../IBluetoothGattServerCallback.aidl | 1 + 3 files changed, 31 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index e94a8cea216..f4513405b19 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -284,6 +284,24 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception: " + ex); } } + + /** + * The MTU for a connection has changed + * @hide + */ + public void onMtuChanged(String address, int mtu) { + if (DBG) Log.d(TAG, "onMtuChanged() - " + + "device=" + address + ", mtu=" + mtu); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onMtuChanged(device, mtu); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } }; /** diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 1dd06f2ce1c..2afcf9a322b 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -145,4 +145,16 @@ public abstract class BluetoothGattServerCallback { */ public void onNotificationSent(BluetoothDevice device, int status) { } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + *

        This callback will be invoked if a remote client has requested to change + * the MTU for a given connection. + * + * @param device The remote device that requested the MTU change + * @param mtu The new MTU size + */ + public void onMtuChanged(BluetoothDevice device, int mtu) { + } } diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index 5d4d6c63efd..8b202b23cc6 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -59,4 +59,5 @@ oneway interface IBluetoothGattServerCallback { in byte[] value); void onExecuteWrite(in String address, in int transId, in boolean execWrite); void onNotificationSent(in String address, in int status); + void onMtuChanged(in String address, in int mtu); } -- GitLab From 3362fef6d2c0d5930592d83164d2009e7ccec43f Mon Sep 17 00:00:00 2001 From: Benjamin Franz Date: Wed, 12 Nov 2014 15:57:54 +0000 Subject: [PATCH 0423/1408] Change the routing path of bluetooth headset connections. The HeadsetService is now bound directly by the BluetoothManagerService. The IBinder object related to the HeadsetService is then given back to the BluetoothHeadset and to the client app. This change makes the HeadsetService available for managed profile clients. Bug: 16968338 Change-Id: I016d1837e4f987c0fab1fc2c64cb06eb91b24d87 --- .../android/bluetooth/BluetoothHeadset.java | 99 +++++--- .../android/bluetooth/IBluetoothManager.aidl | 4 + .../IBluetoothProfileServiceConnection.aidl | 30 +++ .../bluetooth/BluetoothManagerService.java | 229 ++++++++++++++++++ 4 files changed, 324 insertions(+), 38 deletions(-) create mode 100755 framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 353f0fba05d..546a50e2ef4 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -20,9 +20,10 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.util.Log; @@ -221,11 +222,14 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final int STATE_AUDIO_CONNECTED = 12; + private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; + private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; private Context mContext; private ServiceListener mServiceListener; private IBluetoothHeadset mService; private BluetoothAdapter mAdapter; + private boolean mIsClosed; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -233,14 +237,7 @@ public final class BluetoothHeadset implements BluetoothProfile { 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); - } - } + doUnbind(); } else { synchronized (mConnection) { try { @@ -263,6 +260,7 @@ public final class BluetoothHeadset implements BluetoothProfile { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); + mIsClosed = false; IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -277,15 +275,26 @@ public final class BluetoothHeadset implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothHeadset.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 Headset Service with " + intent); - return false; + try { + return mAdapter.getBluetoothManager().bindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind HeadsetService", e); + } + return false; + } + + void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + try { + mAdapter.getBluetoothManager().unbindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG,"Unable to unbind HeadsetService", e); + } + } } - return true; } /** @@ -305,18 +314,8 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.e(TAG,"",e); } } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - mServiceListener = null; + mIsClosed = true; + doUnbind(); } /** @@ -930,21 +929,21 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { + private final IBluetoothProfileServiceConnection mConnection + = new IBluetoothProfileServiceConnection.Stub() { + @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this); - } + mHandler.sendMessage(mHandler.obtainMessage( + MESSAGE_HEADSET_SERVICE_CONNECTED)); } + @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); - } + mHandler.sendMessage(mHandler.obtainMessage( + MESSAGE_HEADSET_SERVICE_DISCONNECTED)); } }; @@ -968,4 +967,28 @@ public final class BluetoothHeadset implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_HEADSET_SERVICE_CONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, + BluetoothHeadset.this); + } + break; + } + case MESSAGE_HEADSET_SERVICE_DISCONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); + } + if (mIsClosed){ + mServiceListener = null; + } + break; + } + } + } + }; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 493d2f8e774..7411d3f27b5 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -19,6 +19,7 @@ package android.bluetooth; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManagerCallback; +import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; /** @@ -38,6 +39,9 @@ interface IBluetoothManager boolean disable(boolean persist); IBluetoothGatt getBluetoothGatt(); + boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); + void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); + String getAddress(); String getName(); } diff --git a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl new file mode 100755 index 00000000000..96c59e23915 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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.content.ComponentName; +import android.os.IBinder; + +/** + * Callback for bluetooth profile connections. + * + * {@hide} + */ +interface IBluetoothProfileServiceConnection { + void onServiceConnected(in ComponentName comp, in IBinder service); + void onServiceDisconnected(in ComponentName comp); +} diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ebdd386d1e6..3117a17175b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -18,11 +18,14 @@ package com.android.server; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothHeadset; import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; +import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -32,6 +35,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -42,12 +46,18 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Log; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Vector; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -67,6 +77,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ERROR_RESTART_TIME_MS = 3000; //Maximum msec to delay MESSAGE_USER_SWITCHED private static final int USER_SWITCHED_TIME_MS = 200; + // Delay for the addProxy function in msec + private static final int ADD_PROXY_DELAY_MS = 100; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -83,6 +95,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; private static final int MESSAGE_USER_SWITCHED = 300; + private static final int MESSAGE_ADD_PROXY_DELAYED = 400; + private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; private static final int MAX_SAVE_RETRIES=3; private static final int MAX_ERROR_RESTART_RETRIES=6; @@ -127,6 +141,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private int mErrorRecoveryRetryCounter; private final int mSystemUiUid; + // Save a ProfileServiceConnections object for each of the bound + // bluetooth profile services + private final Map mProfileServices = + new HashMap (); + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -499,6 +518,187 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mBluetoothGatt; } + @Override + public boolean bindBluetoothProfileService(int bluetoothProfile, + IBluetoothProfileServiceConnection proxy) { + if (!mEnable) { + if (DBG) { + Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile + + ", while Bluetooth was disabled"); + } + return false; + } + synchronized (mProfileServices) { + ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + if (psc == null) { + if (DBG) { + Log.d(TAG, "Creating new ProfileServiceConnections object for" + + " profile: " + bluetoothProfile); + } + Intent intent = null; + if (bluetoothProfile == BluetoothProfile.HEADSET) { + intent = new Intent(IBluetoothHeadset.class.getName()); + } else { + return false; + } + psc = new ProfileServiceConnections(intent); + mProfileServices.put(new Integer(bluetoothProfile), psc); + psc.bindService(); + } + } + + // Introducing a delay to give the client app time to prepare + Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED); + addProxyMsg.arg1 = bluetoothProfile; + addProxyMsg.obj = proxy; + mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS); + return true; + } + + @Override + public void unbindBluetoothProfileService(int bluetoothProfile, + IBluetoothProfileServiceConnection proxy) { + synchronized (mProfileServices) { + ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + if (psc == null) { + return; + } + psc.removeProxy(proxy); + } + } + + private void unbindAllBluetoothProfileServices() { + synchronized (mProfileServices) { + for (Integer i : mProfileServices.keySet()) { + ProfileServiceConnections psc = mProfileServices.get(i); + mContext.unbindService(psc); + psc.removeAllProxies(); + } + mProfileServices.clear(); + } + } + + /** + * This class manages the clients connected to a given ProfileService + * and maintains the connection with that service. + */ + final private class ProfileServiceConnections implements ServiceConnection, + IBinder.DeathRecipient { + final RemoteCallbackList mProxies = + new RemoteCallbackList (); + IBinder mService; + ComponentName mClassName; + Intent mIntent; + + ProfileServiceConnections(Intent intent) { + mService = null; + mClassName = null; + mIntent = intent; + } + + private void bindService() { + if (mIntent != null && mService == null) { + if (!doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { + Log.w(TAG, "Unable to bind with intent: " + mIntent + + ". Triggering retry."); + } + Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); + msg.obj = this; + mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); + } + } + + private void addProxy(IBluetoothProfileServiceConnection proxy) { + mProxies.register(proxy); + if (mService != null) { + try{ + proxy.onServiceConnected(mClassName, mService); + } catch (RemoteException e) { + Log.e(TAG, "Unable to connect to proxy", e); + } + } else { + if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) { + Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); + msg.obj = this; + mHandler.sendMessage(msg); + } + } + } + + private void removeProxy(IBluetoothProfileServiceConnection proxy) { + if (proxy != null) { + if (mProxies.unregister(proxy)) { + try { + proxy.onServiceDisconnected(mClassName); + } catch (RemoteException e) { + Log.e(TAG, "Unable to disconnect proxy", e); + } + } + } else { + Log.w(TAG, "Trying to remove a null proxy"); + } + } + + private void removeAllProxies() { + onServiceDisconnected(mClassName); + mProxies.kill(); + } + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + // remove timeout message + mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this); + mService = service; + mClassName = className; + try { + mService.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "Unable to linkToDeath", e); + } + int n = mProxies.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mProxies.getBroadcastItem(i).onServiceConnected(className, service); + } catch (RemoteException e) { + Log.e(TAG, "Unable to connect to proxy", e); + } + } + mProxies.finishBroadcast(); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + if (mService == null) { + return; + } + mService.unlinkToDeath(this, 0); + mService = null; + mClassName = null; + int n = mProxies.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mProxies.getBroadcastItem(i).onServiceDisconnected(className); + } catch (RemoteException e) { + Log.e(TAG, "Unable to disconnect from proxy", e); + } + } + mProxies.finishBroadcast(); + } + + @Override + public void binderDied() { + if (DBG) { + Log.w(TAG, "Profile service for profile: " + mClassName + + " died."); + } + onServiceDisconnected(mClassName); + // Trigger rebind + Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); + msg.obj = this; + mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); + } + } + private void sendBluetoothStateCallback(boolean isUp) { int n = mStateChangeCallbacks.beginBroadcast(); if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); @@ -803,6 +1003,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; } + case MESSAGE_ADD_PROXY_DELAYED: + { + ProfileServiceConnections psc = mProfileServices.get( + new Integer(msg.arg1)); + if (psc == null) { + break; + } + IBluetoothProfileServiceConnection proxy = + (IBluetoothProfileServiceConnection) msg.obj; + psc.addProxy(proxy); + break; + } + case MESSAGE_BIND_PROFILE_SERVICE: + { + ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj; + removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj); + if (psc == null) { + break; + } + psc.bindService(); + break; + } case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: { if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); @@ -1005,6 +1227,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); } + unbindAllBluetoothProfileServices(); // disable handleDisable(); // Pbap service need receive STATE_TURNING_OFF intent to close @@ -1129,16 +1352,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int callingUser = UserHandle.getCallingUserId(); int callingUid = Binder.getCallingUid(); long callingIdentity = Binder.clearCallingIdentity(); + UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + UserInfo ui = um.getProfileParent(callingUser); + int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL; int callingAppId = UserHandle.getAppId(callingUid); boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); valid = (callingUser == foregroundUser) || + parentUser == foregroundUser || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; if (DBG) { Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser + + " parentUser=" + parentUser + " foregroundUser=" + foregroundUser); } } finally { @@ -1165,6 +1393,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { //If Bluetooth is off, send service down event to proxy objects, and unbind if (!isUp && canUnbindBluetoothService()) { + unbindAllBluetoothProfileServices(); sendBluetoothServiceDownCallback(); unbindAndFinish(); } -- GitLab From c1746009797770a398258c2421c6a8a3dff2d6b6 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 4 Dec 2014 10:12:55 -0800 Subject: [PATCH 0424/1408] Fix BT crash due to unset value for BluetoothGattDescriptor from API user Validate writeDescriptor and writeCharacteristic methods at API invocation for non null initialisation. Bug 18395071 Change-Id: I411a57b77981310d8db1f98c67e03b4327c93339 --- framework/java/android/bluetooth/BluetoothGatt.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index c203a8e69e7..ea2dca08fd4 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -928,7 +928,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); - if (mService == null || mClientIf == 0) return false; + if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false; BluetoothGattService service = characteristic.getService(); if (service == null) return false; @@ -1015,7 +1015,7 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0) return false; + if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false; BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); if (characteristic == null) return false; -- GitLab From a86bfff223e40f2759ed6cd91ebb7d509ef2fdfa Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 9 Dec 2014 16:42:14 -0800 Subject: [PATCH 0425/1408] Prevent NullPointerException in getStringValue if no value is set Bug: 18686365 Change-Id: I5f5d07ef49500e0d8118ba04aaf373ae4514f034 --- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index a86677cfaab..7cdcc2c9b9f 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -502,7 +502,7 @@ public class BluetoothGattCharacteristic { * @return Cached value of the characteristic */ public String getStringValue(int offset) { - if (offset > mValue.length) return null; + if (mValue == null || offset > mValue.length) return null; byte[] strBytes = new byte[mValue.length - offset]; for (int i=0; i != (mValue.length-offset); ++i) strBytes[i] = mValue[offset+i]; return new String(strBytes); -- GitLab From 13fa7292d58381a19e7262a2e2fa9d11c5896c75 Mon Sep 17 00:00:00 2001 From: Benjamin Franz Date: Tue, 9 Dec 2014 18:58:45 +0000 Subject: [PATCH 0426/1408] Only add successfully bound profile services to mProfileServices. This causes crashes on all devices that disable the BluetoothHeadset service when disabling bluetooth and thereby trying to unbind all services. Bug: 18669882 Change-Id: I48978a1556ead967c848c5bd9f6e001d38754b8d --- .../bluetooth/BluetoothManagerService.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 3117a17175b..ca376fdfe9e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -535,15 +535,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Log.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: " + bluetoothProfile); } - Intent intent = null; - if (bluetoothProfile == BluetoothProfile.HEADSET) { - intent = new Intent(IBluetoothHeadset.class.getName()); - } else { - return false; - } + + if (bluetoothProfile != BluetoothProfile.HEADSET) return false; + + Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); + if (!psc.bindService()) return false; + mProfileServices.put(new Integer(bluetoothProfile), psc); - psc.bindService(); } } @@ -571,7 +570,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized (mProfileServices) { for (Integer i : mProfileServices.keySet()) { ProfileServiceConnections psc = mProfileServices.get(i); - mContext.unbindService(psc); + try { + mContext.unbindService(psc); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + } psc.removeAllProxies(); } mProfileServices.clear(); @@ -596,16 +599,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mIntent = intent; } - private void bindService() { - if (mIntent != null && mService == null) { - if (!doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { - Log.w(TAG, "Unable to bind with intent: " + mIntent - + ". Triggering retry."); - } + private boolean bindService() { + if (mIntent != null && mService == null && + doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); msg.obj = this; mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); + return true; } + Log.w(TAG, "Unable to bind with intent: " + mIntent); + return false; } private void addProxy(IBluetoothProfileServiceConnection proxy) { -- GitLab From 54e0c99fe8d97e521feba2641b4fb6b6c43485c9 Mon Sep 17 00:00:00 2001 From: Qiwen Zhao Date: Wed, 10 Dec 2014 14:10:18 -0800 Subject: [PATCH 0427/1408] Initial empty repository -- GitLab From 733656c676b7d4a6f552aab6f3a44e66df11898d Mon Sep 17 00:00:00 2001 From: Benjamin Franz Date: Tue, 16 Dec 2014 15:33:03 +0000 Subject: [PATCH 0428/1408] Don't send the onServiceDisconnected callback after close. Bug: 18667402 Change-Id: Ia029fd861d643f6646fb124d76ca2a1d2a1a695a --- framework/java/android/bluetooth/BluetoothHeadset.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 546a50e2ef4..25d9aa9bae6 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -229,7 +229,6 @@ public final class BluetoothHeadset implements BluetoothProfile { private ServiceListener mServiceListener; private IBluetoothHeadset mService; private BluetoothAdapter mAdapter; - private boolean mIsClosed; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -260,7 +259,6 @@ public final class BluetoothHeadset implements BluetoothProfile { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - mIsClosed = false; IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -314,7 +312,7 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.e(TAG,"",e); } } - mIsClosed = true; + mServiceListener = null; doUnbind(); } @@ -983,9 +981,6 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); } - if (mIsClosed){ - mServiceListener = null; - } break; } } -- GitLab From 0190837e14c10d1d79293c3ed1205d70a01cfc4b Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 18 Dec 2014 14:16:36 -0800 Subject: [PATCH 0429/1408] BluetoothManagerService: Enforce DUMP permission Bug: 18667272 Change-Id: If9a510aa7b5f22b9df1d8b33f0a18183040a8cf6 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ca376fdfe9e..32a6a2faad7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1521,6 +1521,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + writer.println("enabled: " + mEnable); writer.println("state: " + mState); writer.println("address: " + mAddress); -- GitLab From e179b5686b48ae4e15ac26545ad40aab0c844f9d Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Fri, 16 Jan 2015 10:40:11 -0800 Subject: [PATCH 0430/1408] Don't compute flags length for scan response Bug 19041626 Change-Id: I7d8aa12e133477fc60d12b474f57313961021975 --- .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index e76c23b97a7..67d9de59409 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -119,7 +119,7 @@ public final class BluetoothLeAdvertiser { } boolean isConnectable = settings.isConnectable(); if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse, isConnectable) > MAX_ADVERTISING_DATA_BYTES) { + totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } @@ -171,11 +171,11 @@ public final class BluetoothLeAdvertiser { mLeAdvertisers.clear(); } - // Compute the size of the advertise data. - private int totalBytes(AdvertiseData data, boolean isConnectable) { + // Compute the size of advertisement data or scan resp + private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { if (data == null) return 0; // Flags field is omitted if the advertising is not connectable. - int size = isConnectable ? FLAGS_FIELD_BYTES : 0; + int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0; if (data.getServiceUuids() != null) { int num16BitUuids = 0; int num32BitUuids = 0; -- GitLab From a793dbfb9d9238b6b0e2fc02cf6dd8e0829928df Mon Sep 17 00:00:00 2001 From: RoboErik Date: Wed, 21 Jan 2015 15:45:32 -0800 Subject: [PATCH 0431/1408] Move mute/unmute handling to adjust volume paths This deprecates the setStreamMute and setStreamSolo APIs. Soloing is no longer supported and muting is redirected through the adjust volume APIs. Also updates the hidden master versions of these APIs. Change-Id: I65d2a5d5fc15b386bd497abf8ca6869fec75a26a --- framework/java/android/bluetooth/BluetoothA2dp.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 0450150f871..767f59eb129 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.media.AudioManager; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -415,9 +416,16 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * Tells remote device to adjust volume. Only if absolute volume is supported. + * Tells remote device to adjust volume. Only if absolute volume is + * supported. Uses the following values: + *

          + *
        • {@link AudioManager#ADJUST_LOWER}
        • + *
        • {@link AudioManager#ADJUST_RAISE}
        • + *
        • {@link AudioManager#ADJUST_MUTE}
        • + *
        • {@link AudioManager#ADJUST_UNMUTE}
        • + *
        * - * @param direction 1 to increase volume, or -1 to decrease volume + * @param direction One of the supported adjust values. * @hide */ public void adjustAvrcpAbsoluteVolume(int direction) { -- GitLab From b6600692fed1b9b6275443d8fdd1160fbe155d09 Mon Sep 17 00:00:00 2001 From: Matthew Xie Date: Fri, 6 Feb 2015 14:09:54 -0800 Subject: [PATCH 0432/1408] Separate the protection of mProxyServiceStateCallbacks from that of mService The overuse of mManagerCallback caused a deaklock. Bug: 19264190 Change-Id: Iff20019ff0c99bb5f36435feb15e43e280a8e102 --- .../android/bluetooth/BluetoothAdapter.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c262baed4f2..b8f4bf8f044 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1429,14 +1429,16 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); synchronized (mManagerCallback) { mService = bluetoothService; - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceUp(bluetoothService); - } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ + try { + if (cb != null) { + cb.onBluetoothServiceUp(bluetoothService); + } else { + Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + } + } catch (Exception e) { Log.e(TAG,"",e);} + } } } } @@ -1448,14 +1450,16 @@ public final class BluetoothAdapter { if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + } + } catch (Exception e) { Log.e(TAG,"",e);} + } } } } @@ -1596,10 +1600,10 @@ public final class BluetoothAdapter { return mManagerService; } - private ArrayList mProxyServiceStateCallbacks = new ArrayList(); + final private ArrayList mProxyServiceStateCallbacks = new ArrayList(); /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mManagerCallback) { + synchronized (mProxyServiceStateCallbacks) { if (cb == null) { Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); } else if (!mProxyServiceStateCallbacks.contains(cb)) { @@ -1610,7 +1614,7 @@ public final class BluetoothAdapter { } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mManagerCallback) { + synchronized (mProxyServiceStateCallbacks) { mProxyServiceStateCallbacks.remove(cb); } } -- GitLab From 4c5e1298b2670ff3973827792ebc34c70abf4437 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Thu, 5 Feb 2015 20:06:33 -0800 Subject: [PATCH 0433/1408] Add API to check if a Bluetooth connection is encrypted (1/2) Bug: 19186961 Change-Id: I24656a07ee23ebfe067a9dfb9c1bc4041c782d8c --- .../android/bluetooth/BluetoothDevice.java | 34 ++++++++++++++++++- .../java/android/bluetooth/IBluetooth.aidl | 2 +- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5e50b696b22..bb0d0a39f56 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; @@ -66,6 +67,14 @@ public final class BluetoothDevice implements Parcelable { private static final String TAG = "BluetoothDevice"; private static final boolean DBG = false; + /** + * Connection state bitmask as returned by getConnectionState. + */ + private static final int CONNECTION_STATE_DISCONNECTED = 0; + private static final int CONNECTION_STATE_CONNECTED = 1; + private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2; + private static final int CONNECTION_STATE_ENCRYPTED_LE = 4; + /** * Sentinel error value for this class. Guaranteed to not equal any other * integer constant in this class. Provided as a convenience for functions @@ -940,13 +949,36 @@ public final class BluetoothDevice implements Parcelable { * @return True if there is at least one open connection to this device. * @hide */ + @SystemApi public boolean isConnected() { if (sService == null) { // BT is not enabled, we cannot be connected. return false; } try { - return sService.isConnected(this); + return sService.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Returns whether there is an open connection to this device + * that has been encrypted. + *

        Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * @return True if there is at least one encrypted connection to this device. + * @hide + */ + @SystemApi + public boolean isEncrypted() { + if (sService == null) { + // BT is not enabled, we cannot be connected. + return false; + } + try { + return sService.getConnectionState(this) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index cd4535a57f9..dabb1ceaea8 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -59,7 +59,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); + int getConnectionState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); int getRemoteType(in BluetoothDevice device); -- GitLab From a758133e0b011657977776b1c649915799112a45 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Sat, 28 Feb 2015 13:12:17 -0500 Subject: [PATCH 0434/1408] Remove unused imports in frameworks/base. Change-Id: I031443de83f93eb57a98863001826671b18f3b17 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 --- framework/java/android/bluetooth/le/TruncatedFilter.java | 3 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 -- 3 files changed, 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b8f4bf8f044..be26eac31da 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -26,9 +26,7 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; @@ -36,7 +34,6 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java index 6a6b3e3c323..685b1748f9a 100644 --- a/framework/java/android/bluetooth/le/TruncatedFilter.java +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -17,9 +17,6 @@ package android.bluetooth.le; import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - import java.util.List; /** diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 32a6a2faad7..ef51ad62c11 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -55,8 +55,6 @@ import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; -import java.util.List; -import java.util.Vector; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; -- GitLab From 0bb7222b2b5c192004da0faedddb2b52bc0a5470 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 5 Dec 2014 09:31:30 -0800 Subject: [PATCH 0435/1408] DO NOT MERGE ANYWHERE Bluetooth native dumpsys logging support (3/4) Bug: 18508263 Change-Id: I88f9c90dab8b0c825010c8617709449a3dd704b2 --- .../java/android/bluetooth/IBluetooth.aidl | 4 ++-- .../bluetooth/BluetoothManagerService.java | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index dabb1ceaea8..c6f238ee6a6 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -99,6 +99,6 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // for dumpsys support - String dump(); + // For dumpsys support + void dump(in ParcelFileDescriptor fd); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 32a6a2faad7..0c726f313d9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -51,6 +52,7 @@ import android.provider.Settings; import android.util.Log; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; @@ -1523,17 +1525,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - writer.println("enabled: " + mEnable); - writer.println("state: " + mState); - writer.println("address: " + mAddress); - writer.println("name: " + mName); + writer.println("Bluetooth Status"); + writer.println(" enabled: " + mEnable); + writer.println(" state: " + mState); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName + "\n"); + writer.flush(); + if (mBluetooth == null) { writer.println("Bluetooth Service not connected"); } else { try { - writer.println(mBluetooth.dump()); + ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd); + mBluetooth.dump(pfd); + pfd.close(); } catch (RemoteException re) { writer.println("RemoteException while calling Bluetooth Service"); + } catch (IOException re) { + writer.println("IOException attempting to dup() fd"); } } } -- GitLab From 80126abad54ec73e7245c24cc9693b1434929d8a Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 9 Mar 2015 15:23:42 -0700 Subject: [PATCH 0436/1408] Change WiFi and Bluetooth ActivityEnergyInfo classes Have them take an elapsed time millis timestamp instead of having the constructor call System.currentTimeMillis. Change-Id: Ic9ca8f92347c336beee8ebcc3407de2c1e5b4073 --- .../BluetoothActivityEnergyInfo.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index ce87329ee43..161c339e78a 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -26,32 +26,32 @@ import android.os.Parcelable; * @hide */ public final class BluetoothActivityEnergyInfo implements Parcelable { + private final long mTimestamp; private final int mBluetoothStackState; private final int mControllerTxTimeMs; private final int mControllerRxTimeMs; private final int mControllerIdleTimeMs; private final int mControllerEnergyUsed; - private final long timestamp; public static final int BT_STACK_STATE_INVALID = 0; public static final int BT_STACK_STATE_STATE_ACTIVE = 1; public static final int BT_STACK_STATE_STATE_SCANNING = 2; public static final int BT_STACK_STATE_STATE_IDLE = 3; - public BluetoothActivityEnergyInfo(int stackState, int txTime, int rxTime, - int idleTime, int energyUsed) { + public BluetoothActivityEnergyInfo(long timestamp, int stackState, + int txTime, int rxTime, int idleTime, int energyUsed) { + mTimestamp = timestamp; mBluetoothStackState = stackState; mControllerTxTimeMs = txTime; mControllerRxTimeMs = rxTime; mControllerIdleTimeMs = idleTime; mControllerEnergyUsed = energyUsed; - timestamp = System.currentTimeMillis(); } @Override public String toString() { return "BluetoothActivityEnergyInfo{" - + " timestamp=" + timestamp + + " mTimestamp=" + mTimestamp + " mBluetoothStackState=" + mBluetoothStackState + " mControllerTxTimeMs=" + mControllerTxTimeMs + " mControllerRxTimeMs=" + mControllerRxTimeMs @@ -63,13 +63,14 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { + long timestamp = in.readLong(); int stackState = in.readInt(); int txTime = in.readInt(); int rxTime = in.readInt(); int idleTime = in.readInt(); int energyUsed = in.readInt(); - return new BluetoothActivityEnergyInfo(stackState, txTime, rxTime, - idleTime, energyUsed); + return new BluetoothActivityEnergyInfo(timestamp, stackState, + txTime, rxTime, idleTime, energyUsed); } public BluetoothActivityEnergyInfo[] newArray(int size) { return new BluetoothActivityEnergyInfo[size]; @@ -77,6 +78,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { }; public void writeToParcel(Parcel out, int flags) { + out.writeLong(mTimestamp); out.writeInt(mBluetoothStackState); out.writeInt(mControllerTxTimeMs); out.writeInt(mControllerRxTimeMs); @@ -123,11 +125,12 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public int getControllerEnergyUsed() { return mControllerEnergyUsed; } + /** - * @return timestamp(wall clock) of record creation + * @return timestamp(real time elapsed in milliseconds since boot) of record creation. */ public long getTimeStamp() { - return timestamp; + return mTimestamp; } /** -- GitLab From b071399ec49c58aa8d76862f93e544c1790053ec Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 12 Jan 2015 20:02:37 -0800 Subject: [PATCH 0437/1408] Add opportunistic ble scan mode. (1/2) Bug: 19003667 Change-Id: Ibed7e9ec604cb11a58736d168d3d19ece53fc77a (cherry picked from commit 7508ddf8b53bf350a3424ef71f1ce62ea17006b8) --- framework/java/android/bluetooth/le/ScanSettings.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 7eae4398e59..5bdfdb544c4 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -25,6 +25,15 @@ import android.os.Parcelable; * parameters for the scan. */ public final class ScanSettings implements Parcelable { + + /** + * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for + * other scan results without starting BLE scans themselves. + * + * @hide + */ + public static final int SCAN_MODE_OPPORTUNISTIC = -1; + /** * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the * least power. @@ -177,7 +186,7 @@ public final class ScanSettings implements Parcelable { * @throws IllegalArgumentException If the {@code scanMode} is invalid. */ public Builder setScanMode(int scanMode) { - if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) { + if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) { throw new IllegalArgumentException("invalid scan mode " + scanMode); } mScanMode = scanMode; -- GitLab From 55d484ff361501e3903dbbe72be7f322a7325165 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 18 Mar 2015 16:38:08 -0700 Subject: [PATCH 0438/1408] Make Opportunistic Scan API public. Change-Id: I47da0f7e385f0a72815e493852283a44370aa1ce --- framework/java/android/bluetooth/le/ScanSettings.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 5bdfdb544c4..0106686f8f8 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -29,8 +29,6 @@ public final class ScanSettings implements Parcelable { /** * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for * other scan results without starting BLE scans themselves. - * - * @hide */ public static final int SCAN_MODE_OPPORTUNISTIC = -1; -- GitLab From 43c66e29b0fe8ddaffea21a1ba178a8c8f0e8d75 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 19 Mar 2015 15:09:56 -0700 Subject: [PATCH 0439/1408] Add API for BLE_SCAN_ALWAYS_AVAILABLE feature. Change-Id: I83e966d4db076db367ded71bfb50c39e57568156 --- .../android/bluetooth/BluetoothAdapter.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b8f4bf8f044..ee055a5d505 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -208,6 +209,23 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows user to enable BLE scans even when + * Bluetooth is turned off.

        + * + * Notification of result of this activity is posted using + * {@link android.app.Activity#onActivityResult}. The resultCode will be + * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or + * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an + * error occurred. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = + "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; + /** * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter * has changed. @@ -918,6 +936,22 @@ public final class BluetoothAdapter { return false; } + /** + * Returns {@code true} if BLE scan is always available, {@code false} otherwise.

        + * + * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and + * fetch scan results even when Bluetooth is turned off.

        + * + * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}. + * + * @hide + */ + @SystemApi + public boolean isBleScanAlwaysAvailable() { + // TODO: implement after Settings UI change. + return false; + } + /** * Returns whether peripheral mode is supported. * -- GitLab From 98d25c5f895ac94d6af09309bd45553b2e8d7510 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 7 Apr 2015 14:36:53 -0700 Subject: [PATCH 0440/1408] Onfound onlost feature. Change-Id: I5475cb21183abab8cf04af486ff7692396801b92 Signed-off-by: Prerepa Viswanadham --- .../android/bluetooth/BluetoothAdapter.java | 18 +++ .../java/android/bluetooth/IBluetooth.aidl | 1 + .../bluetooth/le/BluetoothLeScanner.java | 39 ++++++ .../android/bluetooth/le/ScanCallback.java | 10 +- .../java/android/bluetooth/le/ScanFilter.java | 10 ++ .../android/bluetooth/le/ScanSettings.java | 113 ++++++++++++++++-- 6 files changed, 182 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ee055a5d505..0442d094404 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -997,6 +997,24 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if hardware has entries available for matching beacons + * + * @return true if there are hw entries available for matching beacons + * @hide + */ + public boolean isHardwareTrackingFiltersAvailable() { + if (getState() != STATE_ON) return false; + try { + synchronized(mManagerCallback) { + if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Return the record of {@link BluetoothActivityEnergyInfo} object that * has the activity and energy info. This can be used to ascertain what diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index dabb1ceaea8..299f4c8e327 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -98,6 +98,7 @@ interface IBluetooth boolean isActivityAndEnergyReportingSupported(); void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); + int numOfHwTrackFiltersAvailable(); // for dumpsys support String dump(); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 93ea299bc8d..73a19072c0e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -128,6 +128,16 @@ public final class BluetoothLeScanner { ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); return; } + if (!isHardwareResourcesAvailableForScan(settings)) { + postCallbackError(callback, + ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); + return; + } + if (!isSettingsAndFilterComboAllowed(settings, filters)) { + postCallbackError(callback, + ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); + return; + } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, callback, resultStorages); wrapper.startRegisteration(); @@ -394,4 +404,33 @@ public final class BluetoothLeScanner { } return false; } + + private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, + List filterList) { + final int callbackType = settings.getCallbackType(); + // If onlost/onfound is requested, a non-empty filter is expected + if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH + | ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { + if (filterList == null) { + return false; + } + for (ScanFilter filter : filterList) { + if (filter.isAllFieldsEmpty()) { + return false; + } + } + } + return true; + } + + private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { + final int callbackType = settings.getCallbackType(); + if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 + || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { + // For onlost/onfound, we required hw support be available + return (mBluetoothAdapter.isOffloadedFilteringSupported() && + mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); + } + return true; + } } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 05782a86d81..27b96bd6e43 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -44,11 +44,17 @@ public abstract class ScanCallback { */ public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4; + /** + * Fails to start scan as it is out of hardware resources. + * @hide + */ + public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; + /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines how this callback was triggered. Currently could only be - * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}. + * @param callbackType Determines how this callback was triggered. Could be of + * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES} * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 502521893ec..92a38171a16 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -67,6 +67,8 @@ public final class ScanFilter implements Parcelable { private final byte[] mManufacturerData; @Nullable private final byte[] mManufacturerDataMask; + private static final ScanFilter EMPTY = new ScanFilter.Builder().build() ; + private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask, ParcelUuid serviceDataUuid, @@ -409,6 +411,14 @@ public final class ScanFilter implements Parcelable { Objects.equals(mServiceUuidMask, other.mServiceUuidMask); } + /** + * Checks if the scanfilter is empty + * @hide + */ + public boolean isAllFieldsEmpty() { + return EMPTY.equals(this); + } + /** * Builder class for {@link ScanFilter}. */ diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 0106686f8f8..f103cae5c25 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -59,7 +59,6 @@ public final class ScanSettings implements Parcelable { /** * A result callback is only triggered for the first advertisement packet received that matches * the filter criteria. - * * @hide */ @SystemApi @@ -68,15 +67,53 @@ public final class ScanSettings implements Parcelable { /** * Receive a callback when advertisements are no longer received from a device that has been * previously reported by a first match callback. - * * @hide */ @SystemApi public static final int CALLBACK_TYPE_MATCH_LOST = 4; + + /** + * Determines how many advertisements to match per filter, as this is scarce hw resource + */ + /** + * Match one advertisement per filter + * @hide + */ + public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; + + /** + * Match few advertisement per filter, depends on current capability and availibility of + * the resources in hw + * @hide + */ + public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; + + /** + * Match as many advertisement per filter as hw could allow, depends on current + * capability and availibility of the resources in hw + * @hide + */ + public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; + + + /** + * In Aggressive mode, hw will determine a match sooner even with feeble signal strength + * and few number of sightings/match in a duration. + * @hide + */ + public static final int MATCH_MODE_AGGRESSIVE = 1; + /** - * Request full scan results which contain the device, rssi, advertising data, scan response as - * well as the scan timestamp. + * For sticky mode, higher threshold of signal strength and sightings is required + * before reporting by hw + * @hide + */ + public static final int MATCH_MODE_STICKY = 2; + + /** + * Request full scan results which contain the device, rssi, advertising data, scan response + * as well as the scan timestamp. * * @hide */ @@ -106,6 +143,10 @@ public final class ScanSettings implements Parcelable { // Time of delay for reporting the scan result private long mReportDelayMillis; + private int mMatchMode; + + private int mNumOfMatchesPerFilter; + public int getScanMode() { return mScanMode; } @@ -118,6 +159,20 @@ public final class ScanSettings implements Parcelable { return mScanResultType; } + /** + * @hide + */ + public int getMatchMode() { + return mMatchMode; + } + + /** + * @hide + */ + public int getNumOfMatches() { + return mNumOfMatchesPerFilter; + } + /** * Returns report delay timestamp based on the device clock. */ @@ -126,11 +181,13 @@ public final class ScanSettings implements Parcelable { } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayMillis) { + long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; + mNumOfMatchesPerFilter = numOfMatchesPerFilter; + mMatchMode = numOfMatchesPerFilter; } private ScanSettings(Parcel in) { @@ -138,6 +195,8 @@ public final class ScanSettings implements Parcelable { mCallbackType = in.readInt(); mScanResultType = in.readInt(); mReportDelayMillis = in.readLong(); + mMatchMode = in.readInt(); + mNumOfMatchesPerFilter = in.readInt(); } @Override @@ -146,6 +205,8 @@ public final class ScanSettings implements Parcelable { dest.writeInt(mCallbackType); dest.writeInt(mScanResultType); dest.writeLong(mReportDelayMillis); + dest.writeInt(mMatchMode); + dest.writeInt(mNumOfMatchesPerFilter); } @Override @@ -174,7 +235,8 @@ public final class ScanSettings implements Parcelable { private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES; private int mScanResultType = SCAN_RESULT_TYPE_FULL; private long mReportDelayMillis = 0; - + private int mMatchMode = MATCH_MODE_AGGRESSIVE; + private int mNumOfMatchesPerFilter = MATCH_NUM_ONE_ADVERTISEMENT; /** * Set scan mode for Bluetooth LE scan. * @@ -254,12 +316,49 @@ public final class ScanSettings implements Parcelable { return this; } + /** + * Set the number of matches for Bluetooth LE scan filters hardware match + * + * @param numOfMatches The num of matches can be one of + * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or + * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or + * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} + * @throws IllegalArgumentException If the {@code matchMode} is invalid. + * @hide + */ + public Builder setNumOfMatches(int numOfMatches) { + if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT + || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) { + throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches); + } + mNumOfMatchesPerFilter = numOfMatches; + return this; + } + + /** + * Set match mode for Bluetooth LE scan filters hardware match + * + * @param matchMode The match mode can be one of + * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or + * {@link ScanSettings#MATCH_MODE_STICKY} + * @throws IllegalArgumentException If the {@code matchMode} is invalid. + * @hide + */ + public Builder setMatchMode(int matchMode) { + if (matchMode < MATCH_MODE_AGGRESSIVE + || matchMode > MATCH_MODE_STICKY) { + throw new IllegalArgumentException("invalid matchMode " + matchMode); + } + mMatchMode = matchMode; + return this; + } + /** * Build {@link ScanSettings}. */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayMillis); + mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter); } } } -- GitLab From cffc87ee19a147796c683da3c844d242f89416a4 Mon Sep 17 00:00:00 2001 From: Scott Kennedy Date: Fri, 10 Apr 2015 10:25:34 -0700 Subject: [PATCH 0441/1408] Fix a typo in BluetoothLeScanner documentation Change-Id: Id19dae9a583c0ef621789604277e3f6c6aead80c --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 93ea299bc8d..5dbfa6ac9c2 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -36,7 +36,7 @@ import java.util.UUID; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of Bluetotoh LE devices using {@link ScanFilter}. It + * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It * can also request different types of callbacks for delivering the result. *

        * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of -- GitLab From c0a7c93812c39589a1c90a18c93ddb3a4e36205a Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Thu, 9 Apr 2015 09:24:48 +0200 Subject: [PATCH 0442/1408] OBEX Over L2CAP + SDP search API for BT profiles - Updated OBEX to support SRM - Added support for OBEX over l2cap and SRM. - Minor bugfixes, and reduce CPU load ALOT - Added support to send responses without body data. - Extend BluetoothSocket to support L2CAP - Added functionality to get the channel number needed to be able to create an SDP record with the channel number. - Added interface to get socket type and max packet sizes. - Added interface to perform SDP search and get the resulting SDP record data. Change-Id: I9d37a00ce73dfffc0e3ce03eab5511ba3a86e5b8 --- .../android/bluetooth/BluetoothAdapter.java | 48 ++++- .../android/bluetooth/BluetoothDevice.java | 69 +++++- .../bluetooth/BluetoothServerSocket.java | 52 ++++- .../android/bluetooth/BluetoothSocket.java | 197 +++++++++++++++--- .../java/android/bluetooth/IBluetooth.aidl | 2 +- .../java/android/bluetooth/SdpMasRecord.java | 147 +++++++++++++ .../java/android/bluetooth/SdpMnsRecord.java | 112 ++++++++++ .../android/bluetooth/SdpOppOpsRecord.java | 118 +++++++++++ .../java/android/bluetooth/SdpPseRecord.java | 125 +++++++++++ .../java/android/bluetooth/SdpRecord.java | 76 +++++++ 10 files changed, 916 insertions(+), 30 deletions(-) create mode 100644 framework/java/android/bluetooth/SdpMasRecord.java create mode 100644 framework/java/android/bluetooth/SdpMnsRecord.java create mode 100644 framework/java/android/bluetooth/SdpOppOpsRecord.java create mode 100644 framework/java/android/bluetooth/SdpPseRecord.java create mode 100644 framework/java/android/bluetooth/SdpRecord.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0442d094404..ac74a621a76 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2009-2014 The Android Open Source Project + * Copyright (C) 2009-2015 The Android Open Source Project + * Copyright (C) 2015 Samsung LSI * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -377,6 +378,18 @@ public final class BluetoothAdapter { /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + + /** When creating a ServerSocket using listenUsingRfcommOn() or + * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create + * a ServerSocket that auto assigns a channel number to the first + * bluetooth socket. + * The channel number assigned to this first Bluetooth Socket will + * be stored in the ServerSocket, and reused for subsequent Bluetooth + * sockets. + * @hide */ + public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2; + + private static final int ADDRESS_LENGTH = 17; private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30; @@ -1144,6 +1157,9 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, true, true, channel); int errno = socket.mSocket.bindListen(); + if(channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } if (errno != 0) { //TODO(BT): Throw the same exception error code // that the previous code was using. @@ -1278,6 +1294,9 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } if (errno != 0) { //TODO(BT): Throw the same exception error code // that the previous code was using. @@ -1300,6 +1319,9 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, true, port); int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } if (errno < 0) { //TODO(BT): Throw the same exception error code // that the previous code was using. @@ -1329,6 +1351,30 @@ public final class BluetoothAdapter { return socket; } + /** + * Construct an encrypted, authenticated, L2CAP server socket. + * Call #accept to retrieve connections to this socket. + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_L2CAP, true, true, port); + int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); + } + return socket; + } + /** * Read the local Out of Band Pairing Data *

        Requires {@link android.Manifest.permission#BLUETOOTH} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index bb0d0a39f56..1fdf9f4df13 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -302,6 +302,12 @@ public final class BluetoothDevice implements Parcelable { */ public static final int DEVICE_TYPE_DUAL = 3; + + /** @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SDP_RECORD = + "android.bluetooth.device.action.SDP_RECORD"; + /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it @@ -526,6 +532,13 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + /** @hide */ + public static final String EXTRA_SDP_RECORD = + "android.bluetooth.device.extra.SDP_RECORD"; + + /** @hide */ + public static final String EXTRA_SDP_SEARCH_STATUS = + "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; /** * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. @@ -1054,14 +1067,34 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Perform a service discovery on the remote device to get the SDP records associated + * with the specified UUID. + * + *

        This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, + * with the SDP records found on the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_SDP_RECORD} intent is sent with an status value in + * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. + * Detailed status error codes can be found by members of the Bluetooth package in + * the AbstractionLayer class. + *

        Requires {@link android.Manifest.permission#BLUETOOTH}. + * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. + * The object type will match one of the SdpXxxRecord types, depending on the UUID searched + * for. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + */ /** @hide */ - public boolean fetchMasInstances() { + public boolean sdpSearch(ParcelUuid uuid) { if (sService == null) { - Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances"); + Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); return false; } try { - return sService.fetchRemoteMasInstances(this); + return sService.sdpSearch(this,uuid); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -1260,6 +1293,36 @@ public final class BluetoothDevice implements Parcelable { null); } + /** + * Create an L2cap {@link BluetoothSocket} ready to start a secure + * outgoing connection to this remote device on given channel. + *

        The remote device will be authenticated and communication on this + * socket will be encrypted. + *

        Use this socket only if an authenticated socket link is possible. + * Authentication refers to the authentication of the link key to + * prevent man-in-the-middle type of attacks. + * For example, for Bluetooth 2.1 devices, if any of the devices does not + * have an input and output capability or just has the ability to + * display a numeric key, a secure socket connection is not possible. + * In such a case, use {#link createInsecureRfcommSocket}. + * For more details, refer to the Security Model section 5.2 (vol 3) of + * Bluetooth Core Specification version 2.1 + EDR. + *

        Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. + *

        Valid L2CAP PSM channels are in range 1 to 2^16. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param channel L2cap PSM/channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + * @hide + */ + public BluetoothSocket createL2capSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel, + null); + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device using SDP lookup of uuid. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index bc56e556492..21024a6021b 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Handler; import android.os.ParcelUuid; +import android.util.Log; import java.io.Closeable; import java.io.IOException; @@ -66,10 +67,11 @@ import java.io.IOException; */ public final class BluetoothServerSocket implements Closeable { + private static final String TAG = "BluetoothServerSocket"; /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; - private final int mChannel; + private int mChannel; /** * Construct a socket for incoming connections. @@ -84,6 +86,9 @@ public final class BluetoothServerSocket implements Closeable { throws IOException { mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); + if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + mSocket.setExcludeSdp(true); + } } /** @@ -98,6 +103,7 @@ public final class BluetoothServerSocket implements Closeable { /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid) throws IOException { mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid); + // TODO: This is the same as mChannel = -1 - is this intentional? mChannel = mSocket.getPort(); } @@ -153,6 +159,7 @@ public final class BluetoothServerSocket implements Closeable { /*package*/ void setServiceName(String ServiceName) { mSocket.setServiceName(ServiceName); } + /** * Returns the channel on which this socket is bound. * @hide @@ -160,4 +167,47 @@ public final class BluetoothServerSocket implements Closeable { public int getChannel() { return mChannel; } + + /** + * Sets the channel on which future sockets are bound. + * Currently used only when a channel is auto generated. + */ + /*package*/ void setChannel(int newChannel) { + /* TODO: From a design/architecture perspective this is wrong. + * The bind operation should be conducted through this class + * and the resulting port should be kept in mChannel, and + * not set from BluetoothAdapter. */ + if(mSocket != null) { + if(mSocket.getPort() != newChannel) { + Log.w(TAG,"The port set is different that the underlying port. mSocket.getPort(): " + + mSocket.getPort() + " requested newChannel: " + newChannel); + } + } + mChannel = newChannel; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ServerSocket: Type: "); + switch(mSocket.getConnectionType()) { + case BluetoothSocket.TYPE_RFCOMM: + { + sb.append("TYPE_RFCOMM"); + break; + } + case BluetoothSocket.TYPE_L2CAP: + { + sb.append("TYPE_L2CAP"); + break; + } + case BluetoothSocket.TYPE_SCO: + { + sb.append("TYPE_SCO"); + break; + } + } + sb.append(" Channel: ").append(mChannel); + return sb.toString(); + } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 36997e54484..5702d117b66 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -21,6 +21,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.BufferedInputStream; import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; @@ -29,6 +30,8 @@ import java.io.OutputStream; import java.util.Locale; import java.util.UUID; import android.net.LocalSocket; + +import java.nio.Buffer; import java.nio.ByteOrder; import java.nio.ByteBuffer; /** @@ -86,17 +89,19 @@ public final class BluetoothSocket implements Closeable { /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; + /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF; /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ - /*package*/ static final int TYPE_RFCOMM = 1; - /*package*/ static final int TYPE_SCO = 2; - /*package*/ static final int TYPE_L2CAP = 3; + public static final int TYPE_RFCOMM = 1; + public static final int TYPE_SCO = 2; + public static final int TYPE_L2CAP = 3; /*package*/ static final int EBADFD = 77; /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; + /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -106,6 +111,7 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; + private boolean mExcludeSdp = false; private ParcelFileDescriptor mPfd; private LocalSocket mSocket; private InputStream mSocketIS; @@ -115,7 +121,11 @@ public final class BluetoothSocket implements Closeable { private String mServiceName; private static int PROXY_CONNECTION_TIMEOUT = 5000; - private static int SOCK_SIGNAL_SIZE = 16; + private static int SOCK_SIGNAL_SIZE = 20; + + private ByteBuffer mL2capBuffer = null; + private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer. + private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received. private enum SocketState { INIT, @@ -144,12 +154,14 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { - if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) { + if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); + if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 + && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { if (port < 1 || port > MAX_RFCOMM_CHANNEL) { throw new IOException("Invalid RFCOMM channel: " + port); } } - if(uuid != null) + if (uuid != null) mUuid = uuid; else mUuid = new ParcelUuid(new UUID(0, 0)); mType = type; @@ -172,6 +184,7 @@ public final class BluetoothSocket implements Closeable { mOutputStream = new BluetoothOutputStream(this); } private BluetoothSocket(BluetoothSocket s) { + if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType); mUuid = s.mUuid; mType = s.mType; mAuth = s.mAuth; @@ -179,7 +192,11 @@ public final class BluetoothSocket implements Closeable { mPort = s.mPort; mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); + mMaxRxPacketSize = s.mMaxRxPacketSize; + mMaxTxPacketSize = s.mMaxTxPacketSize; + mServiceName = s.mServiceName; + mExcludeSdp = s.mExcludeSdp; } private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); @@ -229,6 +246,8 @@ public final class BluetoothSocket implements Closeable { flags |= SEC_FLAG_AUTH; if(mEncrypt) flags |= SEC_FLAG_ENCRYPT; + if(mExcludeSdp) + flags |= BTSOCK_FLAG_NO_SDP; return flags; } @@ -298,7 +317,8 @@ public final class BluetoothSocket implements Closeable { try { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + IBluetooth bluetoothProxy = + BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -370,7 +390,7 @@ public final class BluetoothSocket implements Closeable { mSocketState = SocketState.LISTENING; } if (DBG) Log.d(TAG, "channel: " + channel); - if (mPort == -1) { + if (mPort <= -1) { mPort = channel; } // else ASSERT(mPort == channel) ret = 0; @@ -391,7 +411,8 @@ public final class BluetoothSocket implements Closeable { /*package*/ BluetoothSocket accept(int timeout) throws IOException { BluetoothSocket acceptedSocket; - if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state"); + if (mSocketState != SocketState.LISTENING) + throw new IOException("bt socket is not in listen state"); if(timeout > 0) { Log.d(TAG, "accept() set timeout (ms):" + timeout); mSocket.setSoTimeout(timeout); @@ -427,27 +448,80 @@ public final class BluetoothSocket implements Closeable { } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - if (mSocketIS == null) throw new IOException("read is called on null InputStream"); + int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - int ret = mSocketIS.read(b, offset, length); - if(ret < 0) + if(mType == TYPE_L2CAP) + { + int bytesToRead = length; + if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length + + "mL2capBuffer= " + mL2capBuffer); + if (mL2capBuffer == null) { + createL2capRxBuffer(); + } + if (mL2capBuffer.remaining() == 0) { + if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling..."); + if (fillL2capRxBuffer() == -1) { + return -1; + } + } + if (bytesToRead > mL2capBuffer.remaining()) { + bytesToRead = mL2capBuffer.remaining(); + } + if(VDBG) Log.v(TAG, "get(): offset: " + offset + + " bytesToRead: " + bytesToRead); + mL2capBuffer.get(b, offset, bytesToRead); + ret = bytesToRead; + }else { + if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length); + ret = mSocketIS.read(b, offset, length); + } + if (ret < 0) throw new IOException("bt socket closed, read return: " + ret); if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - if (mSocketOS == null) throw new IOException("write is called on null OutputStream"); - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - mSocketOS.write(b, offset, length); - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; + + //TODO: Since bindings can exist between the SDU size and the + // protocol, we might need to throw an exception instead of just + // splitting the write into multiple smaller writes. + // Rfcomm uses dynamic allocation, and should not have any bindings + // to the actual message length. + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); + if (mType == TYPE_L2CAP) { + if(length <= mMaxTxPacketSize) { + mSocketOS.write(b, offset, length); + } else { + int tmpOffset = offset; + int tmpLength = mMaxTxPacketSize; + int endIndex = offset + length; + boolean done = false; + if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" + + "Packet will be divided into SDU packets of size " + + mMaxTxPacketSize); + do{ + mSocketOS.write(b, tmpOffset, tmpLength); + tmpOffset += mMaxTxPacketSize; + if((tmpOffset + mMaxTxPacketSize) > endIndex) { + tmpLength = endIndex - tmpOffset; + done = true; + } + } while(!done); + + } + } else { + mSocketOS.write(b, offset, length); + } + // There is no good way to confirm since the entire process is asynchronous anyway + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; } @Override public void close() throws IOException { - if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -457,8 +531,9 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + - ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); + if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + + "mSocket: " + mSocket); if(mSocket != null) { if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); @@ -480,6 +555,47 @@ public final class BluetoothSocket implements Closeable { /*package */ int getPort() { return mPort; } + + /** + * Get the maximum supported Transmit packet size for the underlying transport. + * Use this to optimize the writes done to the output socket, to avoid sending + * half full packets. + * @return the maximum supported Transmit packet size for the underlying transport. + */ + public int getMaxTransmitPacketSize(){ + return mMaxTxPacketSize; + } + + /** + * Get the maximum supported Receive packet size for the underlying transport. + * Use this to optimize the reads done on the input stream, as any call to read + * will return a maximum of this amount of bytes - or for some transports a + * multiple of this value. + * @return the maximum supported Receive packet size for the underlying transport. + */ + public int getMaxReceivePacketSize(){ + return mMaxRxPacketSize; + } + + /** + * Get the type of the underlying connection + * @return one of TYPE_ + */ + public int getConnectionType() { + return mType; + } + + /** + * Change if a SDP entry should be automatically created. + * Must be called before calling .bind, for the call to have any effect. + * @param mExcludeSdp

      • TRUE - do not auto generate SDP record. + *
      • FALSE - default - auto generate SPP SDP record. + * @hide + */ + public void setExcludeSdp(boolean excludeSdp) { + this.mExcludeSdp = excludeSdp; + } + private String convertAddr(final byte[] addr) { return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); @@ -487,8 +603,10 @@ public final class BluetoothSocket implements Closeable { private String waitSocketSignal(InputStream is) throws IOException { byte [] sig = new byte[SOCK_SIGNAL_SIZE]; int ret = readAll(is, sig); - if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); + if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + + " bytes signal ret: " + ret); ByteBuffer bb = ByteBuffer.wrap(sig); + /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); if(size != SOCK_SIGNAL_SIZE) @@ -497,19 +615,36 @@ public final class BluetoothSocket implements Closeable { bb.get(addr); int channel = bb.getInt(); int status = bb.getInt(); + mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value + mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value String RemoteAddr = convertAddr(addr); if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " - + RemoteAddr + ", channel: " + channel + ", status: " + status); + + RemoteAddr + ", channel: " + channel + ", status: " + status + + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize); if(status != 0) throw new IOException("Connection failure, status: " + status); return RemoteAddr; } + + private void createL2capRxBuffer(){ + if(mType == TYPE_L2CAP) { + // Allocate the buffer to use for reads. + if(VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); + mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); + if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); + mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request + if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + + mL2capBuffer.remaining()); + } + } + private int readAll(InputStream is, byte[] b) throws IOException { int left = b.length; while(left > 0) { int ret = is.read(b, b.length - left, left); if(ret <= 0) - throw new IOException("read failed, socket might closed or timeout, read ret: " + ret); + throw new IOException("read failed, socket might closed or timeout, read ret: " + + ret); left -= ret; if(left != 0) Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + @@ -526,4 +661,18 @@ public final class BluetoothSocket implements Closeable { bb.order(ByteOrder.nativeOrder()); return bb.getInt(); } + + private int fillL2capRxBuffer() throws IOException { + mL2capBuffer.rewind(); + int ret = mSocketIS.read(mL2capBuffer.array()); + if(ret == -1) { + // reached end of stream - return -1 + mL2capBuffer.limit(0); + return -1; + } + mL2capBuffer.limit(ret); + return ret; + } + + } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 299f4c8e327..af560df8e2f 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -68,7 +68,7 @@ interface IBluetooth int getRemoteClass(in BluetoothDevice device); ParcelUuid[] getRemoteUuids(in BluetoothDevice device); boolean fetchRemoteUuids(in BluetoothDevice device); - boolean fetchRemoteMasInstances(in BluetoothDevice device); + boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] diff --git a/framework/java/android/bluetooth/SdpMasRecord.java b/framework/java/android/bluetooth/SdpMasRecord.java new file mode 100644 index 00000000000..fa164c0fa9d --- /dev/null +++ b/framework/java/android/bluetooth/SdpMasRecord.java @@ -0,0 +1,147 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class SdpMasRecord implements Parcelable { + private final int mMasInstanceId; + private final int mL2capPsm; + private final int mRfcommChannelNumber; + private final int mProfileVersion; + private final int mSupportedFeatures; + private final int mSupportedMessageTypes; + private final String mServiceName; + public static final class MessageType { + public static final int EMAIL = 0x01; + public static final int SMS_GSM = 0x02; + public static final int SMS_CDMA = 0x04; + public static final int MMS = 0x08; + } + + public SdpMasRecord(int mas_instance_id, + int l2cap_psm, + int rfcomm_channel_number, + int profile_version, + int supported_features, + int supported_message_types, + String service_name){ + this.mMasInstanceId = mas_instance_id; + this.mL2capPsm = l2cap_psm; + this.mRfcommChannelNumber = rfcomm_channel_number; + this.mProfileVersion = profile_version; + this.mSupportedFeatures = supported_features; + this.mSupportedMessageTypes = supported_message_types; + this.mServiceName = service_name; + } + + public SdpMasRecord(Parcel in){ + this.mMasInstanceId = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mRfcommChannelNumber = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mSupportedFeatures = in.readInt(); + this.mSupportedMessageTypes = in.readInt(); + this.mServiceName = in.readString(); + } + @Override + public int describeContents() { + // TODO Auto-generated method stub + return 0; + } + + public int getMasInstanceId() { + return mMasInstanceId; + } + + public int getL2capPsm() { + return mL2capPsm; + } + + public int getRfcommCannelNumber() { + return mRfcommChannelNumber; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + public int getSupportedFeatures() { + return mSupportedFeatures; + } + + public int getSupportedMessageTypes() { + return mSupportedMessageTypes; + } + + public boolean msgSupported(int msg) { + return (mSupportedMessageTypes & msg) != 0; + } + + public String getServiceName() { + return mServiceName; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + + dest.writeInt(this.mMasInstanceId); + dest.writeInt(this.mL2capPsm); + dest.writeInt(this.mRfcommChannelNumber); + dest.writeInt(this.mProfileVersion); + dest.writeInt(this.mSupportedFeatures); + dest.writeInt(this.mSupportedMessageTypes); + dest.writeString(this.mServiceName); + + } + @Override + public String toString(){ + String ret = "Bluetooth MAS SDP Record:\n"; + + if(mMasInstanceId != -1){ + ret += "Mas Instance Id: " + mMasInstanceId + "\n"; + } + if(mRfcommChannelNumber != -1){ + ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; + } + if(mL2capPsm != -1){ + ret += "L2CAP PSM: " + mL2capPsm + "\n"; + } + if(mServiceName != null){ + ret += "Service Name: " + mServiceName + "\n"; + } + if(mProfileVersion != -1){ + ret += "Profile version: " + mProfileVersion + "\n"; + } + if(mSupportedMessageTypes != -1){ + ret += "Supported msg types: " + mSupportedMessageTypes + "\n"; + } + if(mSupportedFeatures != -1){ + ret += "Supported features: " + mSupportedFeatures + "\n"; + } + return ret; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpMasRecord createFromParcel(Parcel in) { + return new SdpMasRecord(in); + } + public SdpRecord[] newArray(int size) { + return new SdpRecord[size]; + } + }; +} diff --git a/framework/java/android/bluetooth/SdpMnsRecord.java b/framework/java/android/bluetooth/SdpMnsRecord.java new file mode 100644 index 00000000000..c02bb5a1841 --- /dev/null +++ b/framework/java/android/bluetooth/SdpMnsRecord.java @@ -0,0 +1,112 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class SdpMnsRecord implements Parcelable { + private final int mL2capPsm; + private final int mRfcommChannelNumber; + private final int mSupportedFeatures; + private final int mProfileVersion; + private final String mServiceName; + + public SdpMnsRecord(int l2cap_psm, + int rfcomm_channel_number, + int profile_version, + int supported_features, + String service_name){ + this.mL2capPsm = l2cap_psm; + this.mRfcommChannelNumber = rfcomm_channel_number; + this.mSupportedFeatures = supported_features; + this.mServiceName = service_name; + this.mProfileVersion = profile_version; + } + + public SdpMnsRecord(Parcel in){ + this.mRfcommChannelNumber = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mServiceName = in.readString(); + this.mSupportedFeatures = in.readInt(); + this.mProfileVersion = in.readInt(); + } + @Override + public int describeContents() { + // TODO Auto-generated method stub + return 0; + } + + + public int getL2capPsm() { + return mL2capPsm; + } + + public int getRfcommChannelNumber() { + return mRfcommChannelNumber; + } + + public int getSupportedFeatures() { + return mSupportedFeatures; + } + + public String getServiceName() { + return mServiceName; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRfcommChannelNumber); + dest.writeInt(mL2capPsm); + dest.writeString(mServiceName); + dest.writeInt(mSupportedFeatures); + dest.writeInt(mProfileVersion); + } + + public String toString(){ + String ret = "Bluetooth MNS SDP Record:\n"; + + if(mRfcommChannelNumber != -1){ + ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; + } + if(mL2capPsm != -1){ + ret += "L2CAP PSM: " + mL2capPsm + "\n"; + } + if(mServiceName != null){ + ret += "Service Name: " + mServiceName + "\n"; + } + if(mSupportedFeatures != -1){ + ret += "Supported features: " + mSupportedFeatures + "\n"; + } + if(mProfileVersion != -1){ + ret += "Profile_version: " + mProfileVersion+"\n"; + } + return ret; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpMnsRecord createFromParcel(Parcel in) { + return new SdpMnsRecord(in); + } + public SdpMnsRecord[] newArray(int size) { + return new SdpMnsRecord[size]; + } + }; +} diff --git a/framework/java/android/bluetooth/SdpOppOpsRecord.java b/framework/java/android/bluetooth/SdpOppOpsRecord.java new file mode 100644 index 00000000000..e0e4007a215 --- /dev/null +++ b/framework/java/android/bluetooth/SdpOppOpsRecord.java @@ -0,0 +1,118 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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 java.util.Arrays; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data representation of a Object Push Profile Server side SDP record. + */ +/** @hide */ +public class SdpOppOpsRecord implements Parcelable { + + private final String mServiceName; + private final int mRfcommChannel; + private final int mL2capPsm; + private final int mProfileVersion; + private final byte[] mFormatsList; + + public SdpOppOpsRecord(String serviceName, int rfcommChannel, + int l2capPsm, int version, byte[] formatsList) { + super(); + this.mServiceName = serviceName; + this.mRfcommChannel = rfcommChannel; + this.mL2capPsm = l2capPsm; + this.mProfileVersion = version; + this.mFormatsList = formatsList; + } + + public String getServiceName() { + return mServiceName; + } + + public int getRfcommChannel() { + return mRfcommChannel; + } + + public int getL2capPsm() { + return mL2capPsm; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + public byte[] getFormatsList() { + return mFormatsList; + } + + @Override + public int describeContents() { + /* No special objects */ + return 0; + } + + public SdpOppOpsRecord(Parcel in){ + this.mRfcommChannel = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mServiceName = in.readString(); + int arrayLength = in.readInt(); + if(arrayLength > 0) { + byte[] bytes = new byte[arrayLength]; + in.readByteArray(bytes); + this.mFormatsList = bytes; + } else { + this.mFormatsList = null; + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRfcommChannel); + dest.writeInt(mL2capPsm); + dest.writeInt(mProfileVersion); + dest.writeString(mServiceName); + if(mFormatsList!= null && mFormatsList.length > 0) { + dest.writeInt(mFormatsList.length); + dest.writeByteArray(mFormatsList); + } else { + dest.writeInt(0); + } + } + + public String toString(){ + StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n"); + sb.append(" RFCOMM Chan Number: ").append(mRfcommChannel); + sb.append("\n L2CAP PSM: ").append(mL2capPsm); + sb.append("\n Profile version: ").append(mProfileVersion); + sb.append("\n Service Name: ").append(mServiceName); + sb.append("\n Formats List: ").append(Arrays.toString(mFormatsList)); + return sb.toString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpOppOpsRecord createFromParcel(Parcel in) { + return new SdpOppOpsRecord(in); + } + public SdpOppOpsRecord[] newArray(int size) { + return new SdpOppOpsRecord[size]; + } + }; + +} diff --git a/framework/java/android/bluetooth/SdpPseRecord.java b/framework/java/android/bluetooth/SdpPseRecord.java new file mode 100644 index 00000000000..2c159ccb83c --- /dev/null +++ b/framework/java/android/bluetooth/SdpPseRecord.java @@ -0,0 +1,125 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class SdpPseRecord implements Parcelable { + private final int mL2capPsm; + private final int mRfcommChannelNumber; + private final int mProfileVersion; + private final int mSupportedFeatures; + private final int mSupportedRepositories; + private final String mServiceName; + + public SdpPseRecord(int l2cap_psm, + int rfcomm_channel_number, + int profile_version, + int supported_features, + int supported_repositories, + String service_name){ + this.mL2capPsm = l2cap_psm; + this.mRfcommChannelNumber = rfcomm_channel_number; + this.mProfileVersion = profile_version; + this.mSupportedFeatures = supported_features; + this.mSupportedRepositories = supported_repositories; + this.mServiceName = service_name; + } + + public SdpPseRecord(Parcel in){ + this.mRfcommChannelNumber = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mSupportedFeatures = in.readInt(); + this.mSupportedRepositories = in.readInt(); + this.mServiceName = in.readString(); + } + @Override + public int describeContents() { + // TODO Auto-generated method stub + return 0; + } + + public int getL2capPsm() { + return mL2capPsm; + } + + public int getRfcommChannelNumber() { + return mRfcommChannelNumber; + } + + public int getSupportedFeatures() { + return mSupportedFeatures; + } + + public String getServiceName() { + return mServiceName; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + public int getSupportedRepositories() { + return mSupportedRepositories; + } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRfcommChannelNumber); + dest.writeInt(mL2capPsm); + dest.writeInt(mProfileVersion); + dest.writeInt(mSupportedFeatures); + dest.writeInt(mSupportedRepositories); + dest.writeString(mServiceName); + + } + + public String toString(){ + String ret = "Bluetooth MNS SDP Record:\n"; + + if(mRfcommChannelNumber != -1){ + ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; + } + if(mL2capPsm != -1){ + ret += "L2CAP PSM: " + mL2capPsm + "\n"; + } + if(mProfileVersion != -1){ + ret += "profile version: " + mProfileVersion + "\n"; + } + if(mServiceName != null){ + ret += "Service Name: " + mServiceName + "\n"; + } + if(mSupportedFeatures != -1){ + ret += "Supported features: " + mSupportedFeatures + "\n"; + } + if(mSupportedRepositories != -1){ + ret += "Supported repositories: " + mSupportedRepositories + "\n"; + } + + return ret; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpPseRecord createFromParcel(Parcel in) { + return new SdpPseRecord(in); + } + public SdpPseRecord[] newArray(int size) { + return new SdpPseRecord[size]; + } + }; +} diff --git a/framework/java/android/bluetooth/SdpRecord.java b/framework/java/android/bluetooth/SdpRecord.java new file mode 100644 index 00000000000..6f1065e38d2 --- /dev/null +++ b/framework/java/android/bluetooth/SdpRecord.java @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** @hide */ +public class SdpRecord implements Parcelable{ + + private final byte[] mRawData; + private final int mRawSize; + + @Override + public String toString() { + return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData) + + ", rawSize=" + mRawSize + "]"; + } + + public SdpRecord(int size_record, byte[] record){ + this.mRawData = record; + this.mRawSize = size_record; + } + + public SdpRecord(Parcel in){ + this.mRawSize = in.readInt(); + this.mRawData = new byte[mRawSize]; + in.readByteArray(this.mRawData); + + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.mRawSize); + dest.writeByteArray(this.mRawData); + + + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpRecord createFromParcel(Parcel in) { + return new SdpRecord(in); + } + + public SdpRecord[] newArray(int size) { + return new SdpRecord[size]; + } + }; + + public byte[] getRawData() { + return mRawData; + } + + public int getRawSize() { + return mRawSize; + } +} -- GitLab From 9653e2757354c571ff9354b1b3a2c3282b2af43f Mon Sep 17 00:00:00 2001 From: tturney Date: Mon, 13 Apr 2015 14:55:07 -0700 Subject: [PATCH 0443/1408] Fix onLost/onFound logic in isSettingsAndFilterComboAllowed bug: b/20185066 Change-Id: If9e3fecd12ee86aa12fa63688babeff694bee62e --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 73a19072c0e..e184d1e2726 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -409,8 +409,8 @@ public final class BluetoothLeScanner { List filterList) { final int callbackType = settings.getCallbackType(); // If onlost/onfound is requested, a non-empty filter is expected - if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH - | ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { + if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH + | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { if (filterList == null) { return false; } -- GitLab From 5e8cdc7f00265c4adb2b22935eb28708fdbf29ae Mon Sep 17 00:00:00 2001 From: Nitin Arora Date: Mon, 2 Mar 2015 15:03:51 -0800 Subject: [PATCH 0444/1408] Bluetooth LE background operation mode (2/2) Changes include new framework APIs to enable and disable Bluetooth LE separately from Bluetooth Classic. Along with handling the new states in the Bluetooth manager service. Change-Id: Idf667981f48fcbcb6dfda1aa77ea8bab1b2361f0 --- .../android/bluetooth/BluetoothAdapter.java | 312 +++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 55 ++- .../java/android/bluetooth/IBluetooth.aidl | 2 + .../android/bluetooth/IBluetoothGatt.aidl | 2 + .../android/bluetooth/IBluetoothManager.aidl | 2 + .../bluetooth/IBluetoothManagerCallback.aidl | 3 +- .../bluetooth/le/BluetoothLeUtils.java | 2 +- .../bluetooth/BluetoothManagerService.java | 284 +++++++++++++--- 8 files changed, 602 insertions(+), 60 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ac74a621a76..88bb626bfd7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -34,6 +34,9 @@ import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; +import android.app.ActivityThread; +import android.os.SystemProperties; +import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -121,6 +124,9 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; @@ -131,6 +137,9 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; @@ -155,6 +164,24 @@ public final class BluetoothAdapter { */ public static final int STATE_TURNING_OFF = 13; + /** + * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on. + * @hide + */ + public static final int STATE_BLE_TURNING_ON = 14; + + /** + * Indicates the local Bluetooth adapter is in LE only mode. + * @hide + */ + public static final int STATE_BLE_ON = 15; + + /** + * Indicates the local Bluetooth adapter is turning off LE only mode. + * @hide + */ + public static final int STATE_BLE_TURNING_OFF = 16; + /** * Activity Action: Show a system activity that requests discoverable mode. * This activity will also request the user to turn on Bluetooth if it @@ -366,6 +393,39 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_CONNECTION_STATE = "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; + /** + * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. + * @hide + */ + public static final String ACTION_BLE_STATE_CHANGED = + "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED"; + + /** + * Broadcast Action: The notifys Bluetooth ACL connected event. This will be + * by BLE Always on enabled application to know the ACL_CONNECTED event + * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection + * as Bluetooth LE is the only feature available in STATE_BLE_ON + * + * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which + * works in Bluetooth state STATE_ON + * @hide + */ + public static final String ACTION_BLE_ACL_CONNECTED = + "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; + + /** + * Broadcast Action: The notifys Bluetooth ACL connected event. This will be + * by BLE Always on enabled application to know the ACL_DISCONNECTED event + * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth + * LE is the only feature available in STATE_BLE_ON + * + * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which + * works in Bluetooth state STATE_ON + * @hide + */ + public static final String ACTION_BLE_ACL_DISCONNECTED = + "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; + /** The profile is in disconnected state */ public static final int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ @@ -377,6 +437,7 @@ public final class BluetoothAdapter { /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + private final IBinder mToken; /** When creating a ServerSocket using listenUsingRfcommOn() or @@ -447,6 +508,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap(); + mToken = new Binder(); } /** @@ -493,11 +555,9 @@ public final class BluetoothAdapter { * on this device before calling this method. */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - if (getState() != STATE_ON) { - return null; - } + if (!getLeAccess()) return null; if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) { - Log.e(TAG, "bluetooth le advertising not supported"); + Log.e(TAG, "Bluetooth LE advertising not supported"); return null; } synchronized(mLock) { @@ -512,9 +572,7 @@ public final class BluetoothAdapter { * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ public BluetoothLeScanner getBluetoothLeScanner() { - if (getState() != STATE_ON) { - return null; - } + if (!getLeAccess()) return null; synchronized(mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); @@ -532,7 +590,6 @@ public final class BluetoothAdapter { * @return true if the local adapter is turned on */ public boolean isEnabled() { - try { synchronized(mManagerCallback) { if (mService != null) return mService.isEnabled(); @@ -541,6 +598,178 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if Bluetooth LE(Always BLE On feature) is currently + * enabled and ready for use + *

        This returns true if current state is either STATE_ON or STATE_BLE_ON + * + * @return true if the local Bluetooth LE adapter is turned on + * @hide + */ + public boolean isLeEnabled() { + final int state = getLeState(); + if (state == BluetoothAdapter.STATE_ON) { + if (DBG) Log.d (TAG, "STATE_ON"); + } else if (state == BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Log.d (TAG, "STATE_BLE_ON"); + } else { + if (DBG) Log.d (TAG, "STATE_OFF"); + return false; + } + return true; + } + + /** + * Performs action based on user action to turn BT ON + * or OFF if BT is in BLE_ON state + */ + private void notifyUserAction(boolean enable) { + if (mService == null) { + Log.e(TAG, "mService is null"); + return; + } + + try { + if (enable) { + mService.onLeServiceUp(); //NA:TODO implementation pending + } else { + mService.onBrEdrDown(); //NA:TODO implementation pending + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + /** + * Returns true if LE only mode is enabled, that is apps + * have authorization to turn only BT ON and the calling + * app has privilage to do so + */ + private boolean isLEAlwaysOnEnabled() { + boolean ret = false; + if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) { + Log.v(TAG, "LE always on mode is enabled"); + // TODO: System API authorization check + ret = true; + } else { + Log.v(TAG, "LE always on mode is disabled"); + ret = false; + } + return ret; + } + + /** + * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). + * + *

        If the internal Adapter state is STATE_BLE_ON, this would trigger the transition + * to STATE_OFF and completely shut-down Bluetooth + * + *

        If the Adapter state is STATE_ON, This would unregister the existance of + * special Bluetooth LE application and hence the further turning off of Bluetooth + * from UI would ensure the complete turn-off of Bluetooth rather than staying back + * BLE only state + * + *

        This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} + * to be notified of subsequent adapter state changes If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time + * later transition to either {@link #STATE_BLE_ON} or {@link + * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications + * If this call returns false then there was an + * immediate problem that will prevent the QAdapter from being turned off - + * such as the QAadapter already being turned off. + * + * @return true to indicate success, or false on + * immediate error + * @hide + */ + public boolean disableBLE() { + if (isLEAlwaysOnEnabled() != true) return false; + + int state = getLeState(); + if (state == BluetoothAdapter.STATE_ON) { + if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); + try { + mManagerService.updateBleAppCount(mToken, false); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return true; + + } else if (state == BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Log.d (TAG, "STATE_BLE_ON"); + int bleAppCnt = 0; + try { + bleAppCnt = mManagerService.updateBleAppCount(mToken, false); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + if (bleAppCnt == 0) { + // Disable only if there are no other clients + notifyUserAction(false); + } + return true; + } + + if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); + return false; + } + + /** + * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would + * EnableBLE, EnableBLE brings-up Bluetooth so that application can access + * only LE related feature (Bluetooth GATT layers interfaces using the respective class) + * EnableBLE in turn registers the existance of a special App which wants to + * turn on Bluetooth Low enrgy part without making it visible at the settings UI + * as Bluetooth ON. + *

        Invoking EnableBLE when Bluetooth is already in ON state, would just registers + * the existance of special Application and doesn't do anything to current BT state. + * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth + * would stay in BLE_ON state so that LE features are still acessible to the special + * Applications. + * + *

        This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_BLE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned on - + * such as Airplane mode, or the adapter is already turned on. + * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various + * states, It includes all the classic Bluetooth Adapter states along with + * internal BLE only states + * + * @return true to indicate Bluetooth LE start-up has begun, or false on + * immediate error + * @hide + */ + public boolean enableBLE() { + if (isLEAlwaysOnEnabled() != true) return false; + + if (isLeEnabled() == true) { + if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); + try { + mManagerService.updateBleAppCount(mToken, true); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return true; + } + + try { + if (DBG) Log.d(TAG, "Calling enableBLE"); + mManagerService.updateBleAppCount(mToken, true); + return mManagerService.enable(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + + return false; + } + /** * Get the current state of the local Bluetooth adapter. *

        Possible return values are @@ -559,6 +788,13 @@ public final class BluetoothAdapter { { int state= mService.getState(); if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + //consider all internal states as OFF + if (state == BluetoothAdapter.STATE_BLE_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { + if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + state = BluetoothAdapter.STATE_OFF; + } return state; } // TODO(BT) there might be a small gap during STATE_TURNING_ON that @@ -569,6 +805,49 @@ public final class BluetoothAdapter { return STATE_OFF; } + /** + * Get the current state of the local Bluetooth adapter + *

        This returns current internal state of Adapter including LE ON/OFF + * + *

        Possible return values are + * {@link #STATE_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_OFF}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return current state of Bluetooth adapter + * @hide + */ + public int getLeState() { + try { + synchronized(mManagerCallback) { + if (mService != null) + { + int state= mService.getState(); + if (VDBG) Log.d(TAG,"getLeState() returning " + state); + return state; + } + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return BluetoothAdapter.STATE_OFF; + } + + boolean getLeAccess() { + if(getLeState() == STATE_ON) + return true; + + else if (getLeState() == STATE_BLE_ON) + return true; // TODO: FILTER SYSTEM APPS HERE <-- + + return false; + } + /** * Turn on the local Bluetooth adapter—do not use without explicit * user action to turn on Bluetooth. @@ -597,10 +876,23 @@ public final class BluetoothAdapter { * immediate error */ public boolean enable() { + int state = STATE_OFF; if (isEnabled() == true){ if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } + //Use service interface to get the exact state + if (mService != null) { + try { + state = mService.getState(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + if (state == BluetoothAdapter.STATE_BLE_ON) { + Log.e(TAG, "BT is in BLE_ON State"); + notifyUserAction(true); + return true; + } try { return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -1561,6 +1853,10 @@ public final class BluetoothAdapter { } } } + + public void onBrEdrDown() { + if (VDBG) Log.i(TAG, "on QBrEdrDown: "); + } }; /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1fdf9f4df13..1e82c032bf0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -606,7 +606,9 @@ public final class BluetoothDevice implements Parcelable { public void onBluetoothServiceUp(IBluetooth bluetoothService) throws RemoteException { synchronized (BluetoothDevice.class) { - sService = bluetoothService; + if (sService == null) { + sService = bluetoothService; + } } } @@ -616,6 +618,11 @@ public final class BluetoothDevice implements Parcelable { sService = null; } } + + public void onBrEdrDown() + { + if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); + } }; /** * Create a new BluetoothDevice @@ -1030,7 +1037,7 @@ public final class BluetoothDevice implements Parcelable { * or null on error */ public ParcelUuid[] getUuids() { - if (sService == null) { + if (sService == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); return null; } @@ -1057,7 +1064,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean fetchUuidsWithSdp() { IBluetooth service = sService; - if (service == null) { + if (service == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } @@ -1099,16 +1106,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - public int getServiceChannel(ParcelUuid uuid) { - //TODO(BT) - /* - try { - return sService.getRemoteServiceChannel(this, uuid); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return BluetoothDevice.ERROR; - } - /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. @@ -1187,6 +1184,15 @@ public final class BluetoothDevice implements Parcelable { return false; } + boolean isBluetoothEnabled() { + boolean ret = false; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.isEnabled() == true) { + ret = true; + } + return ret; + } + /** * Requires {@link android.Manifest.permission#BLUETOOTH}. * @return Whether the phonebook access is allowed to this device. Can be @@ -1289,6 +1295,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createRfcommSocket(int channel) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, null); } @@ -1355,6 +1365,11 @@ public final class BluetoothDevice implements Parcelable { * insufficient permissions */ public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, new ParcelUuid(uuid)); } @@ -1388,6 +1403,10 @@ public final class BluetoothDevice implements Parcelable { * insufficient permissions */ public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, new ParcelUuid(uuid)); } @@ -1407,6 +1426,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { + + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, null); } @@ -1422,6 +1446,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createScoSocket() throws IOException { + + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index af560df8e2f..90df19828f6 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -102,4 +102,6 @@ interface IBluetooth // for dumpsys support String dump(); + void onLeServiceUp(); + void onBrEdrDown(); } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 7070baebcf7..4ca57f8ecca 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -101,4 +101,6 @@ interface IBluetoothGatt { in int srvcInstanceId, in ParcelUuid srvcId, in int charInstanceId, in ParcelUuid charId, in boolean confirm, in byte[] value); + void disconnectAll(); + void unregAll(); } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 7411d3f27b5..8d1ce990a56 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -44,4 +44,6 @@ interface IBluetoothManager String getAddress(); String getName(); + int updateBleAppCount(IBinder b, boolean enable); + boolean isBleAppPresent(); } diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl index 9551086a1de..1385dafca2f 100644 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -26,4 +26,5 @@ import android.bluetooth.IBluetooth; interface IBluetoothManagerCallback { void onBluetoothServiceUp(in IBluetooth bluetoothService); void onBluetoothServiceDown(); -} \ No newline at end of file + void onBrEdrDown(); +} diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java index 4916bd9cebf..c40256b8906 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -132,7 +132,7 @@ public class BluetoothLeUtils { * {@link BluetoothAdapter#STATE_ON}. */ static void checkAdapterStateOn(BluetoothAdapter adapter) { - if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) { + if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); } } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 32a6a2faad7..46a4599dad3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -58,6 +58,7 @@ import java.util.Map; import java.util.List; import java.util.Vector; +import java.util.*; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -114,6 +115,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTHGATT = 2; private final Context mContext; + private static int mBleAppCount = 0; // Locks are not provided for mName and mAddress. // They are accessed in handler or broadcast receiver, same thread context. @@ -186,11 +188,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } } + + int st = BluetoothAdapter.STATE_OFF; + if (mBluetooth != null) { + try { + st = mBluetooth.getState(); + } catch (RemoteException e) { + Log.e(TAG,"Unable to call getState", e); + } + } + Log.d(TAG, "state" + st); + if (isAirplaneModeOn()) { - // disable without persisting the setting - sendDisableMsg(); + // Clear registered LE apps to force shut-off + synchronized (this) { + mBleAppCount = 0; + } + if (st == BluetoothAdapter.STATE_BLE_ON) { + //if state is BLE_ON make sure you trigger disableBLE part + try { + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + mEnableExternal = false; + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } else if (st == BluetoothAdapter.STATE_ON){ + // disable without persisting the setting + Log.d(TAG, "Calling disable"); + sendDisableMsg(); + } } else if (mEnableExternal) { // enable without persisting the setting + Log.d(TAG, "Calling enable"); sendEnableMsg(mQuietEnableExternal); } } @@ -205,12 +236,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } } - - if (!isNameAndAddressSet()) { - //Sync the Bluetooth name and address from the Bluetooth Adapter - if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); - getNameAndAddress(); - } } } }; @@ -220,6 +245,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; mBluetooth = null; + mBluetoothGatt = null; mBinding = false; mUnbinding = false; mEnable = false; @@ -398,6 +424,133 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + class ClientDeathRecipient implements IBinder.DeathRecipient { + public void binderDied() { + if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App"); + if (mBleAppCount > 0) --mBleAppCount; + + if (mBleAppCount == 0) { + if (DBG) Log.d(TAG, "Disabling LE only mode after application crash"); + try { + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } + } + } + + /** Internal death rec list */ + Map mBleApps = new HashMap(); + + public int updateBleAppCount(IBinder token, boolean enable) { + if (enable) { + ClientDeathRecipient r = mBleApps.get(token); + if (r == null) { + ClientDeathRecipient deathRec = new ClientDeathRecipient(); + try { + token.linkToDeath(deathRec, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + mBleApps.put(token, deathRec); + synchronized (this) { + ++mBleAppCount; + } + if (DBG) Log.d(TAG, "Registered for death Notification"); + } + + } else { + ClientDeathRecipient r = mBleApps.get(token); + if (r != null) { + try { + token.linkToDeath(r, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + mBleApps.remove(token); + synchronized (this) { + if (mBleAppCount > 0) --mBleAppCount; + } + if (DBG) Log.d(TAG, "Unregistered for death Notification"); + } + } + if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount); + if (mBleAppCount == 0 && mEnable) { + try { + if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { + if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); + mEnable = false; + } + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + } + } + return mBleAppCount; + } + + /** @hide*/ + public boolean isBleAppPresent() { + if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount); + return (mBleAppCount > 0); + } + + /** + * Action taken when GattService is turned off + */ + private void onBluetoothGattServiceUp() { + if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); + try{ + if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mBluetooth.onLeServiceUp(); + + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + Binder.restoreCallingIdentity(callingIdentity); + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onServiceUp", e); + } + } + + /** + * Inform BluetoothAdapter instances that BREDR part is down + * and turn off all service and stack if no LE app needs it + */ + private void sendBrEdrDownCallback() { + if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); + int n = mCallbacks.beginBroadcast(); + + if (isBleAppPresent() == false) { + try { + mBluetooth.onBrEdrDown(); + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } + else{//need to stay at BLE ON. disconnect all Gatt connections + try{ + mBluetoothGatt.unregAll();//disconnectAll(); + } catch(RemoteException e) { + Log.e(TAG,"Unable to disconn all", e); + } + } + + Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers."); + for (int i=0; i " + newState); + // Send broadcast message to everyone else + Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } + private void bluetoothStateChangeHandler(int prevState, int newState) { + boolean isStandardBroadcast = true; if (prevState != newState) { //Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) { - boolean isUp = (newState==BluetoothAdapter.STATE_ON); - sendBluetoothStateCallback(isUp); - - if (isUp) { - // connect to GattService - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT); - } - } else { - //If Bluetooth is off, send service down event to proxy objects, and unbind - if (!isUp && canUnbindBluetoothService()) { - unbindAllBluetoothProfileServices(); + if (newState == BluetoothAdapter.STATE_BLE_ON + || newState == BluetoothAdapter.STATE_OFF) { + boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF + && newState == BluetoothAdapter.STATE_BLE_ON); + + if (newState == BluetoothAdapter.STATE_OFF) { + // If Bluetooth is off, send service down event to proxy objects, and unbind + if (DBG) Log.d(TAG, "Bluetooth is complete turn off"); + if (canUnbindBluetoothService()) { + if (DBG) Log.d(TAG, "Good to unbind!"); sendBluetoothServiceDownCallback(); unbindAndFinish(); + sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; } + + } else if (!intermediate_off) { + // connect to GattService + if (DBG) Log.d(TAG, "Bluetooth is in LE only mode"); + if (mBluetoothGatt != null) { + if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp"); + onBluetoothGattServiceUp(); + } else { + if (DBG) Log.d(TAG, "Binding Bluetooth GATT service"); + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH_LE)) { + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); + } + } + sendBleStateChanged(prevState, newState); + //Don't broadcase this as std intent + isStandardBroadcast = false; + + } else if (intermediate_off){ + if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode"); + // For LE only mode, broadcast as is + sendBleStateChanged(prevState, newState); + sendBluetoothStateCallback(false); // BT is OFF for general users + // Broadcast as STATE_OFF + newState = BluetoothAdapter.STATE_OFF; + sendBrEdrDownCallback(); } + } else if (newState == BluetoothAdapter.STATE_ON) { + boolean isUp = (newState==BluetoothAdapter.STATE_ON); + sendBluetoothStateCallback(isUp); + sendBleStateChanged(prevState, newState); + + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON + || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + sendBleStateChanged(prevState, newState); + isStandardBroadcast = false; + + } else if (newState == BluetoothAdapter.STATE_TURNING_ON + || newState == BluetoothAdapter.STATE_TURNING_OFF) { + sendBleStateChanged(prevState, newState); } - //Send broadcast message to everyone else - Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, - BLUETOOTH_PERM); + if (isStandardBroadcast) { + if (prevState == BluetoothAdapter.STATE_BLE_ON) { + // Show prevState of BLE_ON as OFF to standard users + prevState = BluetoothAdapter.STATE_OFF; + } + Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } } } -- GitLab From 82fb4059b7d431b1be9c5a044da37c87283af17a Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Thu, 19 Mar 2015 10:36:45 +0100 Subject: [PATCH 0445/1408] Add support for Bluetooth Sim Access Profile (2/4) Change-Id: I6c634aa38d31a7b5a98c9089840557257fd58209 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothDevice.java | 41 ++ .../android/bluetooth/BluetoothProfile.java | 10 +- .../java/android/bluetooth/BluetoothSap.java | 389 ++++++++++++++++++ .../java/android/bluetooth/BluetoothUuid.java | 9 +- .../java/android/bluetooth/IBluetooth.aidl | 2 + .../java/android/bluetooth/IBluetoothSap.aidl | 37 ++ 7 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothSap.java create mode 100644 framework/java/android/bluetooth/IBluetoothSap.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 88bb626bfd7..d7705a4026c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1746,6 +1746,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEADSET_CLIENT) { BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); return true; + } else if (profile == BluetoothProfile.SAP) { + BluetoothSap sap = new BluetoothSap(context, listener); + return true; } else { return false; } @@ -1810,6 +1813,10 @@ public final class BluetoothAdapter { BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy; headsetClient.close(); break; + case BluetoothProfile.SAP: + BluetoothSap sap = (BluetoothSap)proxy; + sap.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1e82c032bf0..bfc374fb913 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -382,6 +382,9 @@ public final class BluetoothDevice implements Parcelable { /**@hide*/ public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; + /**@hide*/ + public static final int REQUEST_TYPE_SIM_ACCESS = 4; + /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, * Contains package name to return reply intent to. @@ -1269,6 +1272,44 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Requires {@link android.Manifest.permission#BLUETOOTH}. + * @return Whether the Sim access is allowed to this device. Can be + * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @hide + */ + public int getSimAccessPermission() { + if (sService == null) { + return ACCESS_UNKNOWN; + } + try { + return sService.getSimAccessPermission(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return ACCESS_UNKNOWN; + } + + /** + * Sets whether the Sim access is allowed to this device. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or + * {@link #ACCESS_REJECTED}. + * @return Whether the value has been successfully set. + * @hide + */ + public boolean setSimAccessPermission(int value) { + if (sService == null) { + return false; + } + try { + return sService.setSimAccessPermission(this, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 136740505a4..eecb0739657 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -103,17 +103,23 @@ public interface BluetoothProfile { */ public static final int MAP = 9; + /* + * SAP Profile + * @hide + */ + public static final int SAP = 10; + /** * A2DP Sink Profile * @hide */ - public static final int A2DP_SINK = 10; + public static final int A2DP_SINK = 11; /** * AVRCP Controller Profile * @hide */ - public static final int AVRCP_CONTROLLER = 11; + public static final int AVRCP_CONTROLLER = 12; /** * Headset Client - HFP HF Role diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java new file mode 100644 index 00000000000..60528849a01 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2008 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 java.util.ArrayList; +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + + +public final class BluetoothSap implements BluetoothProfile { + + private static final String TAG = "BluetoothSap"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; + + private IBluetoothSap 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 BluetoothSap proxy object. + */ + /*package*/ BluetoothSap(Context context, ServiceListener l) { + if (DBG) Log.d(TAG, "Create BluetoothSap 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(IBluetoothMap.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 SAP 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 BluetoothSap will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized 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; + } + + /** + * Get the current state of the BluetoothSap service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Sap service. + */ + public int getState() { + if (VDBG) log("getState()"); + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return BluetoothSap.STATE_ERROR; + } + + /** + * Get the currently connected remote Bluetooth device (PCE). + * @return The remote Bluetooth device, or null if not in connected or + * connecting state, or if this proxy object is not connected to + * the Sap service. + */ + public BluetoothDevice getClient() { + if (VDBG) log("getClient()"); + if (mService != null) { + try { + return mService.getClient(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return 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 Sap service. + */ + public boolean isConnected(BluetoothDevice device) { + if (VDBG) log("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(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate connection. Initiation of outgoing connections is not + * supported for SAP server. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); + return false; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, + * true otherwise + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } 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 list of connected devices. Currently at most one. + * + * @return list of connected devices + */ + public List getConnectedDevices() { + if (DBG) log("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 + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("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 + */ + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("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 + * @param priority + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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("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; + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) log("Proxy object connected"); + mService = IBluetoothSap.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) log("Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) + return true; + log("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; + } + +} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 194a53e0f6c..2ded4c8fea3 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -76,7 +76,9 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); - + public static final ParcelUuid SAP = + ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); @@ -89,7 +91,7 @@ public final class BluetoothUuid { public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS, MAS}; + ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -143,6 +145,9 @@ public final class BluetoothUuid { public static boolean isMas(ParcelUuid uuid) { return uuid.equals(MAS); } + public static boolean isSap(ParcelUuid uuid) { + return uuid.equals(SAP); + } /** * Returns true if ParcelUuid is present in uuidArray diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 90df19828f6..f6001bfffed 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -79,6 +79,8 @@ interface IBluetooth boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); int getMessageAccessPermission(in BluetoothDevice device); boolean setMessageAccessPermission(in BluetoothDevice device, int value); + int getSimAccessPermission(in BluetoothDevice device); + boolean setSimAccessPermission(in BluetoothDevice device, int value); void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); diff --git a/framework/java/android/bluetooth/IBluetoothSap.aidl b/framework/java/android/bluetooth/IBluetoothSap.aidl new file mode 100644 index 00000000000..8970639467c --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothSap.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * System private API for Bluetooth SAP service + * + * {@hide} + */ +interface IBluetoothSap { + int getState(); + BluetoothDevice getClient(); + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); +} -- GitLab From 20843f715f14d250063c0b0786b8019fcc2365d6 Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Wed, 15 Apr 2015 11:55:20 -0700 Subject: [PATCH 0446/1408] Fix make update-api Change-Id: Iaee002c64096fbd6ba05d6484cc50840146a4af0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ------ framework/java/android/bluetooth/BluetoothSap.java | 6 ------ 2 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d7705a4026c..875aef6264f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -124,9 +124,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; @@ -137,9 +134,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 60528849a01..7b4c6f923ff 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -296,8 +296,6 @@ public final class BluetoothSap 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}, * * @param device Paired bluetooth device * @param priority @@ -325,10 +323,6 @@ public final class BluetoothSap 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} - * * @param device Bluetooth device * @return priority of the device */ -- GitLab From 7688f220e1dd3457c608aabf39ab900606a387a8 Mon Sep 17 00:00:00 2001 From: Nitin Arora Date: Mon, 2 Mar 2015 15:03:51 -0800 Subject: [PATCH 0447/1408] Bluetooth LE background operation mode (2/2) Changes include new framework APIs to enable and disable Bluetooth LE separately from Bluetooth Classic. Along with handling the new states in the Bluetooth manager service. Change-Id: Idf667981f48fcbcb6dfda1aa77ea8bab1b2361f0 --- .../android/bluetooth/BluetoothAdapter.java | 312 +++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 55 ++- .../java/android/bluetooth/IBluetooth.aidl | 2 + .../android/bluetooth/IBluetoothGatt.aidl | 2 + .../android/bluetooth/IBluetoothManager.aidl | 2 + .../bluetooth/IBluetoothManagerCallback.aidl | 3 +- .../bluetooth/le/BluetoothLeUtils.java | 2 +- .../bluetooth/BluetoothManagerService.java | 283 +++++++++++++--- 8 files changed, 601 insertions(+), 60 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 87d5bb030e2..dd030f8538c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -32,6 +32,9 @@ import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; +import android.app.ActivityThread; +import android.os.SystemProperties; +import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -118,6 +121,9 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; @@ -128,6 +134,9 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; @@ -152,6 +161,24 @@ public final class BluetoothAdapter { */ public static final int STATE_TURNING_OFF = 13; + /** + * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on. + * @hide + */ + public static final int STATE_BLE_TURNING_ON = 14; + + /** + * Indicates the local Bluetooth adapter is in LE only mode. + * @hide + */ + public static final int STATE_BLE_ON = 15; + + /** + * Indicates the local Bluetooth adapter is turning off LE only mode. + * @hide + */ + public static final int STATE_BLE_TURNING_OFF = 16; + /** * Activity Action: Show a system activity that requests discoverable mode. * This activity will also request the user to turn on Bluetooth if it @@ -363,6 +390,39 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_CONNECTION_STATE = "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; + /** + * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. + * @hide + */ + public static final String ACTION_BLE_STATE_CHANGED = + "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED"; + + /** + * Broadcast Action: The notifys Bluetooth ACL connected event. This will be + * by BLE Always on enabled application to know the ACL_CONNECTED event + * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection + * as Bluetooth LE is the only feature available in STATE_BLE_ON + * + * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which + * works in Bluetooth state STATE_ON + * @hide + */ + public static final String ACTION_BLE_ACL_CONNECTED = + "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; + + /** + * Broadcast Action: The notifys Bluetooth ACL connected event. This will be + * by BLE Always on enabled application to know the ACL_DISCONNECTED event + * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth + * LE is the only feature available in STATE_BLE_ON + * + * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which + * works in Bluetooth state STATE_ON + * @hide + */ + public static final String ACTION_BLE_ACL_DISCONNECTED = + "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; + /** The profile is in disconnected state */ public static final int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ @@ -374,6 +434,7 @@ public final class BluetoothAdapter { /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + private final IBinder mToken; /** When creating a ServerSocket using listenUsingRfcommOn() or @@ -444,6 +505,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap(); + mToken = new Binder(); } /** @@ -490,11 +552,9 @@ public final class BluetoothAdapter { * on this device before calling this method. */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - if (getState() != STATE_ON) { - return null; - } + if (!getLeAccess()) return null; if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) { - Log.e(TAG, "bluetooth le advertising not supported"); + Log.e(TAG, "Bluetooth LE advertising not supported"); return null; } synchronized(mLock) { @@ -509,9 +569,7 @@ public final class BluetoothAdapter { * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ public BluetoothLeScanner getBluetoothLeScanner() { - if (getState() != STATE_ON) { - return null; - } + if (!getLeAccess()) return null; synchronized(mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); @@ -529,7 +587,6 @@ public final class BluetoothAdapter { * @return true if the local adapter is turned on */ public boolean isEnabled() { - try { synchronized(mManagerCallback) { if (mService != null) return mService.isEnabled(); @@ -538,6 +595,178 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if Bluetooth LE(Always BLE On feature) is currently + * enabled and ready for use + *

        This returns true if current state is either STATE_ON or STATE_BLE_ON + * + * @return true if the local Bluetooth LE adapter is turned on + * @hide + */ + public boolean isLeEnabled() { + final int state = getLeState(); + if (state == BluetoothAdapter.STATE_ON) { + if (DBG) Log.d (TAG, "STATE_ON"); + } else if (state == BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Log.d (TAG, "STATE_BLE_ON"); + } else { + if (DBG) Log.d (TAG, "STATE_OFF"); + return false; + } + return true; + } + + /** + * Performs action based on user action to turn BT ON + * or OFF if BT is in BLE_ON state + */ + private void notifyUserAction(boolean enable) { + if (mService == null) { + Log.e(TAG, "mService is null"); + return; + } + + try { + if (enable) { + mService.onLeServiceUp(); //NA:TODO implementation pending + } else { + mService.onBrEdrDown(); //NA:TODO implementation pending + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + /** + * Returns true if LE only mode is enabled, that is apps + * have authorization to turn only BT ON and the calling + * app has privilage to do so + */ + private boolean isLEAlwaysOnEnabled() { + boolean ret = false; + if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) { + Log.v(TAG, "LE always on mode is enabled"); + // TODO: System API authorization check + ret = true; + } else { + Log.v(TAG, "LE always on mode is disabled"); + ret = false; + } + return ret; + } + + /** + * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). + * + *

        If the internal Adapter state is STATE_BLE_ON, this would trigger the transition + * to STATE_OFF and completely shut-down Bluetooth + * + *

        If the Adapter state is STATE_ON, This would unregister the existance of + * special Bluetooth LE application and hence the further turning off of Bluetooth + * from UI would ensure the complete turn-off of Bluetooth rather than staying back + * BLE only state + * + *

        This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} + * to be notified of subsequent adapter state changes If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time + * later transition to either {@link #STATE_BLE_ON} or {@link + * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications + * If this call returns false then there was an + * immediate problem that will prevent the QAdapter from being turned off - + * such as the QAadapter already being turned off. + * + * @return true to indicate success, or false on + * immediate error + * @hide + */ + public boolean disableBLE() { + if (isLEAlwaysOnEnabled() != true) return false; + + int state = getLeState(); + if (state == BluetoothAdapter.STATE_ON) { + if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); + try { + mManagerService.updateBleAppCount(mToken, false); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return true; + + } else if (state == BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Log.d (TAG, "STATE_BLE_ON"); + int bleAppCnt = 0; + try { + bleAppCnt = mManagerService.updateBleAppCount(mToken, false); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + if (bleAppCnt == 0) { + // Disable only if there are no other clients + notifyUserAction(false); + } + return true; + } + + if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); + return false; + } + + /** + * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would + * EnableBLE, EnableBLE brings-up Bluetooth so that application can access + * only LE related feature (Bluetooth GATT layers interfaces using the respective class) + * EnableBLE in turn registers the existance of a special App which wants to + * turn on Bluetooth Low enrgy part without making it visible at the settings UI + * as Bluetooth ON. + *

        Invoking EnableBLE when Bluetooth is already in ON state, would just registers + * the existance of special Application and doesn't do anything to current BT state. + * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth + * would stay in BLE_ON state so that LE features are still acessible to the special + * Applications. + * + *

        This is an asynchronous call: it will return immediately, and + * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} + * to be notified of subsequent adapter state changes. If this call returns + * true, then the adapter state will immediately transition from {@link + * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time + * later transition to either {@link #STATE_OFF} or {@link + * #STATE_BLE_ON}. If this call returns false then there was an + * immediate problem that will prevent the adapter from being turned on - + * such as Airplane mode, or the adapter is already turned on. + * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various + * states, It includes all the classic Bluetooth Adapter states along with + * internal BLE only states + * + * @return true to indicate Bluetooth LE start-up has begun, or false on + * immediate error + * @hide + */ + public boolean enableBLE() { + if (isLEAlwaysOnEnabled() != true) return false; + + if (isLeEnabled() == true) { + if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); + try { + mManagerService.updateBleAppCount(mToken, true); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return true; + } + + try { + if (DBG) Log.d(TAG, "Calling enableBLE"); + mManagerService.updateBleAppCount(mToken, true); + return mManagerService.enable(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + + return false; + } + /** * Get the current state of the local Bluetooth adapter. *

        Possible return values are @@ -556,6 +785,13 @@ public final class BluetoothAdapter { { int state= mService.getState(); if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + //consider all internal states as OFF + if (state == BluetoothAdapter.STATE_BLE_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { + if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + state = BluetoothAdapter.STATE_OFF; + } return state; } // TODO(BT) there might be a small gap during STATE_TURNING_ON that @@ -566,6 +802,49 @@ public final class BluetoothAdapter { return STATE_OFF; } + /** + * Get the current state of the local Bluetooth adapter + *

        This returns current internal state of Adapter including LE ON/OFF + * + *

        Possible return values are + * {@link #STATE_OFF}, + * {@link #STATE_BLE_TURNING_ON}, + * {@link #STATE_BLE_ON}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + * {@link #STATE_BLE_TURNING_OFF}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return current state of Bluetooth adapter + * @hide + */ + public int getLeState() { + try { + synchronized(mManagerCallback) { + if (mService != null) + { + int state= mService.getState(); + if (VDBG) Log.d(TAG,"getLeState() returning " + state); + return state; + } + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return BluetoothAdapter.STATE_OFF; + } + + boolean getLeAccess() { + if(getLeState() == STATE_ON) + return true; + + else if (getLeState() == STATE_BLE_ON) + return true; // TODO: FILTER SYSTEM APPS HERE <-- + + return false; + } + /** * Turn on the local Bluetooth adapter—do not use without explicit * user action to turn on Bluetooth. @@ -594,10 +873,23 @@ public final class BluetoothAdapter { * immediate error */ public boolean enable() { + int state = STATE_OFF; if (isEnabled() == true){ if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } + //Use service interface to get the exact state + if (mService != null) { + try { + state = mService.getState(); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + + if (state == BluetoothAdapter.STATE_BLE_ON) { + Log.e(TAG, "BT is in BLE_ON State"); + notifyUserAction(true); + return true; + } try { return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -1558,6 +1850,10 @@ public final class BluetoothAdapter { } } } + + public void onBrEdrDown() { + if (VDBG) Log.i(TAG, "on QBrEdrDown: "); + } }; /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1fdf9f4df13..1e82c032bf0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -606,7 +606,9 @@ public final class BluetoothDevice implements Parcelable { public void onBluetoothServiceUp(IBluetooth bluetoothService) throws RemoteException { synchronized (BluetoothDevice.class) { - sService = bluetoothService; + if (sService == null) { + sService = bluetoothService; + } } } @@ -616,6 +618,11 @@ public final class BluetoothDevice implements Parcelable { sService = null; } } + + public void onBrEdrDown() + { + if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); + } }; /** * Create a new BluetoothDevice @@ -1030,7 +1037,7 @@ public final class BluetoothDevice implements Parcelable { * or null on error */ public ParcelUuid[] getUuids() { - if (sService == null) { + if (sService == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); return null; } @@ -1057,7 +1064,7 @@ public final class BluetoothDevice implements Parcelable { */ public boolean fetchUuidsWithSdp() { IBluetooth service = sService; - if (service == null) { + if (service == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } @@ -1099,16 +1106,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - public int getServiceChannel(ParcelUuid uuid) { - //TODO(BT) - /* - try { - return sService.getRemoteServiceChannel(this, uuid); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return BluetoothDevice.ERROR; - } - /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. @@ -1187,6 +1184,15 @@ public final class BluetoothDevice implements Parcelable { return false; } + boolean isBluetoothEnabled() { + boolean ret = false; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.isEnabled() == true) { + ret = true; + } + return ret; + } + /** * Requires {@link android.Manifest.permission#BLUETOOTH}. * @return Whether the phonebook access is allowed to this device. Can be @@ -1289,6 +1295,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createRfcommSocket(int channel) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, null); } @@ -1355,6 +1365,11 @@ public final class BluetoothDevice implements Parcelable { * insufficient permissions */ public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } + return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, new ParcelUuid(uuid)); } @@ -1388,6 +1403,10 @@ public final class BluetoothDevice implements Parcelable { * insufficient permissions */ public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, new ParcelUuid(uuid)); } @@ -1407,6 +1426,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { + + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, null); } @@ -1422,6 +1446,11 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createScoSocket() throws IOException { + + if (isBluetoothEnabled() == false) { + Log.e(TAG, "Bluetooth is not enabled"); + throw new IOException(); + } return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index af560df8e2f..90df19828f6 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -102,4 +102,6 @@ interface IBluetooth // for dumpsys support String dump(); + void onLeServiceUp(); + void onBrEdrDown(); } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 7070baebcf7..4ca57f8ecca 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -101,4 +101,6 @@ interface IBluetoothGatt { in int srvcInstanceId, in ParcelUuid srvcId, in int charInstanceId, in ParcelUuid charId, in boolean confirm, in byte[] value); + void disconnectAll(); + void unregAll(); } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 7411d3f27b5..8d1ce990a56 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -44,4 +44,6 @@ interface IBluetoothManager String getAddress(); String getName(); + int updateBleAppCount(IBinder b, boolean enable); + boolean isBleAppPresent(); } diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl index 9551086a1de..1385dafca2f 100644 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -26,4 +26,5 @@ import android.bluetooth.IBluetooth; interface IBluetoothManagerCallback { void onBluetoothServiceUp(in IBluetooth bluetoothService); void onBluetoothServiceDown(); -} \ No newline at end of file + void onBrEdrDown(); +} diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java index 4916bd9cebf..c40256b8906 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -132,7 +132,7 @@ public class BluetoothLeUtils { * {@link BluetoothAdapter#STATE_ON}. */ static void checkAdapterStateOn(BluetoothAdapter adapter) { - if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) { + if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); } } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ef51ad62c11..3e5eee86a77 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -112,6 +112,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTHGATT = 2; private final Context mContext; + private static int mBleAppCount = 0; // Locks are not provided for mName and mAddress. // They are accessed in handler or broadcast receiver, same thread context. @@ -184,11 +185,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } } + + int st = BluetoothAdapter.STATE_OFF; + if (mBluetooth != null) { + try { + st = mBluetooth.getState(); + } catch (RemoteException e) { + Log.e(TAG,"Unable to call getState", e); + } + } + Log.d(TAG, "state" + st); + if (isAirplaneModeOn()) { - // disable without persisting the setting - sendDisableMsg(); + // Clear registered LE apps to force shut-off + synchronized (this) { + mBleAppCount = 0; + } + if (st == BluetoothAdapter.STATE_BLE_ON) { + //if state is BLE_ON make sure you trigger disableBLE part + try { + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + mEnableExternal = false; + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } else if (st == BluetoothAdapter.STATE_ON){ + // disable without persisting the setting + Log.d(TAG, "Calling disable"); + sendDisableMsg(); + } } else if (mEnableExternal) { // enable without persisting the setting + Log.d(TAG, "Calling enable"); sendEnableMsg(mQuietEnableExternal); } } @@ -203,12 +233,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } } - - if (!isNameAndAddressSet()) { - //Sync the Bluetooth name and address from the Bluetooth Adapter - if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); - getNameAndAddress(); - } } } }; @@ -218,6 +242,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; mBluetooth = null; + mBluetoothGatt = null; mBinding = false; mUnbinding = false; mEnable = false; @@ -396,6 +421,133 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + class ClientDeathRecipient implements IBinder.DeathRecipient { + public void binderDied() { + if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App"); + if (mBleAppCount > 0) --mBleAppCount; + + if (mBleAppCount == 0) { + if (DBG) Log.d(TAG, "Disabling LE only mode after application crash"); + try { + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } + } + } + + /** Internal death rec list */ + Map mBleApps = new HashMap(); + + public int updateBleAppCount(IBinder token, boolean enable) { + if (enable) { + ClientDeathRecipient r = mBleApps.get(token); + if (r == null) { + ClientDeathRecipient deathRec = new ClientDeathRecipient(); + try { + token.linkToDeath(deathRec, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + mBleApps.put(token, deathRec); + synchronized (this) { + ++mBleAppCount; + } + if (DBG) Log.d(TAG, "Registered for death Notification"); + } + + } else { + ClientDeathRecipient r = mBleApps.get(token); + if (r != null) { + try { + token.linkToDeath(r, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("Wake lock is already dead."); + } + mBleApps.remove(token); + synchronized (this) { + if (mBleAppCount > 0) --mBleAppCount; + } + if (DBG) Log.d(TAG, "Unregistered for death Notification"); + } + } + if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount); + if (mBleAppCount == 0 && mEnable) { + try { + if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { + if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); + mEnable = false; + } + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + } + } + return mBleAppCount; + } + + /** @hide*/ + public boolean isBleAppPresent() { + if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount); + return (mBleAppCount > 0); + } + + /** + * Action taken when GattService is turned off + */ + private void onBluetoothGattServiceUp() { + if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); + try{ + if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mBluetooth.onLeServiceUp(); + + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + Binder.restoreCallingIdentity(callingIdentity); + } + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onServiceUp", e); + } + } + + /** + * Inform BluetoothAdapter instances that BREDR part is down + * and turn off all service and stack if no LE app needs it + */ + private void sendBrEdrDownCallback() { + if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); + int n = mCallbacks.beginBroadcast(); + + if (isBleAppPresent() == false) { + try { + mBluetooth.onBrEdrDown(); + } catch(RemoteException e) { + Log.e(TAG,"Unable to call onBrEdrDown", e); + } + } + else{//need to stay at BLE ON. disconnect all Gatt connections + try{ + mBluetoothGatt.unregAll();//disconnectAll(); + } catch(RemoteException e) { + Log.e(TAG,"Unable to disconn all", e); + } + } + + Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers."); + for (int i=0; i " + newState); + // Send broadcast message to everyone else + Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } + private void bluetoothStateChangeHandler(int prevState, int newState) { + boolean isStandardBroadcast = true; if (prevState != newState) { //Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) { - boolean isUp = (newState==BluetoothAdapter.STATE_ON); - sendBluetoothStateCallback(isUp); - - if (isUp) { - // connect to GattService - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT); - } - } else { - //If Bluetooth is off, send service down event to proxy objects, and unbind - if (!isUp && canUnbindBluetoothService()) { - unbindAllBluetoothProfileServices(); + if (newState == BluetoothAdapter.STATE_BLE_ON + || newState == BluetoothAdapter.STATE_OFF) { + boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF + && newState == BluetoothAdapter.STATE_BLE_ON); + + if (newState == BluetoothAdapter.STATE_OFF) { + // If Bluetooth is off, send service down event to proxy objects, and unbind + if (DBG) Log.d(TAG, "Bluetooth is complete turn off"); + if (canUnbindBluetoothService()) { + if (DBG) Log.d(TAG, "Good to unbind!"); sendBluetoothServiceDownCallback(); unbindAndFinish(); + sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; } + + } else if (!intermediate_off) { + // connect to GattService + if (DBG) Log.d(TAG, "Bluetooth is in LE only mode"); + if (mBluetoothGatt != null) { + if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp"); + onBluetoothGattServiceUp(); + } else { + if (DBG) Log.d(TAG, "Binding Bluetooth GATT service"); + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH_LE)) { + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); + } + } + sendBleStateChanged(prevState, newState); + //Don't broadcase this as std intent + isStandardBroadcast = false; + + } else if (intermediate_off){ + if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode"); + // For LE only mode, broadcast as is + sendBleStateChanged(prevState, newState); + sendBluetoothStateCallback(false); // BT is OFF for general users + // Broadcast as STATE_OFF + newState = BluetoothAdapter.STATE_OFF; + sendBrEdrDownCallback(); } + } else if (newState == BluetoothAdapter.STATE_ON) { + boolean isUp = (newState==BluetoothAdapter.STATE_ON); + sendBluetoothStateCallback(isUp); + sendBleStateChanged(prevState, newState); + + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON + || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + sendBleStateChanged(prevState, newState); + isStandardBroadcast = false; + + } else if (newState == BluetoothAdapter.STATE_TURNING_ON + || newState == BluetoothAdapter.STATE_TURNING_OFF) { + sendBleStateChanged(prevState, newState); } - //Send broadcast message to everyone else - Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, - BLUETOOTH_PERM); + if (isStandardBroadcast) { + if (prevState == BluetoothAdapter.STATE_BLE_ON) { + // Show prevState of BLE_ON as OFF to standard users + prevState = BluetoothAdapter.STATE_OFF; + } + Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } } } -- GitLab From d8355fbc2eebadece0ee3beae25361be32d9ee34 Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Thu, 19 Mar 2015 10:36:45 +0100 Subject: [PATCH 0448/1408] Add support for Bluetooth Sim Access Profile (2/4) Change-Id: I6c634aa38d31a7b5a98c9089840557257fd58209 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothDevice.java | 41 ++ .../android/bluetooth/BluetoothProfile.java | 10 +- .../java/android/bluetooth/BluetoothSap.java | 389 ++++++++++++++++++ .../java/android/bluetooth/BluetoothUuid.java | 9 +- .../java/android/bluetooth/IBluetooth.aidl | 2 + .../java/android/bluetooth/IBluetoothSap.aidl | 37 ++ 7 files changed, 491 insertions(+), 4 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothSap.java create mode 100644 framework/java/android/bluetooth/IBluetoothSap.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index dd030f8538c..10ee21d2fd1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1743,6 +1743,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEADSET_CLIENT) { BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); return true; + } else if (profile == BluetoothProfile.SAP) { + BluetoothSap sap = new BluetoothSap(context, listener); + return true; } else { return false; } @@ -1807,6 +1810,10 @@ public final class BluetoothAdapter { BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy; headsetClient.close(); break; + case BluetoothProfile.SAP: + BluetoothSap sap = (BluetoothSap)proxy; + sap.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1e82c032bf0..bfc374fb913 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -382,6 +382,9 @@ public final class BluetoothDevice implements Parcelable { /**@hide*/ public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; + /**@hide*/ + public static final int REQUEST_TYPE_SIM_ACCESS = 4; + /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, * Contains package name to return reply intent to. @@ -1269,6 +1272,44 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Requires {@link android.Manifest.permission#BLUETOOTH}. + * @return Whether the Sim access is allowed to this device. Can be + * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @hide + */ + public int getSimAccessPermission() { + if (sService == null) { + return ACCESS_UNKNOWN; + } + try { + return sService.getSimAccessPermission(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return ACCESS_UNKNOWN; + } + + /** + * Sets whether the Sim access is allowed to this device. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or + * {@link #ACCESS_REJECTED}. + * @return Whether the value has been successfully set. + * @hide + */ + public boolean setSimAccessPermission(int value) { + if (sService == null) { + return false; + } + try { + return sService.setSimAccessPermission(this, value); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 136740505a4..eecb0739657 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -103,17 +103,23 @@ public interface BluetoothProfile { */ public static final int MAP = 9; + /* + * SAP Profile + * @hide + */ + public static final int SAP = 10; + /** * A2DP Sink Profile * @hide */ - public static final int A2DP_SINK = 10; + public static final int A2DP_SINK = 11; /** * AVRCP Controller Profile * @hide */ - public static final int AVRCP_CONTROLLER = 11; + public static final int AVRCP_CONTROLLER = 12; /** * Headset Client - HFP HF Role diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java new file mode 100644 index 00000000000..60528849a01 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2008 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 java.util.ArrayList; +import java.util.List; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + + +public final class BluetoothSap implements BluetoothProfile { + + private static final String TAG = "BluetoothSap"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; + + private IBluetoothSap 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 BluetoothSap proxy object. + */ + /*package*/ BluetoothSap(Context context, ServiceListener l) { + if (DBG) Log.d(TAG, "Create BluetoothSap 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(IBluetoothMap.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 SAP 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 BluetoothSap will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized 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; + } + + /** + * Get the current state of the BluetoothSap service. + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy + * object is currently not connected to the Sap service. + */ + public int getState() { + if (VDBG) log("getState()"); + if (mService != null) { + try { + return mService.getState(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return BluetoothSap.STATE_ERROR; + } + + /** + * Get the currently connected remote Bluetooth device (PCE). + * @return The remote Bluetooth device, or null if not in connected or + * connecting state, or if this proxy object is not connected to + * the Sap service. + */ + public BluetoothDevice getClient() { + if (VDBG) log("getClient()"); + if (mService != null) { + try { + return mService.getClient(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } + return 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 Sap service. + */ + public boolean isConnected(BluetoothDevice device) { + if (VDBG) log("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(Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate connection. Initiation of outgoing connections is not + * supported for SAP server. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); + return false; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, + * true otherwise + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } 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 list of connected devices. Currently at most one. + * + * @return list of connected devices + */ + public List getConnectedDevices() { + if (DBG) log("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 + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("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 + */ + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("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 + * @param priority + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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("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; + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) log("Proxy object connected"); + mService = IBluetoothSap.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) log("Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) + return true; + log("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; + } + +} diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 194a53e0f6c..2ded4c8fea3 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -76,7 +76,9 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); - + public static final ParcelUuid SAP = + ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); @@ -89,7 +91,7 @@ public final class BluetoothUuid { public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS, MAS}; + ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -143,6 +145,9 @@ public final class BluetoothUuid { public static boolean isMas(ParcelUuid uuid) { return uuid.equals(MAS); } + public static boolean isSap(ParcelUuid uuid) { + return uuid.equals(SAP); + } /** * Returns true if ParcelUuid is present in uuidArray diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 90df19828f6..f6001bfffed 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -79,6 +79,8 @@ interface IBluetooth boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); int getMessageAccessPermission(in BluetoothDevice device); boolean setMessageAccessPermission(in BluetoothDevice device, int value); + int getSimAccessPermission(in BluetoothDevice device); + boolean setSimAccessPermission(in BluetoothDevice device, int value); void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); diff --git a/framework/java/android/bluetooth/IBluetoothSap.aidl b/framework/java/android/bluetooth/IBluetoothSap.aidl new file mode 100644 index 00000000000..8970639467c --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothSap.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * System private API for Bluetooth SAP service + * + * {@hide} + */ +interface IBluetoothSap { + int getState(); + BluetoothDevice getClient(); + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); +} -- GitLab From ed9c9583f3e5249c77973f22d7387847f601bfb3 Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Wed, 15 Apr 2015 11:55:20 -0700 Subject: [PATCH 0449/1408] Fix make update-api Change-Id: Iaee002c64096fbd6ba05d6484cc50840146a4af0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ------ framework/java/android/bluetooth/BluetoothSap.java | 6 ------ 2 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 10ee21d2fd1..2418e824259 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -121,9 +121,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; @@ -134,9 +131,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 60528849a01..7b4c6f923ff 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -296,8 +296,6 @@ public final class BluetoothSap 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}, * * @param device Paired bluetooth device * @param priority @@ -325,10 +323,6 @@ public final class BluetoothSap 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} - * * @param device Bluetooth device * @return priority of the device */ -- GitLab From 7968421c32372cc12019c16d533b4b8ac1f38698 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 9 Apr 2015 17:14:50 -0700 Subject: [PATCH 0450/1408] Manage onfound/lost resources (1/2) Provide error callback to app if advertisement tracking resources can't be reserved Change-Id: Ie66b2ec7a64b24bbdf3bb22003a4a7eb46623792 --- .../java/android/bluetooth/BluetoothAdapter.java | 7 +++++-- .../bluetooth/BluetoothGattCallbackWrapper.java | 3 +++ framework/java/android/bluetooth/IBluetooth.aidl | 1 - framework/java/android/bluetooth/IBluetoothGatt.aidl | 1 + .../android/bluetooth/IBluetoothGattCallback.aidl | 1 + .../android/bluetooth/le/BluetoothLeScanner.java | 12 ++++++++++++ 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 875aef6264f..3efbc2da377 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1305,9 +1305,12 @@ public final class BluetoothAdapter { public boolean isHardwareTrackingFiltersAvailable() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0); + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; } + return (iGatt.numHwTrackFiltersAvailable() != 0); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index cdb24f40e2d..01778b3dc40 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -123,4 +123,7 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException { } + @Override + public void onScanManagerErrorCallback(int errorCode) throws RemoteException { + } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index f6001bfffed..a3eceb5efd9 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -100,7 +100,6 @@ interface IBluetooth boolean isActivityAndEnergyReportingSupported(); void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - int numOfHwTrackFiltersAvailable(); // for dumpsys support String dump(); diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 4ca57f8ecca..72abeaf699c 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -103,4 +103,5 @@ interface IBluetoothGatt { in boolean confirm, in byte[] value); void disconnectAll(); void unregAll(); + int numHwTrackFiltersAvailable(); } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 91e62ea65c7..cbba9f02142 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -67,6 +67,7 @@ oneway interface IBluetoothGattCallback { void onReadRemoteRssi(in String address, in int rssi, in int status); void onMultiAdvertiseCallback(in int status, boolean isStart, in AdvertiseSettings advertiseSettings); + void onScanManagerErrorCallback(in int errorCode); void onConfigureMTU(in String address, in int mtu, in int status); void onFoundOrLost(in boolean onFound, in ScanResult scanResult); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index e184d1e2726..9c3f3f1add3 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -381,6 +381,18 @@ public final class BluetoothLeScanner { } }); } + + @Override + public void onScanManagerErrorCallback(final int errorCode) { + if (VDBG) { + Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); + } + synchronized (this) { + if (mClientIf <= 0) + return; + } + postCallbackError(mScanCallback, errorCode); + } } private void postCallbackError(final ScanCallback callback, final int errorCode) { -- GitLab From b0ea4d61bb8ae9f00a15489b75225a482456c3de Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 22 Apr 2015 11:11:43 -0700 Subject: [PATCH 0451/1408] Make onlost/onfound APIs public Change-Id: I455124478d953d652f216f71d5813782fc9633ab --- framework/java/android/bluetooth/le/ScanCallback.java | 6 ++++-- framework/java/android/bluetooth/le/ScanSettings.java | 11 ----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 27b96bd6e43..61b2e787c86 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -53,8 +53,10 @@ public abstract class ScanCallback { /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines how this callback was triggered. Could be of - * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES} + * @param callbackType Determines how this callback was triggered. Could be one of + * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, + * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or + * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index f103cae5c25..31d7963cd01 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -59,17 +59,13 @@ public final class ScanSettings implements Parcelable { /** * A result callback is only triggered for the first advertisement packet received that matches * the filter criteria. - * @hide */ - @SystemApi public static final int CALLBACK_TYPE_FIRST_MATCH = 2; /** * Receive a callback when advertisements are no longer received from a device that has been * previously reported by a first match callback. - * @hide */ - @SystemApi public static final int CALLBACK_TYPE_MATCH_LOST = 4; @@ -78,21 +74,18 @@ public final class ScanSettings implements Parcelable { */ /** * Match one advertisement per filter - * @hide */ public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; /** * Match few advertisement per filter, depends on current capability and availibility of * the resources in hw - * @hide */ public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; /** * Match as many advertisement per filter as hw could allow, depends on current * capability and availibility of the resources in hw - * @hide */ public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; @@ -100,14 +93,12 @@ public final class ScanSettings implements Parcelable { /** * In Aggressive mode, hw will determine a match sooner even with feeble signal strength * and few number of sightings/match in a duration. - * @hide */ public static final int MATCH_MODE_AGGRESSIVE = 1; /** * For sticky mode, higher threshold of signal strength and sightings is required * before reporting by hw - * @hide */ public static final int MATCH_MODE_STICKY = 2; @@ -324,7 +315,6 @@ public final class ScanSettings implements Parcelable { * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} * @throws IllegalArgumentException If the {@code matchMode} is invalid. - * @hide */ public Builder setNumOfMatches(int numOfMatches) { if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT @@ -342,7 +332,6 @@ public final class ScanSettings implements Parcelable { * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or * {@link ScanSettings#MATCH_MODE_STICKY} * @throws IllegalArgumentException If the {@code matchMode} is invalid. - * @hide */ public Builder setMatchMode(int matchMode) { if (matchMode < MATCH_MODE_AGGRESSIVE -- GitLab From a807da9bb4b59eb36503728100b168f02c8ef63a Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Tue, 10 Mar 2015 20:55:31 -0700 Subject: [PATCH 0452/1408] Annotate some APIs with threading annotations Change-Id: I24bda29261cdecbe78b2529866624b9313ca5c03 --- framework/java/android/bluetooth/BluetoothHealthCallback.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index baf2adee849..128376f24f3 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -17,6 +17,7 @@ package android.bluetooth; +import android.annotation.BinderThread; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -39,6 +40,7 @@ public abstract class BluetoothHealthCallback { * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} */ + @BinderThread public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, int status) { Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); @@ -58,6 +60,7 @@ public abstract class BluetoothHealthCallback { * @param channelId The id associated with the channel. This id will be used * in future calls like when disconnecting the channel. */ + @BinderThread public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, int channelId) { -- GitLab From ea513c5d27bae3008d787bc42c6ad8ab6879b9e9 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Thu, 23 Apr 2015 14:44:50 -0700 Subject: [PATCH 0453/1408] Reduce log spam in BluetoothAdapter for getState. Change-Id: I38995fc55f12b000d58a769ed67aff623865169b --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2418e824259..79e560ff9f4 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -792,7 +792,6 @@ public final class BluetoothAdapter { // mService is null, handle that case } } catch (RemoteException e) {Log.e(TAG, "", e);} - if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF"); return STATE_OFF; } -- GitLab From 017ad5c0adcba3afbdd2dee256f9013a8b3c610e Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Thu, 23 Apr 2015 18:25:08 -0700 Subject: [PATCH 0454/1408] Bluetooth document fix: remove reference from open API to hidden entities Change-Id: I183ae1083727d2e2b96aa503d0e90d81ce3b1a82 --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 515be038d3a..d4e79be3240 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -133,10 +133,7 @@ public final class BluetoothAdapter { * {@link #STATE_OFF}, * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, + * {@link #STATE_TURNING_OFF} */ public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; -- GitLab From 0515c51a52886def674d2ced9edc0be258fead4c Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Fri, 24 Apr 2015 18:06:59 -0700 Subject: [PATCH 0455/1408] Fix documentation - non-public API. Change-Id: I418bf47b197936b2f50b231425312d5b6d272df8 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d4e79be3240..3044a9494a9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -121,9 +121,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_BLE_TURNING_OFF}, */ public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; -- GitLab From 20e41df181aa2e2eba6f9912bdce3aeeac70ce51 Mon Sep 17 00:00:00 2001 From: tturney Date: Wed, 29 Apr 2015 12:01:00 -0700 Subject: [PATCH 0456/1408] Fix typo. bug: b/20610710 Change-Id: I7b8e0331daddadcb31f45650f5cd937b1f4f90ff --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3044a9494a9..5c676a5a79a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -389,7 +389,7 @@ public final class BluetoothAdapter { * @hide */ public static final String ACTION_BLE_STATE_CHANGED = - "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED"; + "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be -- GitLab From 0924a0ddeec3758ed32239cbd6c3705c26d0d799 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 29 Apr 2015 11:14:18 -0700 Subject: [PATCH 0457/1408] Temp workaround to track max beacons. Change-Id: Ia65a7a0ec98d0181cf62db343e00c4a0b01c32d8 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index f103cae5c25..123514eb937 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -236,7 +236,7 @@ public final class ScanSettings implements Parcelable { private int mScanResultType = SCAN_RESULT_TYPE_FULL; private long mReportDelayMillis = 0; private int mMatchMode = MATCH_MODE_AGGRESSIVE; - private int mNumOfMatchesPerFilter = MATCH_NUM_ONE_ADVERTISEMENT; + private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; /** * Set scan mode for Bluetooth LE scan. * -- GitLab From 3acb7e832747b2a14dd7db79dfd47e6d6a5dd066 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Sun, 26 Apr 2015 17:04:29 -0700 Subject: [PATCH 0458/1408] Honor Ble scanning settings from Settings UI. Bug:20643039 Change-Id: Ib1465108e26b8537c9da1bfbb31a99d2e33da910 --- .../android/bluetooth/BluetoothAdapter.java | 31 ++++++------------- .../android/bluetooth/IBluetoothManager.aidl | 2 ++ .../bluetooth/BluetoothManagerService.java | 11 +++++++ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3044a9494a9..b63192043f5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -34,6 +34,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.app.ActivityThread; import android.os.SystemProperties; +import android.provider.Settings; import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -631,24 +632,6 @@ public final class BluetoothAdapter { } } - /** - * Returns true if LE only mode is enabled, that is apps - * have authorization to turn only BT ON and the calling - * app has privilage to do so - */ - private boolean isLEAlwaysOnEnabled() { - boolean ret = false; - if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) { - Log.v(TAG, "LE always on mode is enabled"); - // TODO: System API authorization check - ret = true; - } else { - Log.v(TAG, "LE always on mode is disabled"); - ret = false; - } - return ret; - } - /** * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). * @@ -676,7 +659,7 @@ public final class BluetoothAdapter { * @hide */ public boolean disableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); if (state == BluetoothAdapter.STATE_ON) { @@ -738,7 +721,7 @@ public final class BluetoothAdapter { * @hide */ public boolean enableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; if (isLeEnabled() == true) { if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); @@ -1243,8 +1226,12 @@ public final class BluetoothAdapter { */ @SystemApi public boolean isBleScanAlwaysAvailable() { - // TODO: implement after Settings UI change. - return false; + try { + return mManagerService.isBleScanAlwaysAvailable(); + } catch (RemoteException e) { + Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e); + return false; + } } /** diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 8d1ce990a56..0b81ee8c547 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -44,6 +44,8 @@ interface IBluetoothManager String getAddress(); String getName(); + + boolean isBleScanAlwaysAvailable(); int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ef82bb70cec..1019faa6d9c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -48,6 +48,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.io.FileDescriptor; @@ -443,6 +444,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** Internal death rec list */ Map mBleApps = new HashMap(); + @Override + public boolean isBleScanAlwaysAvailable() { + try { + return (Settings.Global.getInt(mContentResolver, + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; + } catch (SettingNotFoundException e) { + } + return false; + } + public int updateBleAppCount(IBinder token, boolean enable) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); -- GitLab From d12d369c2919e2fdb74c7f5ce803b45be71661e3 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 1 May 2015 00:50:32 -0700 Subject: [PATCH 0459/1408] Fix a bug in ScanSettings constructor. Change-Id: I36e6effe8063bacddd61fe2c47c0360d7ffaee71 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 123514eb937..dad486dd941 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -187,7 +187,7 @@ public final class ScanSettings implements Parcelable { mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; mNumOfMatchesPerFilter = numOfMatchesPerFilter; - mMatchMode = numOfMatchesPerFilter; + mMatchMode = matchMode; } private ScanSettings(Parcel in) { -- GitLab From 660bff7dedcab83606ca69560bf4bcd003e1c7e7 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 4 May 2015 13:48:50 -0700 Subject: [PATCH 0460/1408] Add documentation for BluetoothSocket.getConnectionType() Bug: 20824264 Change-Id: I58aa8f5bbe08ddb252f01f4ad187ae8741963f0b --- framework/java/android/bluetooth/BluetoothSocket.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 5702d117b66..5cf2300bef1 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -91,9 +91,13 @@ public final class BluetoothSocket implements Closeable { public static final int MAX_RFCOMM_CHANNEL = 30; /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF; - /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ + /** RFCOMM socket */ public static final int TYPE_RFCOMM = 1; + + /** SCO socket */ public static final int TYPE_SCO = 2; + + /** L2CAP socket */ public static final int TYPE_L2CAP = 3; /*package*/ static final int EBADFD = 77; @@ -578,8 +582,8 @@ public final class BluetoothSocket implements Closeable { } /** - * Get the type of the underlying connection - * @return one of TYPE_ + * Get the type of the underlying connection. + * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} */ public int getConnectionType() { return mType; -- GitLab From 35566b2b38c4925a8a1c5de8578cc3bfc6ee12bb Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 4 May 2015 13:28:04 -0700 Subject: [PATCH 0461/1408] BluetoothSap class should not be public Bug: 20823932 Change-Id: I87dfa10b994f7a14c123bb384925c08b34bfbe67 --- .../android/bluetooth/BluetoothAdapter.java | 3 +- .../java/android/bluetooth/BluetoothSap.java | 59 +++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9b4dcc6b346..0a778681b57 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1692,7 +1692,8 @@ public final class BluetoothAdapter { * @param context Context of the application * @param listener The service Listener for connection callbacks. * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, - * {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}. + * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. + * {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 7b4c6f923ff..014cb22c57b 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -28,13 +28,41 @@ import android.os.IBinder; import android.os.ServiceManager; import android.util.Log; - +/** + * This class provides the APIs to control the Bluetooth SIM + * Access Profile (SAP). + * + *

        BluetoothSap is a proxy object for controlling the Bluetooth + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothSap proxy object. + * + *

        Each method is protected with its appropriate permission. + * @hide + */ public final class BluetoothSap implements BluetoothProfile { private static final String TAG = "BluetoothSap"; private static final boolean DBG = true; private static final boolean VDBG = false; + /** + * Intent used to broadcast the change in connection state of the profile. + * + *

        This intent will have 4 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * @hide + */ public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; @@ -43,12 +71,22 @@ public final class BluetoothSap implements BluetoothProfile { private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; + /** + * There was an error trying to obtain the state. + * @hide + */ + public static final int STATE_ERROR = -1; - public static final int RESULT_FAILURE = 0; + /** + * Connection state change succceeded. + * @hide + */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ + + /** + * Connection canceled before completion. + * @hide + */ public static final int RESULT_CANCELED = 2; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -124,6 +162,7 @@ public final class BluetoothSap implements BluetoothProfile { * Other public functions of BluetoothSap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * @hide */ public synchronized void close() { IBluetoothManager mgr = mAdapter.getBluetoothManager(); @@ -152,6 +191,7 @@ public final class BluetoothSap implements BluetoothProfile { * Get the current state of the BluetoothSap service. * @return One of the STATE_ return codes, or STATE_ERROR if this proxy * object is currently not connected to the Sap service. + * @hide */ public int getState() { if (VDBG) log("getState()"); @@ -171,6 +211,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return The remote Bluetooth device, or null if not in connected or * connecting state, or if this proxy object is not connected to * the Sap service. + * @hide */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); @@ -189,6 +230,7 @@ public final class BluetoothSap 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 Sap service. + * @hide */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); @@ -206,6 +248,7 @@ public final class BluetoothSap implements BluetoothProfile { /** * Initiate connection. Initiation of outgoing connections is not * supported for SAP server. + * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); @@ -218,6 +261,7 @@ public final class BluetoothSap implements BluetoothProfile { * @param device Remote Bluetooth Device * @return false on error, * true otherwise + * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -238,6 +282,7 @@ public final class BluetoothSap implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * @hide */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); @@ -257,6 +302,7 @@ public final class BluetoothSap implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * @hide */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); @@ -276,6 +322,7 @@ public final class BluetoothSap implements BluetoothProfile { * Get connection state of device * * @return device connection state + * @hide */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); @@ -300,6 +347,7 @@ public final class BluetoothSap implements BluetoothProfile { * @param device Paired bluetooth device * @param priority * @return true if priority is set, false on error + * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); @@ -325,6 +373,7 @@ public final class BluetoothSap implements BluetoothProfile { * * @param device Bluetooth device * @return priority of the device + * @hide */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); -- GitLab From 91ffd4b503b38e713a5a4084424217bd24e7ef27 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 29 Apr 2015 11:14:18 -0700 Subject: [PATCH 0462/1408] [DO NOT MERGE] Temp workaround to track max beacons. Change-Id: Ia65a7a0ec98d0181cf62db343e00c4a0b01c32d8 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 31d7963cd01..abe3efd7741 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -227,7 +227,7 @@ public final class ScanSettings implements Parcelable { private int mScanResultType = SCAN_RESULT_TYPE_FULL; private long mReportDelayMillis = 0; private int mMatchMode = MATCH_MODE_AGGRESSIVE; - private int mNumOfMatchesPerFilter = MATCH_NUM_ONE_ADVERTISEMENT; + private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; /** * Set scan mode for Bluetooth LE scan. * -- GitLab From 7bd11574a5ba9a30a5cd47802c284dce56779349 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Wed, 22 Apr 2015 11:11:43 -0700 Subject: [PATCH 0463/1408] Make onlost/onfound APIs public Change-Id: I455124478d953d652f216f71d5813782fc9633ab --- framework/java/android/bluetooth/le/ScanCallback.java | 6 ++++-- framework/java/android/bluetooth/le/ScanSettings.java | 11 ----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 27b96bd6e43..61b2e787c86 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -53,8 +53,10 @@ public abstract class ScanCallback { /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines how this callback was triggered. Could be of - * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES} + * @param callbackType Determines how this callback was triggered. Could be one of + * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, + * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or + * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index dad486dd941..4eeb577a1de 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -59,17 +59,13 @@ public final class ScanSettings implements Parcelable { /** * A result callback is only triggered for the first advertisement packet received that matches * the filter criteria. - * @hide */ - @SystemApi public static final int CALLBACK_TYPE_FIRST_MATCH = 2; /** * Receive a callback when advertisements are no longer received from a device that has been * previously reported by a first match callback. - * @hide */ - @SystemApi public static final int CALLBACK_TYPE_MATCH_LOST = 4; @@ -78,21 +74,18 @@ public final class ScanSettings implements Parcelable { */ /** * Match one advertisement per filter - * @hide */ public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; /** * Match few advertisement per filter, depends on current capability and availibility of * the resources in hw - * @hide */ public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; /** * Match as many advertisement per filter as hw could allow, depends on current * capability and availibility of the resources in hw - * @hide */ public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; @@ -100,14 +93,12 @@ public final class ScanSettings implements Parcelable { /** * In Aggressive mode, hw will determine a match sooner even with feeble signal strength * and few number of sightings/match in a duration. - * @hide */ public static final int MATCH_MODE_AGGRESSIVE = 1; /** * For sticky mode, higher threshold of signal strength and sightings is required * before reporting by hw - * @hide */ public static final int MATCH_MODE_STICKY = 2; @@ -324,7 +315,6 @@ public final class ScanSettings implements Parcelable { * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} * @throws IllegalArgumentException If the {@code matchMode} is invalid. - * @hide */ public Builder setNumOfMatches(int numOfMatches) { if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT @@ -342,7 +332,6 @@ public final class ScanSettings implements Parcelable { * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or * {@link ScanSettings#MATCH_MODE_STICKY} * @throws IllegalArgumentException If the {@code matchMode} is invalid. - * @hide */ public Builder setMatchMode(int matchMode) { if (matchMode < MATCH_MODE_AGGRESSIVE -- GitLab From 32a64b3496874d0cea600f51b0d61185d72358da Mon Sep 17 00:00:00 2001 From: Nitin Arora Date: Wed, 29 Apr 2015 12:35:03 -0700 Subject: [PATCH 0464/1408] DO NOT MERGE Bluetooth: Ensure Bluetooth interface handle is valid This change adds null checks to Bluetooth interface handle to prevent using null references when BluetoothService is not up. Also removed the callbacks for the intermediate state removed for now as they are not being used. Change-Id: I4c4a406c99a51e0759d9163f7d658d64dee11ad9 --- .../bluetooth/BluetoothManagerService.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 46a4599dad3..5d353b6372f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -503,7 +503,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void onBluetoothGattServiceUp() { if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); try{ - if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + if (isBleAppPresent() == false && mBluetooth != null + && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { mBluetooth.onLeServiceUp(); // waive WRITE_SECURE_SETTINGS permission check @@ -522,32 +523,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void sendBrEdrDownCallback() { if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); - int n = mCallbacks.beginBroadcast(); + + if(mBluetooth == null) { + Log.w(TAG, "Bluetooth handle is null"); + return; + } if (isBleAppPresent() == false) { try { mBluetooth.onBrEdrDown(); } catch(RemoteException e) { - Log.e(TAG,"Unable to call onBrEdrDown", e); + Log.e(TAG, "Call to onBrEdrDown() failed.", e); } - } - else{//need to stay at BLE ON. disconnect all Gatt connections + } else { + // Need to stay at BLE ON. Disconnect all Gatt connections try{ - mBluetoothGatt.unregAll();//disconnectAll(); + mBluetoothGatt.unregAll(); } catch(RemoteException e) { - Log.e(TAG,"Unable to disconn all", e); - } - } - - Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers."); - for (int i=0; i Date: Wed, 29 Apr 2015 12:35:03 -0700 Subject: [PATCH 0465/1408] Bluetooth: Ensure Bluetooth interface handle is valid This change adds null checks to Bluetooth interface handle to prevent using null references when BluetoothService is not up. Also removed the callbacks for the intermediate state removed for now as they are not being used. Change-Id: I0e72ff4da467a8bcf5a4e5ac48d8558e7f308c7e --- .../bluetooth/BluetoothManagerService.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1019faa6d9c..32b91d25fb1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -512,7 +512,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void onBluetoothGattServiceUp() { if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); try{ - if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + if (isBleAppPresent() == false && mBluetooth != null + && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { mBluetooth.onLeServiceUp(); // waive WRITE_SECURE_SETTINGS permission check @@ -531,32 +532,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void sendBrEdrDownCallback() { if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); - int n = mCallbacks.beginBroadcast(); + + if(mBluetooth == null) { + Log.w(TAG, "Bluetooth handle is null"); + return; + } if (isBleAppPresent() == false) { try { mBluetooth.onBrEdrDown(); } catch(RemoteException e) { - Log.e(TAG,"Unable to call onBrEdrDown", e); + Log.e(TAG, "Call to onBrEdrDown() failed.", e); } - } - else{//need to stay at BLE ON. disconnect all Gatt connections + } else { + // Need to stay at BLE ON. Disconnect all Gatt connections try{ - mBluetoothGatt.unregAll();//disconnectAll(); + mBluetoothGatt.unregAll(); } catch(RemoteException e) { - Log.e(TAG,"Unable to disconn all", e); - } - } - - Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers."); - for (int i=0; i Date: Thu, 7 May 2015 16:25:33 -0700 Subject: [PATCH 0466/1408] Allow obtaining BLE capabilities in BLE scan only mode. Bug:20923734 Change-Id: I7f3e5fd4410da39922be4512e4ba6ccdeb32887d --- .../java/android/bluetooth/BluetoothAdapter.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0a778681b57..ec6f18d217e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -28,14 +28,11 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; -import android.app.ActivityThread; -import android.os.SystemProperties; -import android.provider.Settings; -import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -1255,7 +1252,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { @@ -1270,7 +1267,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { @@ -1286,7 +1283,7 @@ public final class BluetoothAdapter { * @hide */ public boolean isHardwareTrackingFiltersAvailable() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { -- GitLab From e94cb7b08530582d2fb9b7447b0afa0f2a5c132d Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 7 May 2015 16:25:33 -0700 Subject: [PATCH 0467/1408] Allow obtaining BLE capabilities in BLE scan only mode. Bug:20923734 Change-Id: I7f3e5fd4410da39922be4512e4ba6ccdeb32887d --- .../java/android/bluetooth/BluetoothAdapter.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0a778681b57..ec6f18d217e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -28,14 +28,11 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; -import android.app.ActivityThread; -import android.os.SystemProperties; -import android.provider.Settings; -import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -1255,7 +1252,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { @@ -1270,7 +1267,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { @@ -1286,7 +1283,7 @@ public final class BluetoothAdapter { * @hide */ public boolean isHardwareTrackingFiltersAvailable() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { -- GitLab From a3646b2a6a69f254b29d39f0e85d4e564538ed19 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Sun, 26 Apr 2015 17:04:29 -0700 Subject: [PATCH 0468/1408] [DO NOT MERGE] Honor Ble scanning settings from Settings UI. Bug:20643039 Change-Id: Ib1465108e26b8537c9da1bfbb31a99d2e33da910 --- .../android/bluetooth/BluetoothAdapter.java | 31 ++++++------------- .../android/bluetooth/IBluetoothManager.aidl | 2 ++ .../bluetooth/BluetoothManagerService.java | 11 +++++++ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3efbc2da377..b2519ca700e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,6 +36,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.app.ActivityThread; import android.os.SystemProperties; +import android.provider.Settings; import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -634,24 +635,6 @@ public final class BluetoothAdapter { } } - /** - * Returns true if LE only mode is enabled, that is apps - * have authorization to turn only BT ON and the calling - * app has privilage to do so - */ - private boolean isLEAlwaysOnEnabled() { - boolean ret = false; - if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) { - Log.v(TAG, "LE always on mode is enabled"); - // TODO: System API authorization check - ret = true; - } else { - Log.v(TAG, "LE always on mode is disabled"); - ret = false; - } - return ret; - } - /** * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). * @@ -679,7 +662,7 @@ public final class BluetoothAdapter { * @hide */ public boolean disableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); if (state == BluetoothAdapter.STATE_ON) { @@ -741,7 +724,7 @@ public final class BluetoothAdapter { * @hide */ public boolean enableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; if (isLeEnabled() == true) { if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); @@ -1247,8 +1230,12 @@ public final class BluetoothAdapter { */ @SystemApi public boolean isBleScanAlwaysAvailable() { - // TODO: implement after Settings UI change. - return false; + try { + return mManagerService.isBleScanAlwaysAvailable(); + } catch (RemoteException e) { + Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e); + return false; + } } /** diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 8d1ce990a56..0b81ee8c547 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -44,6 +44,8 @@ interface IBluetoothManager String getAddress(); String getName(); + + boolean isBleScanAlwaysAvailable(); int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5d353b6372f..2cc5ff0601d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -48,6 +48,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.io.FileDescriptor; @@ -445,6 +446,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** Internal death rec list */ Map mBleApps = new HashMap(); + @Override + public boolean isBleScanAlwaysAvailable() { + try { + return (Settings.Global.getInt(mContentResolver, + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; + } catch (SettingNotFoundException e) { + } + return false; + } + public int updateBleAppCount(IBinder token, boolean enable) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); -- GitLab From 6330585542a02a4e036c3c53113f576a2d856076 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 1 May 2015 00:50:32 -0700 Subject: [PATCH 0469/1408] [DO NOT MERGE] Fix a bug in ScanSettings constructor. Change-Id: I36e6effe8063bacddd61fe2c47c0360d7ffaee71 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index abe3efd7741..4eeb577a1de 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -178,7 +178,7 @@ public final class ScanSettings implements Parcelable { mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; mNumOfMatchesPerFilter = numOfMatchesPerFilter; - mMatchMode = numOfMatchesPerFilter; + mMatchMode = matchMode; } private ScanSettings(Parcel in) { -- GitLab From 038eab23e3c2350e6c9d3eda2904edd6e1767022 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 5 Dec 2014 09:31:30 -0800 Subject: [PATCH 0470/1408] Bluetooth native dumpsys logging support (3/5) Bug: 18508263 Change-Id: I88f9c90dab8b0c825010c8617709449a3dd704b2 --- .../java/android/bluetooth/IBluetooth.aidl | 4 +-- .../bluetooth/BluetoothManagerService.java | 27 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index a3eceb5efd9..7a894ae67af 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -101,8 +101,8 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // for dumpsys support - String dump(); + // For dumpsys support + void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 32b91d25fb1..f5d27f952be 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -52,6 +53,7 @@ import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; @@ -1737,17 +1739,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - writer.println("enabled: " + mEnable); - writer.println("state: " + mState); - writer.println("address: " + mAddress); - writer.println("name: " + mName); + writer.println("Bluetooth Status"); + writer.println(" enabled: " + mEnable); + writer.println(" state: " + mState); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName + "\n"); + writer.flush(); + if (mBluetooth == null) { writer.println("Bluetooth Service not connected"); } else { + ParcelFileDescriptor pfd = null; try { - writer.println(mBluetooth.dump()); + pfd = ParcelFileDescriptor.dup(fd); + mBluetooth.dump(pfd); } catch (RemoteException re) { writer.println("RemoteException while calling Bluetooth Service"); + } catch (IOException ioe) { + writer.println("IOException attempting to dup() fd"); + } finally { + if (pfd != null) { + try { + pfd.close(); + } catch (IOException ioe) { + writer.println("IOException attempting to close() fd"); + } + } } } } -- GitLab From 27ce6cfbe105e5c36e19b1f76bc9b8f29710b331 Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Thu, 23 Apr 2015 17:10:21 -0700 Subject: [PATCH 0471/1408] Add bluetooth permission annotations Change-Id: I5bc86f8ec6ea5c873f1e14dab0e0c47c5c9df7f7 --- .../java/android/bluetooth/BluetoothA2dp.java | 3 ++ .../android/bluetooth/BluetoothAdapter.java | 39 ++++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 12 ++++++ .../android/bluetooth/BluetoothManager.java | 5 +++ .../android/bluetooth/BluetoothProfile.java | 6 +++ .../bluetooth/le/BluetoothLeScanner.java | 5 +++ 6 files changed, 69 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 767f59eb129..f66b5ff466c 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; @@ -380,6 +382,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return priority of the device * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ec6f18d217e..8768f400b13 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -17,6 +17,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -37,6 +40,8 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -133,6 +138,12 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; + /** @hide */ + @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON, + STATE_BLE_ON, STATE_BLE_TURNING_OFF}) + @Retention(RetentionPolicy.SOURCE) + public @interface AdapterState {} + /** * Indicates the local Bluetooth adapter is off. */ @@ -273,6 +284,11 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_SCAN_MODE = "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; + /** @hide */ + @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScanMode {} + /** * Indicates that both inquiry scan and page scan are disabled on the local * Bluetooth adapter. Therefore this device is neither discoverable @@ -578,6 +594,7 @@ public final class BluetoothAdapter { * * @return true if the local adapter is turned on */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { try { synchronized(mManagerCallback) { @@ -752,6 +769,8 @@ public final class BluetoothAdapter { * * @return current state of Bluetooth adapter */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @AdapterState public int getState() { try { synchronized(mManagerCallback) { @@ -792,6 +811,8 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @AdapterState public int getLeState() { try { synchronized(mManagerCallback) { @@ -845,6 +866,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter startup has begun, or false on * immediate error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { int state = STATE_OFF; if (isEnabled() == true){ @@ -893,6 +915,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on * immediate error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { return mManagerService.disable(true); @@ -925,6 +948,7 @@ public final class BluetoothAdapter { * * @return Bluetooth hardware address as string */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public String getAddress() { try { return mManagerService.getAddress(); @@ -998,6 +1022,7 @@ public final class BluetoothAdapter { * @param name a valid Bluetooth name * @return true if the name was set, false otherwise */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setName(String name) { if (getState() != STATE_ON) return false; try { @@ -1024,6 +1049,8 @@ public final class BluetoothAdapter { * * @return scan mode */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @ScanMode public int getScanMode() { if (getState() != STATE_ON) return SCAN_MODE_NONE; try { @@ -1062,7 +1089,7 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - public boolean setScanMode(int mode, int duration) { + public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) return false; try { synchronized(mManagerCallback) { @@ -1130,6 +1157,7 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery() { if (getState() != STATE_ON) return false; try { @@ -1157,6 +1185,7 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { @@ -1186,6 +1215,7 @@ public final class BluetoothAdapter { * * @return true if discovering */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isDiscovering() { if (getState() != STATE_ON) return false; try { @@ -1345,6 +1375,7 @@ public final class BluetoothAdapter { * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public Set getBondedDevices() { if (getState() != STATE_ON) { return toDeviceSet(new BluetoothDevice[0]); @@ -1396,6 +1427,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { @@ -1460,6 +1492,7 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, true, true); @@ -1491,6 +1524,7 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, false); @@ -2032,6 +2066,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(LeScanCallback callback) { return startLeScan(null, callback); } @@ -2052,6 +2087,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); if (callback == null) { @@ -2138,6 +2174,7 @@ public final class BluetoothAdapter { * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void stopLeScan(LeScanCallback callback) { if (DBG) Log.d(TAG, "stopLeScan()"); BluetoothLeScanner scanner = getBluetoothLeScanner(); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index bfc374fb913..26a91e43a8e 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -709,6 +711,7 @@ public final class BluetoothDevice implements Parcelable { * * @return the Bluetooth name, or null if there was a problem. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public String getName() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); @@ -729,6 +732,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #DEVICE_TYPE_DUAL}. * {@link #DEVICE_TYPE_UNKNOWN} if it's not available */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getType() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); @@ -807,6 +811,7 @@ public final class BluetoothDevice implements Parcelable { * * @return false on immediate error, true if bonding will begin */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); @@ -948,6 +953,7 @@ public final class BluetoothDevice implements Parcelable { * * @return the bond state */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBondState() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get bond state"); @@ -1014,6 +1020,7 @@ public final class BluetoothDevice implements Parcelable { * * @return Bluetooth class object, or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothClass getBluetoothClass() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); @@ -1039,6 +1046,7 @@ public final class BluetoothDevice implements Parcelable { * @return the supported features (UUIDs) of the remote device, * or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public ParcelUuid[] getUuids() { if (sService == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); @@ -1065,6 +1073,7 @@ public final class BluetoothDevice implements Parcelable { * of initiating an ACL connection to the remote device * was started. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { IBluetooth service = sService; if (service == null || isBluetoothEnabled() == false) { @@ -1144,6 +1153,7 @@ public final class BluetoothDevice implements Parcelable { * @return true confirmation has been sent out * false for error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); @@ -1405,6 +1415,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (isBluetoothEnabled() == false) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1443,6 +1454,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (isBluetoothEnabled() == false) { Log.e(TAG, "Bluetooth is not enabled"); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index b1618cf3289..e355a1c0a53 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.content.Context; import android.os.RemoteException; import android.util.Log; @@ -89,6 +91,7 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device, int profile) { if (DBG) Log.d(TAG,"getConnectionState()"); @@ -117,6 +120,7 @@ public final class BluetoothManager { * @param profile GATT or GATT_SERVER * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List getConnectedDevices(int profile) { if (DBG) Log.d(TAG,"getConnectedDevices"); if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { @@ -161,6 +165,7 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List getDevicesMatchingConnectionStates(int profile, int[] states) { if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index eecb0739657..cbce22cdea6 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -17,6 +17,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; + import java.util.List; /** @@ -163,6 +166,7 @@ public interface BluetoothProfile { * * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List getConnectedDevices(); /** @@ -179,6 +183,7 @@ public interface BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List getDevicesMatchingConnectionStates(int[] states); /** @@ -191,6 +196,7 @@ public interface BluetoothProfile { * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device); /** diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 687bd5db099..2e6c4f03d8b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,6 +16,8 @@ package android.bluetooth.le; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; @@ -80,6 +82,7 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code callback} is null. */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(final ScanCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback is null"); @@ -97,6 +100,7 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { startScan(filters, settings, callback, null); @@ -151,6 +155,7 @@ public final class BluetoothLeScanner { * * @param callback */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void stopScan(ScanCallback callback) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); synchronized (mLeScanClients) { -- GitLab From 252a61970f78294b0d6d3d85c0dcdb9af7f588b4 Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Tue, 12 May 2015 19:13:36 -0700 Subject: [PATCH 0472/1408] Do not report WiFi and Bluetooth MAC addresses - framework. As a part of the new runtime permissions work we are limiting the PII apps can access. BT and WiFi MAC addresses are PII and based on our research there is no valid use case for app dev to get these addresses aside of user tracking which we are trying to limit. bug:21078858 Change-Id: Ib48223b272c0fd4f5c36acc889d4f44df204b309 --- .../java/android/bluetooth/BluetoothAdapter.java | 8 ++++++++ .../server/bluetooth/BluetoothManagerService.java | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8768f400b13..b22b914686d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -95,6 +95,14 @@ public final class BluetoothAdapter { private static final boolean DBG = true; private static final boolean VDBG = false; + /** + * Default MAC address reported to a client that does not have the + * android.permission.LOCAL_MAC_ADDRESS permission. + * + * @hide + */ + public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; + /** * Sentinel error value for this class. Guaranteed to not equal any other * integer constant in this class. Provided as a convenience for functions diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f5d27f952be..c46fa76e897 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.Manifest; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; @@ -909,16 +910,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks.finishBroadcast(); } } + public String getAddress() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); + "Need BLUETOOTH permission"); if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { + (!checkIfCallerIsForegroundUser())) { Log.w(TAG,"getAddress(): not allowed for non-active and non system user"); return null; } + if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS) + != PackageManager.PERMISSION_GRANTED) { + return BluetoothAdapter.DEFAULT_MAC_ADDRESS; + } + synchronized(mConnection) { if (mBluetooth != null) { try { -- GitLab From a8d9f1f25b42eca3ea4df275dd3fac6e43a858f8 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Sun, 15 Feb 2015 18:56:54 +0530 Subject: [PATCH 0473/1408] Bluetooth: Check for mPanService before changing tethering state Checking for mPanService before changing OR checking tethering state, otherwise it will lead to application crash while checking isTetheringOn OR changing tethering state. Change-Id: I00844c03cdb8616118c1d50d7d31c75e51f0ef9b --- .../java/android/bluetooth/BluetoothPan.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4f81f9863c5..eb6166acee3 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -333,19 +333,25 @@ public final class BluetoothPan implements BluetoothProfile { public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - try { - mPanService.setBluetoothTethering(value); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + + if (mPanService != null && isEnabled()) { + try { + mPanService.setBluetoothTethering(value); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } } } public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - try { - return mPanService.isTetheringOn(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + + if (mPanService != null && isEnabled()) { + try { + return mPanService.isTetheringOn(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } } return false; } -- GitLab From cc39974bac4123511dc97c87a3610af61ca9709c Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Sun, 15 Feb 2015 18:56:54 +0530 Subject: [PATCH 0474/1408] DO NOT MERGE Bluetooth: Check for mPanService before changing tethering state Checking for mPanService before changing OR checking tethering state, otherwise it will lead to application crash while checking isTetheringOn OR changing tethering state. Change-Id: I00844c03cdb8616118c1d50d7d31c75e51f0ef9b --- .../java/android/bluetooth/BluetoothPan.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4f81f9863c5..eb6166acee3 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -333,19 +333,25 @@ public final class BluetoothPan implements BluetoothProfile { public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - try { - mPanService.setBluetoothTethering(value); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + + if (mPanService != null && isEnabled()) { + try { + mPanService.setBluetoothTethering(value); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } } } public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - try { - return mPanService.isTetheringOn(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + + if (mPanService != null && isEnabled()) { + try { + return mPanService.isTetheringOn(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } } return false; } -- GitLab From 8d25b6833eb21085d9c1bd202dd75f8ce545d7c9 Mon Sep 17 00:00:00 2001 From: Prerepa Viswanadham Date: Tue, 19 May 2015 14:55:09 -0700 Subject: [PATCH 0475/1408] setCallbackType should be public for onlost/onfound apis Bug: 21304922 Change-Id: I27ed4f3e00ac13354e3ef75e5686916be0d10918 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 4eeb577a1de..d61662469b6 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -249,9 +249,7 @@ public final class ScanSettings implements Parcelable { * * @param callbackType The callback type flags for the scan. * @throws IllegalArgumentException If the {@code callbackType} is invalid. - * @hide */ - @SystemApi public Builder setCallbackType(int callbackType) { if (!isValidCallbackType(callbackType)) { -- GitLab From bd8268db5921a2826ca29ead948b1f1ae0b1c974 Mon Sep 17 00:00:00 2001 From: Nitin Arora Date: Thu, 7 May 2015 18:45:44 -0700 Subject: [PATCH 0476/1408] Bluetooth: Clear BLE always ON apps at Airplane mode This patch clears the container managing the Ble Always On apps while switching on, the airplane mode. The airplane mode will completely turn Off the Bleutooth Adapter from any state it is in. Change-Id: Ib28d39d85efe3aac37e3a53a4fb892099568c702 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c46fa76e897..66fd36fb0f1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -205,6 +205,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Clear registered LE apps to force shut-off synchronized (this) { mBleAppCount = 0; + mBleApps.clear(); } if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part -- GitLab From 2dc8a974a0446242fcdf62c69adcc57bc5469959 Mon Sep 17 00:00:00 2001 From: Nitin Arora Date: Thu, 7 May 2015 18:45:44 -0700 Subject: [PATCH 0477/1408] DO NOT MERGE Bluetooth: Clear BLE always ON apps at Airplane mode This patch clears the container managing the Ble Always On apps while switching on, the airplane mode. The airplane mode will completely turn Off the Bleutooth Adapter from any state it is in. Change-Id: Ib28d39d85efe3aac37e3a53a4fb892099568c702 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2cc5ff0601d..24611fd8d57 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -204,6 +204,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Clear registered LE apps to force shut-off synchronized (this) { mBleAppCount = 0; + mBleApps.clear(); } if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part -- GitLab From 76888c7fe5af97f57b3b71a6ac9167c7239dc91f Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Thu, 14 May 2015 14:48:00 -0700 Subject: [PATCH 0478/1408] Un-hide connectGatt() variant with selective transport Change-Id: I1d3c512160de2bd68550faf243bb151d5cf7f3c9 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 26a91e43a8e..dcf06d8a887 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -567,19 +567,16 @@ public final class BluetoothDevice implements Parcelable { /** * No preferrence of physical transport for GATT connections to remote dual-mode devices - * @hide */ public static final int TRANSPORT_AUTO = 0; /** * Prefer BR/EDR transport for GATT connections to remote dual-mode devices - * @hide */ public static final int TRANSPORT_BREDR = 1; /** * Prefer LE transport for GATT connections to remote dual-mode devices - * @hide */ public static final int TRANSPORT_LE = 2; @@ -1564,7 +1561,6 @@ public final class BluetoothDevice implements Parcelable { * {@link BluetoothDevice#TRANSPORT_AUTO} or * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} * @throws IllegalArgumentException if callback is null - * @hide */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { -- GitLab From 7f9ee01563ee7bb79e2786dbc09dabbb3ca33692 Mon Sep 17 00:00:00 2001 From: Nitin Shivpure Date: Thu, 12 Dec 2013 20:03:49 +0530 Subject: [PATCH 0479/1408] Bluetooth: Fix to avoid framework reboot during monkey testing When running monkeyrunner, sometimes the framework is disconnected due to an unhandled Java exception while binding the PAN service (when it is unsuccessful). Handing the Java exception while binding PAN service solves this issue. Change-Id: Idea710593a3f9496305f636042605303e73e7749 --- .../java/android/bluetooth/BluetoothPan.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index eb6166acee3..744f9421b23 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -138,7 +138,6 @@ public final class BluetoothPan implements BluetoothProfile { } if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); doBind(); - if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); } boolean doBind() { @@ -185,12 +184,22 @@ public final class BluetoothPan implements BluetoothProfile { final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @Override - public void onBluetoothStateChange(boolean on) throws RemoteException { - //Handle enable request to bind again. + public void onBluetoothStateChange(boolean on) { + // Handle enable request to bind again. + Log.d(TAG, "onBluetoothStateChange on: " + on); if (on) { - Log.d(TAG, "onBluetoothStateChange(on) call bindService"); - doBind(); - if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); + try { + if (mPanService == null) { + if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); + doBind(); + } + + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); + + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); + } } else { if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { -- GitLab From acfb194dd71e1b813e02fbf2c12553adf03d4b4a Mon Sep 17 00:00:00 2001 From: Nitin Shivpure Date: Thu, 12 Dec 2013 20:03:49 +0530 Subject: [PATCH 0480/1408] DO NOT MERGE Bluetooth: Fix to avoid framework reboot during monkey testing When running monkeyrunner, sometimes the framework is disconnected due to an unhandled Java exception while binding the PAN service (when it is unsuccessful). Handing the Java exception while binding PAN service solves this issue. Change-Id: Idea710593a3f9496305f636042605303e73e7749 --- .../java/android/bluetooth/BluetoothPan.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index eb6166acee3..744f9421b23 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -138,7 +138,6 @@ public final class BluetoothPan implements BluetoothProfile { } if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); doBind(); - if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); } boolean doBind() { @@ -185,12 +184,22 @@ public final class BluetoothPan implements BluetoothProfile { final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @Override - public void onBluetoothStateChange(boolean on) throws RemoteException { - //Handle enable request to bind again. + public void onBluetoothStateChange(boolean on) { + // Handle enable request to bind again. + Log.d(TAG, "onBluetoothStateChange on: " + on); if (on) { - Log.d(TAG, "onBluetoothStateChange(on) call bindService"); - doBind(); - if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called"); + try { + if (mPanService == null) { + if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); + doBind(); + } + + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); + + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); + } } else { if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { -- GitLab From 5a6856971729f446fca4392eb7f09ccb88484e93 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 1 Jun 2015 12:14:28 -0700 Subject: [PATCH 0481/1408] Expose a few system APIs for BLE scan only mode. Bug: 21562349 Change-Id: Ifc58efcf01edac5b4f9e5266adb966e7bc492209 --- .../android/bluetooth/BluetoothAdapter.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b22b914686d..8107a97e9d8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -410,6 +410,7 @@ public final class BluetoothAdapter { * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. * @hide */ + @SystemApi public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; @@ -620,17 +621,18 @@ public final class BluetoothAdapter { * @return true if the local Bluetooth LE adapter is turned on * @hide */ - public boolean isLeEnabled() { - final int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON"); - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - } else { - if (DBG) Log.d (TAG, "STATE_OFF"); - return false; - } - return true; + @SystemApi + public boolean isLeEnabled() { + final int state = getLeState(); + if (state == BluetoothAdapter.STATE_ON) { + if (DBG) Log.d (TAG, "STATE_ON"); + } else if (state == BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Log.d (TAG, "STATE_BLE_ON"); + } else { + if (DBG) Log.d (TAG, "STATE_OFF"); + return false; + } + return true; } /** @@ -680,6 +682,7 @@ public final class BluetoothAdapter { * immediate error * @hide */ + @SystemApi public boolean disableBLE() { if (!isBleScanAlwaysAvailable()) return false; @@ -742,6 +745,7 @@ public final class BluetoothAdapter { * immediate error * @hide */ + @SystemApi public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) return false; -- GitLab From a69ab7cc9fda5b00a636d919b3d35b59a8fe8e14 Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Thu, 16 Apr 2015 15:28:21 +0200 Subject: [PATCH 0482/1408] DO NOT MERGE - SAP: Change to use new SDP Api (4/4) Added class to carry SAP sdp record data. Change-Id: Ic0d07402eca52c88e2be101181f6dd12e5e738d0 Signed-off-by: Casper Bonde --- .../java/android/bluetooth/SdpSapsRecord.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 framework/java/android/bluetooth/SdpSapsRecord.java diff --git a/framework/java/android/bluetooth/SdpSapsRecord.java b/framework/java/android/bluetooth/SdpSapsRecord.java new file mode 100644 index 00000000000..84a29b92c00 --- /dev/null +++ b/framework/java/android/bluetooth/SdpSapsRecord.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class SdpSapsRecord implements Parcelable { + private final int mRfcommChannelNumber; + private final int mProfileVersion; + private final String mServiceName; + + public SdpSapsRecord(int rfcomm_channel_number, + int profile_version, + String service_name) { + this.mRfcommChannelNumber = rfcomm_channel_number; + this.mProfileVersion = profile_version; + this.mServiceName = service_name; + } + + public SdpSapsRecord(Parcel in) { + this.mRfcommChannelNumber = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mServiceName = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + public int getRfcommCannelNumber() { + return mRfcommChannelNumber; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + public String getServiceName() { + return mServiceName; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.mRfcommChannelNumber); + dest.writeInt(this.mProfileVersion); + dest.writeString(this.mServiceName); + + } + + @Override + public String toString() { + String ret = "Bluetooth MAS SDP Record:\n"; + + if (mRfcommChannelNumber != -1) { + ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; + } + if (mServiceName != null) { + ret += "Service Name: " + mServiceName + "\n"; + } + if (mProfileVersion != -1) { + ret += "Profile version: " + mProfileVersion + "\n"; + } + return ret; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpSapsRecord createFromParcel(Parcel in) { + return new SdpSapsRecord(in); + } + public SdpRecord[] newArray(int size) { + return new SdpRecord[size]; + } + }; +} -- GitLab From 6733e717be78e8a312692300e42be34f3a9a2610 Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Thu, 16 Apr 2015 15:28:21 +0200 Subject: [PATCH 0483/1408] SAP: Change to use new SDP Api (4/4) Added class to carry SAP SDP record data. Change-Id: Ic0d07402eca52c88e2be101181f6dd12e5e738d0 Signed-off-by: Casper Bonde --- .../java/android/bluetooth/SdpSapsRecord.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 framework/java/android/bluetooth/SdpSapsRecord.java diff --git a/framework/java/android/bluetooth/SdpSapsRecord.java b/framework/java/android/bluetooth/SdpSapsRecord.java new file mode 100644 index 00000000000..84a29b92c00 --- /dev/null +++ b/framework/java/android/bluetooth/SdpSapsRecord.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class SdpSapsRecord implements Parcelable { + private final int mRfcommChannelNumber; + private final int mProfileVersion; + private final String mServiceName; + + public SdpSapsRecord(int rfcomm_channel_number, + int profile_version, + String service_name) { + this.mRfcommChannelNumber = rfcomm_channel_number; + this.mProfileVersion = profile_version; + this.mServiceName = service_name; + } + + public SdpSapsRecord(Parcel in) { + this.mRfcommChannelNumber = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mServiceName = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + public int getRfcommCannelNumber() { + return mRfcommChannelNumber; + } + + public int getProfileVersion() { + return mProfileVersion; + } + + public String getServiceName() { + return mServiceName; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.mRfcommChannelNumber); + dest.writeInt(this.mProfileVersion); + dest.writeString(this.mServiceName); + + } + + @Override + public String toString() { + String ret = "Bluetooth MAS SDP Record:\n"; + + if (mRfcommChannelNumber != -1) { + ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; + } + if (mServiceName != null) { + ret += "Service Name: " + mServiceName + "\n"; + } + if (mProfileVersion != -1) { + ret += "Profile version: " + mProfileVersion + "\n"; + } + return ret; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpSapsRecord createFromParcel(Parcel in) { + return new SdpSapsRecord(in); + } + public SdpRecord[] newArray(int size) { + return new SdpRecord[size]; + } + }; +} -- GitLab From 60d77c2c9ceea76d7e4e32ab173ef3da64ff607e Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Tue, 21 Apr 2015 13:12:05 +0200 Subject: [PATCH 0484/1408] Add support for MITM for BluetoothSockets (1/4) This change adds an option to enforce Man-in-the-middle protection for the authentication process. This feature is needed for the Sim Access Profile. Change-Id: Ia3ef0caeb750f88608c9fa6bf6367d1c77de4cf3 Signed-off-by: Casper Bonde --- .../android/bluetooth/BluetoothAdapter.java | 48 +++++++++++++++++-- .../bluetooth/BluetoothServerSocket.java | 20 ++++++++ .../android/bluetooth/BluetoothSocket.java | 28 ++++++++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8107a97e9d8..ab3f7bcdf56 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1467,10 +1467,31 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { + return listenUsingRfcommOn(channel, false); + } + + /** + * Create a listening, secure RFCOMM Bluetooth socket. + *

        A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

        Valid RFCOMM channels are in range 1 to 30. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

        To auto assign a channel without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. + * @param channel RFCOMM channel to listen on + * @param mitm enforce man-in-the-middle protection for authentication. + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel); + BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm); int errno = socket.mSocket.bindListen(); - if(channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno != 0) { @@ -1669,14 +1690,18 @@ public final class BluetoothAdapter { /** * Construct an encrypted, authenticated, L2CAP server socket. * Call #accept to retrieve connections to this socket. + *

        To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @param mitm enforce man-in-the-middle protection for authentication. * @return An L2CAP BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. * @hide */ - public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { + public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, true, true, port); + BluetoothSocket.TYPE_L2CAP, true, true, port, mitm); int errno = socket.mSocket.bindListen(); if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1690,6 +1715,21 @@ public final class BluetoothAdapter { return socket; } + /** + * Construct an encrypted, authenticated, L2CAP server socket. + * Call #accept to retrieve connections to this socket. + *

        To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { + return listenUsingL2capOn(port, false); + } + /** * Read the local Out of Band Pairing Data *

        Requires {@link android.Manifest.permission#BLUETOOTH} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 21024a6021b..a80f55c0e49 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -86,6 +86,26 @@ public final class BluetoothServerSocket implements Closeable { throws IOException { mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); + if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + mSocket.setExcludeSdp(true); + } + } + + /** + * Construct a socket for incoming connections. + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @param mitm enforce man-in-the-middle protection for authentication. + * @throws IOException On error, for example Bluetooth not available, or + * insufficient privileges + */ + /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, + boolean mitm) + throws IOException { + mChannel = port; + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm); if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { mSocket.setExcludeSdp(true); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 5cf2300bef1..6ca6976b9ae 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -106,6 +106,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; + /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -115,7 +116,8 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; - private boolean mExcludeSdp = false; + private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ + private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private ParcelFileDescriptor mPfd; private LocalSocket mSocket; private InputStream mSocketIS; @@ -158,6 +160,24 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { + this(type, fd, auth, encrypt, device, port, uuid, false); + } + + /** + * Construct a BluetoothSocket. + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param device remote device that this socket can connect to + * @param port remote port + * @param uuid SDP uuid + * @param mitm enforce man-in-the-middle protection. + * @throws IOException On error, for example Bluetooth not available, or + * insufficient privileges + */ + /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, + BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm) throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { @@ -170,6 +190,7 @@ public final class BluetoothSocket implements Closeable { else mUuid = new ParcelUuid(new UUID(0, 0)); mType = type; mAuth = auth; + mAuthMitm = mitm; mEncrypt = encrypt; mDevice = device; mPort = port; @@ -201,6 +222,7 @@ public final class BluetoothSocket implements Closeable { mServiceName = s.mServiceName; mExcludeSdp = s.mExcludeSdp; + mAuthMitm = s.mAuthMitm; } private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); @@ -232,7 +254,7 @@ public final class BluetoothSocket implements Closeable { */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null); + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false); } /** @hide */ @@ -252,6 +274,8 @@ public final class BluetoothSocket implements Closeable { flags |= SEC_FLAG_ENCRYPT; if(mExcludeSdp) flags |= BTSOCK_FLAG_NO_SDP; + if(mAuthMitm) + flags |= SEC_FLAG_AUTH_MITM; return flags; } -- GitLab From 46312893ede08113697d34dd437497155d00953b Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Tue, 21 Apr 2015 13:12:05 +0200 Subject: [PATCH 0485/1408] DO NOT MERGE - Add support for MITM for BluetoothSockets (1/4) This change adds an option to enforce Man-in-the-middle protection for the authentication process. This feature is needed for the Sim Access Profile. Change-Id: Ia3ef0caeb750f88608c9fa6bf6367d1c77de4cf3 Signed-off-by: Casper Bonde --- .../android/bluetooth/BluetoothAdapter.java | 48 +++++++++++++++++-- .../bluetooth/BluetoothServerSocket.java | 20 ++++++++ .../android/bluetooth/BluetoothSocket.java | 28 ++++++++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b2519ca700e..308c4c84e3a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1430,10 +1430,31 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { + return listenUsingRfcommOn(channel, false); + } + + /** + * Create a listening, secure RFCOMM Bluetooth socket. + *

        A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

        Valid RFCOMM channels are in range 1 to 30. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + *

        To auto assign a channel without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. + * @param channel RFCOMM channel to listen on + * @param mitm enforce man-in-the-middle protection for authentication. + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + * @hide + */ + public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel); + BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm); int errno = socket.mSocket.bindListen(); - if(channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno != 0) { @@ -1630,14 +1651,18 @@ public final class BluetoothAdapter { /** * Construct an encrypted, authenticated, L2CAP server socket. * Call #accept to retrieve connections to this socket. + *

        To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @param mitm enforce man-in-the-middle protection for authentication. * @return An L2CAP BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. * @hide */ - public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { + public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, true, true, port); + BluetoothSocket.TYPE_L2CAP, true, true, port, mitm); int errno = socket.mSocket.bindListen(); if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1651,6 +1676,21 @@ public final class BluetoothAdapter { return socket; } + /** + * Construct an encrypted, authenticated, L2CAP server socket. + * Call #accept to retrieve connections to this socket. + *

        To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { + return listenUsingL2capOn(port, false); + } + /** * Read the local Out of Band Pairing Data *

        Requires {@link android.Manifest.permission#BLUETOOTH} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 21024a6021b..a80f55c0e49 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -86,6 +86,26 @@ public final class BluetoothServerSocket implements Closeable { throws IOException { mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); + if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + mSocket.setExcludeSdp(true); + } + } + + /** + * Construct a socket for incoming connections. + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @param mitm enforce man-in-the-middle protection for authentication. + * @throws IOException On error, for example Bluetooth not available, or + * insufficient privileges + */ + /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, + boolean mitm) + throws IOException { + mChannel = port; + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm); if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { mSocket.setExcludeSdp(true); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 5702d117b66..4722d013102 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -102,6 +102,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; + /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -111,7 +112,8 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; - private boolean mExcludeSdp = false; + private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ + private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private ParcelFileDescriptor mPfd; private LocalSocket mSocket; private InputStream mSocketIS; @@ -154,6 +156,24 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { + this(type, fd, auth, encrypt, device, port, uuid, false); + } + + /** + * Construct a BluetoothSocket. + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param device remote device that this socket can connect to + * @param port remote port + * @param uuid SDP uuid + * @param mitm enforce man-in-the-middle protection. + * @throws IOException On error, for example Bluetooth not available, or + * insufficient privileges + */ + /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, + BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm) throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { @@ -166,6 +186,7 @@ public final class BluetoothSocket implements Closeable { else mUuid = new ParcelUuid(new UUID(0, 0)); mType = type; mAuth = auth; + mAuthMitm = mitm; mEncrypt = encrypt; mDevice = device; mPort = port; @@ -197,6 +218,7 @@ public final class BluetoothSocket implements Closeable { mServiceName = s.mServiceName; mExcludeSdp = s.mExcludeSdp; + mAuthMitm = s.mAuthMitm; } private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); @@ -228,7 +250,7 @@ public final class BluetoothSocket implements Closeable { */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null); + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false); } /** @hide */ @@ -248,6 +270,8 @@ public final class BluetoothSocket implements Closeable { flags |= SEC_FLAG_ENCRYPT; if(mExcludeSdp) flags |= BTSOCK_FLAG_NO_SDP; + if(mAuthMitm) + flags |= SEC_FLAG_AUTH_MITM; return flags; } -- GitLab From 3ff210a5a8638f91d694304272e910d99b19f69e Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Fri, 8 May 2015 14:32:24 +0200 Subject: [PATCH 0486/1408] DO NOT MERGE - SAP: Make it possible to enforce a 16-digit pin code (4/5) This change enable the posibility to enforce using a 16-digit pin or MITM for a RFCOMM or L2CAP connection. This is needed for the SIM access profile. Change-Id: I3205013f9e758c353381442a86845dab467780f8 Signed-off-by: Casper Bonde --- .../android/bluetooth/BluetoothAdapter.java | 17 +++++++++++------ .../java/android/bluetooth/BluetoothDevice.java | 11 +++++++++-- .../bluetooth/BluetoothServerSocket.java | 6 ++++-- .../java/android/bluetooth/BluetoothSocket.java | 14 +++++++++++--- .../android/bluetooth/BluetoothTestUtils.java | 1 + 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 308c4c84e3a..d71db94bdd3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1430,7 +1430,7 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { - return listenUsingRfcommOn(channel, false); + return listenUsingRfcommOn(channel, false, false); } /** @@ -1445,14 +1445,17 @@ public final class BluetoothAdapter { * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * @param channel RFCOMM channel to listen on * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. * @hide */ - public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm) throws IOException { + public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, + boolean min16DigitPin) + throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm); + BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin); int errno = socket.mSocket.bindListen(); if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1655,14 +1658,16 @@ public final class BluetoothAdapter { * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * @param port the PSM to listen on * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. * @return An L2CAP BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. * @hide */ - public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm) throws IOException { + public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) + throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, true, true, port, mitm); + BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin); int errno = socket.mSocket.bindListen(); if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1688,7 +1693,7 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { - return listenUsingL2capOn(port, false); + return listenUsingL2capOn(port, false, false); } /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index bfc374fb913..129cd257023 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -528,6 +528,13 @@ public final class BluetoothDevice implements Parcelable { */ public static final int PAIRING_VARIANT_OOB_CONSENT = 6; + /** + * The user will be prompted to enter a 16 digit pin or + * an app will enter a 16 digit pin for user. + * @hide + */ + public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7; + /** * Used as an extra field in {@link #ACTION_UUID} intents, * Contains the {@link android.os.ParcelUuid}s of the remote device which @@ -1308,8 +1315,8 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "", e); } return false; - } - + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index a80f55c0e49..c15852dcdaa 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -98,14 +98,16 @@ public final class BluetoothServerSocket implements Closeable { * @param encrypt require the connection to be encrypted * @param port remote port * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or * insufficient privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, - boolean mitm) + boolean mitm, boolean min16DigitPin) throws IOException { mChannel = port; - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm); + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm, + min16DigitPin); if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { mSocket.setExcludeSdp(true); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 4722d013102..2f8e7aca927 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -103,6 +103,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; + /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -114,6 +115,7 @@ public final class BluetoothSocket implements Closeable { private final ParcelUuid mUuid; private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ + private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ private ParcelFileDescriptor mPfd; private LocalSocket mSocket; private InputStream mSocketIS; @@ -156,7 +158,7 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { - this(type, fd, auth, encrypt, device, port, uuid, false); + this(type, fd, auth, encrypt, device, port, uuid, false, false); } /** @@ -169,11 +171,13 @@ public final class BluetoothSocket implements Closeable { * @param port remote port * @param uuid SDP uuid * @param mitm enforce man-in-the-middle protection. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or * insufficient privileges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, - BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm) throws IOException { + BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) + throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { @@ -187,6 +191,7 @@ public final class BluetoothSocket implements Closeable { mType = type; mAuth = auth; mAuthMitm = mitm; + mMin16DigitPin = min16DigitPin; mEncrypt = encrypt; mDevice = device; mPort = port; @@ -219,6 +224,7 @@ public final class BluetoothSocket implements Closeable { mServiceName = s.mServiceName; mExcludeSdp = s.mExcludeSdp; mAuthMitm = s.mAuthMitm; + mMin16DigitPin = s.mMin16DigitPin; } private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); @@ -250,7 +256,7 @@ public final class BluetoothSocket implements Closeable { */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false); + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false); } /** @hide */ @@ -272,6 +278,8 @@ public final class BluetoothSocket implements Closeable { flags |= BTSOCK_FLAG_NO_SDP; if(mAuthMitm) flags |= SEC_FLAG_AUTH_MITM; + if(mMin16DigitPin) + flags |= SEC_FLAG_AUTH_16_DIGIT; return flags; } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 8fbd2147c9d..0d9980ad205 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -170,6 +170,7 @@ public class BluetoothTestUtils extends Assert { assertNotSame(-1, varient); switch (varient) { case BluetoothDevice.PAIRING_VARIANT_PIN: + case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS: mDevice.setPin(mPin); break; case BluetoothDevice.PAIRING_VARIANT_PASSKEY: -- GitLab From 91276018bd5dd6c61dcd69cd87090ba022b532e7 Mon Sep 17 00:00:00 2001 From: Casper Bonde Date: Fri, 8 May 2015 14:32:24 +0200 Subject: [PATCH 0487/1408] SAP: Make it possible to enforce a 16-digit pin code (4/5) This change enable the posibility to enforce using a 16-digit pin or MITM for a RFCOMM or L2CAP connection. This is needed for the SIM access profile. Change-Id: I3205013f9e758c353381442a86845dab467780f8 Signed-off-by: Casper Bonde --- .../android/bluetooth/BluetoothAdapter.java | 17 +++++++++++------ .../java/android/bluetooth/BluetoothDevice.java | 11 +++++++++-- .../bluetooth/BluetoothServerSocket.java | 6 ++++-- .../java/android/bluetooth/BluetoothSocket.java | 14 +++++++++++--- .../android/bluetooth/BluetoothTestUtils.java | 1 + 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ab3f7bcdf56..97afafa4c8b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1467,7 +1467,7 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { - return listenUsingRfcommOn(channel, false); + return listenUsingRfcommOn(channel, false, false); } /** @@ -1482,14 +1482,17 @@ public final class BluetoothAdapter { * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * @param channel RFCOMM channel to listen on * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. * @hide */ - public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm) throws IOException { + public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, + boolean min16DigitPin) + throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm); + BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin); int errno = socket.mSocket.bindListen(); if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1694,14 +1697,16 @@ public final class BluetoothAdapter { * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * @param port the PSM to listen on * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. * @return An L2CAP BluetoothServerSocket * @throws IOException On error, for example Bluetooth not available, or * insufficient permissions. * @hide */ - public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm) throws IOException { + public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) + throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, true, true, port, mitm); + BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin); int errno = socket.mSocket.bindListen(); if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1727,7 +1732,7 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { - return listenUsingL2capOn(port, false); + return listenUsingL2capOn(port, false, false); } /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index dcf06d8a887..c96fe71e797 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -530,6 +530,13 @@ public final class BluetoothDevice implements Parcelable { */ public static final int PAIRING_VARIANT_OOB_CONSENT = 6; + /** + * The user will be prompted to enter a 16 digit pin or + * an app will enter a 16 digit pin for user. + * @hide + */ + public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7; + /** * Used as an extra field in {@link #ACTION_UUID} intents, * Contains the {@link android.os.ParcelUuid}s of the remote device which @@ -1315,8 +1322,8 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "", e); } return false; - } - + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device on given channel. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index a80f55c0e49..c15852dcdaa 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -98,14 +98,16 @@ public final class BluetoothServerSocket implements Closeable { * @param encrypt require the connection to be encrypted * @param port remote port * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or * insufficient privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, - boolean mitm) + boolean mitm, boolean min16DigitPin) throws IOException { mChannel = port; - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm); + mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm, + min16DigitPin); if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { mSocket.setExcludeSdp(true); } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 6ca6976b9ae..6302521e634 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -107,6 +107,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; + /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -118,6 +119,7 @@ public final class BluetoothSocket implements Closeable { private final ParcelUuid mUuid; private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ + private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ private ParcelFileDescriptor mPfd; private LocalSocket mSocket; private InputStream mSocketIS; @@ -160,7 +162,7 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { - this(type, fd, auth, encrypt, device, port, uuid, false); + this(type, fd, auth, encrypt, device, port, uuid, false, false); } /** @@ -173,11 +175,13 @@ public final class BluetoothSocket implements Closeable { * @param port remote port * @param uuid SDP uuid * @param mitm enforce man-in-the-middle protection. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or * insufficient privileges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, - BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm) throws IOException { + BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) + throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { @@ -191,6 +195,7 @@ public final class BluetoothSocket implements Closeable { mType = type; mAuth = auth; mAuthMitm = mitm; + mMin16DigitPin = min16DigitPin; mEncrypt = encrypt; mDevice = device; mPort = port; @@ -223,6 +228,7 @@ public final class BluetoothSocket implements Closeable { mServiceName = s.mServiceName; mExcludeSdp = s.mExcludeSdp; mAuthMitm = s.mAuthMitm; + mMin16DigitPin = s.mMin16DigitPin; } private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); @@ -254,7 +260,7 @@ public final class BluetoothSocket implements Closeable { */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false); + this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false); } /** @hide */ @@ -276,6 +282,8 @@ public final class BluetoothSocket implements Closeable { flags |= BTSOCK_FLAG_NO_SDP; if(mAuthMitm) flags |= SEC_FLAG_AUTH_MITM; + if(mMin16DigitPin) + flags |= SEC_FLAG_AUTH_16_DIGIT; return flags; } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 8fbd2147c9d..0d9980ad205 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -170,6 +170,7 @@ public class BluetoothTestUtils extends Assert { assertNotSame(-1, varient); switch (varient) { case BluetoothDevice.PAIRING_VARIANT_PIN: + case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS: mDevice.setPin(mPin); break; case BluetoothDevice.PAIRING_VARIANT_PASSKEY: -- GitLab From dccd80808d727baf2cfd3a98d9d222d7aa0e5423 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Fri, 5 Dec 2014 09:31:30 -0800 Subject: [PATCH 0488/1408] Bluetooth native dumpsys logging support (3/5) Bug: 18508263 Change-Id: I88f9c90dab8b0c825010c8617709449a3dd704b2 --- .../java/android/bluetooth/IBluetooth.aidl | 4 +-- .../bluetooth/BluetoothManagerService.java | 27 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index dabb1ceaea8..c6f238ee6a6 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -99,6 +99,6 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // for dumpsys support - String dump(); + // For dumpsys support + void dump(in ParcelFileDescriptor fd); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 32a6a2faad7..7aa8e113e03 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -51,6 +52,7 @@ import android.provider.Settings; import android.util.Log; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; @@ -1523,17 +1525,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - writer.println("enabled: " + mEnable); - writer.println("state: " + mState); - writer.println("address: " + mAddress); - writer.println("name: " + mName); + writer.println("Bluetooth Status"); + writer.println(" enabled: " + mEnable); + writer.println(" state: " + mState); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName + "\n"); + writer.flush(); + if (mBluetooth == null) { writer.println("Bluetooth Service not connected"); } else { + ParcelFileDescriptor pfd = null; try { - writer.println(mBluetooth.dump()); + pfd = ParcelFileDescriptor.dup(fd); + mBluetooth.dump(pfd); } catch (RemoteException re) { writer.println("RemoteException while calling Bluetooth Service"); + } catch (IOException ioe) { + writer.println("IOException attempting to dup() fd"); + } finally { + if (pfd != null) { + try { + pfd.close(); + } catch (IOException ioe) { + writer.println("IOException attempting to close() fd"); + } + } } } } -- GitLab From 80c388b84556479872432be7a370df26829fe005 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 11 Jun 2015 17:50:29 -0700 Subject: [PATCH 0489/1408] Stops BLE scan when scan only mode is disabled. Bug: 21791070 Change-Id: I1137190f01fb3790b7dfbc409429414da44abe58 --- .../bluetooth/BluetoothManagerService.java | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 66fd36fb0f1..fbb6dc9bb96 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -21,8 +21,8 @@ import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; -import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothCallback; +import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothHeadset; import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; @@ -37,6 +37,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.database.ContentObserver; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -56,11 +57,8 @@ import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; - import java.util.HashMap; import java.util.Map; - -import java.util.*; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -259,6 +257,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mName = null; mErrorRecoveryRetryCounter = 0; mContentResolver = context.getContentResolver(); + // Observe BLE scan only mode settings change. + registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); @@ -458,6 +458,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + // Monitor change of BLE scan only mode settings. + private void registerForBleScanModeChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + if (!isBleScanAlwaysAvailable()) { + disableBleScanMode(); + clearBleApps(); + try { + if (mBluetooth != null) mBluetooth.onBrEdrDown(); + } catch (RemoteException e) { + Log.e(TAG, "error when disabling bluetooth", e); + } + } + } + }; + + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), + false, contentObserver); + } + + // Disable ble scan only mode. + private void disableBleScanMode() { + try { + if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { + if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); + mEnable = false; + } + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + } + } + public int updateBleAppCount(IBinder token, boolean enable) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); @@ -478,11 +512,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { ClientDeathRecipient r = mBleApps.get(token); if (r != null) { - try { - token.linkToDeath(r, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("Wake lock is already dead."); - } + // Unregister death recipient as the app goes away. + token.unlinkToDeath(r, 0); mBleApps.remove(token); synchronized (this) { if (mBleAppCount > 0) --mBleAppCount; @@ -492,18 +523,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount); if (mBleAppCount == 0 && mEnable) { - try { - if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { - if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); - mEnable = false; - } - } catch (RemoteException e) { - Log.e(TAG, "getState()", e); - } + disableBleScanMode(); } return mBleAppCount; } + // Clear all apps using BLE scan only mode. + private void clearBleApps() { + synchronized (this) { + mBleApps.clear(); + mBleAppCount = 0; + } + } + /** @hide*/ public boolean isBleAppPresent() { if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount); -- GitLab From b971a2d779364f8f4f8d917011c61b0b919d922d Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 15 Jun 2015 17:19:07 -0700 Subject: [PATCH 0490/1408] Bluetooth energy: fix overflow in calculation Use longs instead of ints, because the energy values can get pretty high. Change-Id: I43e696ad9e5965c2e616b11920db5bfae5db1671 --- .../BluetoothActivityEnergyInfo.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index 161c339e78a..834a587d83d 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -28,10 +28,10 @@ import android.os.Parcelable; public final class BluetoothActivityEnergyInfo implements Parcelable { private final long mTimestamp; private final int mBluetoothStackState; - private final int mControllerTxTimeMs; - private final int mControllerRxTimeMs; - private final int mControllerIdleTimeMs; - private final int mControllerEnergyUsed; + private final long mControllerTxTimeMs; + private final long mControllerRxTimeMs; + private final long mControllerIdleTimeMs; + private final long mControllerEnergyUsed; public static final int BT_STACK_STATE_INVALID = 0; public static final int BT_STACK_STATE_STATE_ACTIVE = 1; @@ -39,7 +39,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public static final int BT_STACK_STATE_STATE_IDLE = 3; public BluetoothActivityEnergyInfo(long timestamp, int stackState, - int txTime, int rxTime, int idleTime, int energyUsed) { + long txTime, long rxTime, long idleTime, long energyUsed) { mTimestamp = timestamp; mBluetoothStackState = stackState; mControllerTxTimeMs = txTime; @@ -65,10 +65,10 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { long timestamp = in.readLong(); int stackState = in.readInt(); - int txTime = in.readInt(); - int rxTime = in.readInt(); - int idleTime = in.readInt(); - int energyUsed = in.readInt(); + long txTime = in.readLong(); + long rxTime = in.readLong(); + long idleTime = in.readLong(); + long energyUsed = in.readLong(); return new BluetoothActivityEnergyInfo(timestamp, stackState, txTime, rxTime, idleTime, energyUsed); } @@ -80,10 +80,10 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(mTimestamp); out.writeInt(mBluetoothStackState); - out.writeInt(mControllerTxTimeMs); - out.writeInt(mControllerRxTimeMs); - out.writeInt(mControllerIdleTimeMs); - out.writeInt(mControllerEnergyUsed); + out.writeLong(mControllerTxTimeMs); + out.writeLong(mControllerRxTimeMs); + out.writeLong(mControllerIdleTimeMs); + out.writeLong(mControllerEnergyUsed); } public int describeContents() { @@ -100,21 +100,21 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { /** * @return tx time in ms */ - public int getControllerTxTimeMillis() { + public long getControllerTxTimeMillis() { return mControllerTxTimeMs; } /** * @return rx time in ms */ - public int getControllerRxTimeMillis() { + public long getControllerRxTimeMillis() { return mControllerRxTimeMs; } /** * @return idle time in ms */ - public int getControllerIdleTimeMillis() { + public long getControllerIdleTimeMillis() { return mControllerIdleTimeMs; } @@ -122,7 +122,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { * product of current(mA), voltage(V) and time(ms) * @return energy used */ - public int getControllerEnergyUsed() { + public long getControllerEnergyUsed() { return mControllerEnergyUsed; } @@ -137,8 +137,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { * @return if the record is valid */ public boolean isValid() { - return ((getControllerTxTimeMillis() !=0) || - (getControllerRxTimeMillis() !=0) || - (getControllerIdleTimeMillis() !=0)); + return ((mControllerTxTimeMs !=0) || + (mControllerRxTimeMs !=0) || + (mControllerIdleTimeMs !=0)); } } -- GitLab From 07cc3918d5ca93587cea7af0b757221c5a942bb7 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 17 Jun 2015 17:01:23 -0700 Subject: [PATCH 0491/1408] Sync the Bluetooth name and address from the Bluetooth Adapter It looks like the synchronization of the Bluetooth name and address from the Bluetooth Adapter has been removed by the following commit without an explanation: Bluetooth LE background operation mode (2/2) As a result, the BluetoothManagerService.mAddress was always null. Bug: 20545952 Change-Id: I595f370e06e17b2c67ce511f793efdee7674598c --- .../android/server/bluetooth/BluetoothManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index fbb6dc9bb96..f9f67144d89 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -237,6 +237,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } } + + if (!isNameAndAddressSet()) { + // Sync the Bluetooth name and address from the Bluetooth Adapter + if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); + getNameAndAddress(); + } } } }; -- GitLab From 9d6dfbef00176de7d27467de745cb4b1c81d25e9 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sat, 20 Jun 2015 22:55:04 -0700 Subject: [PATCH 0492/1408] Fetch the Bluetooth name and address without enabling the whole stack After factory reset and first bootup, fetching the Bluetooth device name and address is now done without explicitly enabling/disabling the whole Bluetooth stack. Apparently, enabling/disabling the whole stack during th first bootup was somehow holding the kernel wakelock without releasing it. Also, disable debug log messages for class BluetoothManagerService. Bug: 21949364 Change-Id: Iffc14f7969d05c1456159d1f4246c53fb5df0f7a --- .../bluetooth/BluetoothManagerService.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f9f67144d89..b33b10bc7ab 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -61,7 +61,7 @@ import java.util.HashMap; import java.util.Map; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; - private static final boolean DBG = true; + private static final boolean DBG = false; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -227,21 +227,23 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { + if (DBG) Log.d(TAG, "Bluetooth user switched"); mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + if (DBG) Log.d(TAG, "Bluetooth boot completed"); synchronized(mReceiver) { if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { //Enable if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); } - } - - if (!isNameAndAddressSet()) { - // Sync the Bluetooth name and address from the Bluetooth Adapter - if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address..."); - getNameAndAddress(); + if (!isNameAndAddressSet()) { + // Sync the Bluetooth name and address from the + // Bluetooth Adapter + if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address..."); + getNameAndAddress(); + } } } } @@ -1099,7 +1101,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { boolean unbind = false; if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS"); synchronized(mConnection) { - if (!mEnable && mBluetooth != null) { + if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) { try { mBluetooth.enable(); } catch (RemoteException e) { @@ -1107,7 +1109,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } - if (mBluetooth != null) waitForOnOff(true, false); + if (mBluetooth != null && !mConnection.isGetNameAddressOnly()) waitForOnOff(true, false); synchronized(mConnection) { if (mBluetooth != null) { String name = null; @@ -1137,7 +1139,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } - if (!mEnable) { + if (!mEnable && !mConnection.isGetNameAddressOnly()) { try { mBluetooth.disable(); } catch (RemoteException e) { @@ -1152,7 +1154,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessage(getMsg); } } - if (!mEnable && mBluetooth != null) waitForOnOff(false, true); + if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) { + waitForOnOff(false, true); + } if (unbind) { unbindAndFinish(); } -- GitLab From 5f1a96fb7a27d2f66a9c66a05d2945a864108df5 Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Sat, 20 Jun 2015 12:05:52 -0700 Subject: [PATCH 0493/1408] Dump bonded Bluetooth devices in dumpsys. Change-Id: Ie509b0abc64cbc7813e9fe844ff4c05d8cb05f9d --- .../bluetooth/BluetoothManagerService.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0c726f313d9..063144741b8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothGatt; @@ -115,6 +116,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; + private static final String[] DEVICE_TYPE_NAMES = new String[] { + "???", + "BR/EDR", + "LE", + "DUAL" + }; + private final Context mContext; // Locks are not provided for mName and mAddress. @@ -1530,12 +1538,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" state: " + mState); writer.println(" address: " + mAddress); writer.println(" name: " + mName + "\n"); - writer.flush(); if (mBluetooth == null) { writer.println("Bluetooth Service not connected"); } else { try { + writer.println("Bonded devices:"); + for (BluetoothDevice device : mBluetooth.getBondedDevices()) { + writer.println(" " + device.getAddress() + + " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " + + device.getName()); + } + writer.flush(); + ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd); mBluetooth.dump(pfd); pfd.close(); -- GitLab From e65babd4df9b3f9a0ade1f375e143f17191c928b Mon Sep 17 00:00:00 2001 From: Jerome Poichet Date: Thu, 25 Jun 2015 12:03:02 -0700 Subject: [PATCH 0494/1408] Preventing leak When transitioning from ON to OFF with LE Advertisers, advertiser do not get a chance to unregister themselves as the stopAdvertising checks the state of the stack and throws before unregistering the object. It will then never remove the callback objects causing a leak. b/22092678 | Remote service crash after switching to restricted profile Change-Id: I04817026a524d10d60abdd8b533554a71a0112e2 --- framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 67d9de59409..eaf20d8e19b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -152,7 +152,6 @@ public final class BluetoothLeAdvertiser { */ public void stopAdvertising(final AdvertiseCallback callback) { synchronized (mLeAdvertisers) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } -- GitLab From 1e7ac062da74127adf1a5e7a1f5fb74bb493fdeb Mon Sep 17 00:00:00 2001 From: Miao Chou Date: Fri, 26 Jun 2015 17:14:35 -0700 Subject: [PATCH 0495/1408] Add BluetoothService to reduce resuming time after reboot As a subclass of SystemService, BluetoothService wraps around BluetoothManagerService to unlock Auto-enabling Bluetooth earlier by overriding onBootPhase() and removes the need to wait for BOOT_COMPLETED message. Bug:21705209 Change-Id: I2acc41370a750d8416e11e662e06392326741d2c --- .../bluetooth/BluetoothManagerService.java | 49 ++++++++++-------- .../server/bluetooth/BluetoothService.java | 51 +++++++++++++++++++ 2 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 service/java/com/android/server/bluetooth/BluetoothService.java diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 25f8872fa53..b9f62e67856 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = false; @@ -234,25 +235,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } } - } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { - if (DBG) Log.d(TAG, "Bluetooth user switched"); - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); - } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { - if (DBG) Log.d(TAG, "Bluetooth boot completed"); - synchronized(mReceiver) { - if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { - //Enable - if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal); - } - if (!isNameAndAddressSet()) { - // Sync the Bluetooth name and address from the - // Bluetooth Adapter - if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address..."); - getNameAndAddress(); - } - } } } }; @@ -277,9 +259,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); - IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - filter.addAction(Intent.ACTION_USER_SWITCHED); + IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); registerForAirplaneMode(filter); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); @@ -789,6 +769,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Send enable message and set adapter name and address. Called when the boot phase becomes + * PHASE_SYSTEM_SERVICES_READY. + */ + public void handleOnBootPhase() { + if (DBG) Log.d(TAG, "Bluetooth boot completed"); + if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { + if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); + sendEnableMsg(mQuietEnableExternal); + } + if (!isNameAndAddressSet()) { + // Sync the Bluetooth name and address from the Bluetooth Adapter + if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address..."); + getNameAndAddress(); + } + } + + /** + * Called when switching to a different foreground user. + */ + public void handleOnSwitchUser(int userHandle) { + if (DBG) Log.d(TAG, "Bluetooth user switched"); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0)); + } + /** * This class manages the clients connected to a given ProfileService * and maintains the connection with that service. diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java new file mode 100644 index 00000000000..73e8c52a3e6 --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.util.Log; + +class BluetoothService extends SystemService { + private static final String TAG = "BluetoothService"; + private BluetoothManagerService mBluetoothManagerService; + + public BluetoothService(Context context) { + super(context); + mBluetoothManagerService = new BluetoothManagerService(context); + } + + @Override + public void onStart() { + Log.d(TAG, "onStart: publishing BluetoothManagerService"); + publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); + mBluetoothManagerService.handleOnBootPhase(); + } + } + + @Override + public void onSwitchUser(int userHandle) { + Log.d(TAG, "onSwitchUser: switching to user " + userHandle); + mBluetoothManagerService.handleOnSwitchUser(userHandle); + } +} -- GitLab From a25c8ff64c627cfca23367845383e30ee8b1a0a1 Mon Sep 17 00:00:00 2001 From: Fyodor Kupolov Date: Fri, 19 Jun 2015 15:35:11 -0700 Subject: [PATCH 0496/1408] Do not return devices when caller has no location permission During startLeScan, do not return devices if calling process has no location permission/appop Bug: 21852542 Change-Id: Idce8f4704558574e373e8144390f2a2fa1a7e84a --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 2 +- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 72abeaf699c..3660be7c8eb 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -36,7 +36,7 @@ interface IBluetoothGatt { void startScan(in int appIf, in boolean isServer, in ScanSettings settings, in List filters, - in List scanStorages); + in List scanStorages, in String callingPackage); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2e6c4f03d8b..e09ab5676de 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -19,6 +19,7 @@ package android.bluetooth.le; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallbackWrapper; @@ -313,7 +314,7 @@ public final class BluetoothLeScanner { mClientIf = clientIf; try { mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, - mResultStorages); + mResultStorages, ActivityThread.currentOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; -- GitLab From c6242459415fe6bd92aa1e0d69dd63edb339f7b7 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Mon, 13 Jul 2015 18:00:35 -0700 Subject: [PATCH 0497/1408] Fix BluetoothSocket file descriptor leak Sockets accepted on a server socket didn't populate the mPfd field, which is used to close out the java end of the native-and-java communication socket when the overall rfcomm socket is closed. #badnewsbears b/21398841 Change-Id: I3adb0a9965f83d0f3006fa4f79ea4abeab5c9a17 --- framework/java/android/bluetooth/BluetoothSocket.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 6302521e634..fb81fd1ea9d 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -240,6 +240,8 @@ public final class BluetoothSocket implements Closeable { as.close(); throw new IOException("bt socket acept failed"); } + + as.mPfd = new ParcelFileDescriptor(fds[0]); as.mSocket = new LocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From 2e8a27e8380a85752619af2eef4f1a8635fce3cc Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Mon, 13 Jul 2015 18:00:35 -0700 Subject: [PATCH 0498/1408] Fix BluetoothSocket file descriptor leak Sockets accepted on a server socket didn't populate the mPfd field, which is used to close out the java end of the native-and-java communication socket when the overall rfcomm socket is closed. #badnewsbears b/21398841 Change-Id: I3adb0a9965f83d0f3006fa4f79ea4abeab5c9a17 --- framework/java/android/bluetooth/BluetoothSocket.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 36997e54484..386a1e0d169 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -191,6 +191,8 @@ public final class BluetoothSocket implements Closeable { as.close(); throw new IOException("bt socket acept failed"); } + + as.mPfd = new ParcelFileDescriptor(fds[0]); as.mSocket = new LocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From 850dfffcf087a9a28515bc18fbb5a2b7553e6a1c Mon Sep 17 00:00:00 2001 From: Fyodor Kupolov Date: Tue, 14 Jul 2015 11:38:58 -0700 Subject: [PATCH 0499/1408] Require ACCESS_COARSE_LOCATION for ACTION_FOUND broadcast Receivers of ACTION_FOUND intent are now required to have ACCESS_COARSE_LOCATION permission. Bug: 21852542 Change-Id: I8306f7431f067ca36bfc568a912385153fa3d496 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c96fe71e797..d27dfa04faf 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -92,7 +92,8 @@ public final class BluetoothDevice implements Parcelable { *

        Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or * {@link #EXTRA_RSSI} if they are available. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} and + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive. */ // TODO: Change API to not broadcast RSSI if not available (incoming connection) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) -- GitLab From 45810f40d82f0547593b4d88e40decfa4e5cf9fd Mon Sep 17 00:00:00 2001 From: Fyodor Kupolov Date: Thu, 16 Jul 2015 19:40:13 -0700 Subject: [PATCH 0500/1408] Added a note about location permission ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission is required to get results. Bug: 21852542 Change-Id: I3a2746d691f4d7024b0bb7b884d4436c73ce82e6 --- .../java/android/bluetooth/le/BluetoothLeScanner.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index e09ab5676de..2ba87744d08 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -79,6 +79,10 @@ public final class BluetoothLeScanner { * delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * An app must hold + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission + * in order to get results. * * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code callback} is null. @@ -95,6 +99,10 @@ public final class BluetoothLeScanner { * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * An app must hold + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission + * in order to get results. * * @param filters {@link ScanFilter}s for finding exact BLE devices. * @param settings Settings for the scan. -- GitLab From 62aa79cf68c9b3ee2fc16a38da14c1857a28d13c Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Thu, 30 Jul 2015 08:59:32 -0700 Subject: [PATCH 0501/1408] Bluetooth: Don't call beginBroadcast() while in a broadcast Block duplicate calls to beginBroadcast() and add try/finally to ensure finishBroadcast() is always called. Bug: 22800686 Change-Id: Ie8d4005f4cd50dd2544a2832773d72eab0015d92 --- .../bluetooth/BluetoothManagerService.java | 112 +++++++++++------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b9f62e67856..10a4cd1dae8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -805,6 +805,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IBinder mService; ComponentName mClassName; Intent mIntent; + boolean mInvokingProxyCallbacks = false; ProfileServiceConnections(Intent intent) { mService = null; @@ -871,34 +872,54 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException e) { Log.e(TAG, "Unable to linkToDeath", e); } - int n = mProxies.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mProxies.getBroadcastItem(i).onServiceConnected(className, service); - } catch (RemoteException e) { - Log.e(TAG, "Unable to connect to proxy", e); + + if (mInvokingProxyCallbacks) { + Log.e(TAG, "Proxy callbacks already in progress."); + return; + } + mInvokingProxyCallbacks = true; + + final int n = mProxies.beginBroadcast(); + try { + for (int i = 0; i < n; i++) { + try { + mProxies.getBroadcastItem(i).onServiceConnected(className, service); + } catch (RemoteException e) { + Log.e(TAG, "Unable to connect to proxy", e); + } } + } finally { + mProxies.finishBroadcast(); + mInvokingProxyCallbacks = false; } - mProxies.finishBroadcast(); } @Override public void onServiceDisconnected(ComponentName className) { - if (mService == null) { - return; - } + if (mService == null) return; mService.unlinkToDeath(this, 0); mService = null; mClassName = null; - int n = mProxies.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - mProxies.getBroadcastItem(i).onServiceDisconnected(className); - } catch (RemoteException e) { - Log.e(TAG, "Unable to disconnect from proxy", e); + + if (mInvokingProxyCallbacks) { + Log.e(TAG, "Proxy callbacks already in progress."); + return; + } + mInvokingProxyCallbacks = true; + + final int n = mProxies.beginBroadcast(); + try { + for (int i = 0; i < n; i++) { + try { + mProxies.getBroadcastItem(i).onServiceDisconnected(className); + } catch (RemoteException e) { + Log.e(TAG, "Unable to disconnect from proxy", e); + } } + } finally { + mProxies.finishBroadcast(); + mInvokingProxyCallbacks = false; } - mProxies.finishBroadcast(); } @Override @@ -916,16 +937,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendBluetoothStateCallback(boolean isUp) { - int n = mStateChangeCallbacks.beginBroadcast(); - if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); - for (int i=0; i Date: Fri, 31 Jul 2015 14:43:15 +0100 Subject: [PATCH 0502/1408] Fix wrong javadoc in BluetoothAdapter. Calling getSystemService(BLUETOOTH_SERVICE) returns a BluetoothManager. See ag/661924. Change-Id: I589837b9db3b7e0bc0639c85f4acf23a8f9fb7d1 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 97afafa4c8b..4316c55ccf8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -64,9 +64,7 @@ import java.util.UUID; *

        To get a {@link BluetoothAdapter} representing the local Bluetooth * adapter, when running on JELLY_BEAN_MR1 and below, call the * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and - * higher, retrieve it through - * {@link android.content.Context#getSystemService} with - * {@link android.content.Context#BLUETOOTH_SERVICE}. + * higher, call {@link BluetoothManager#getAdapter}. * Fundamentally, this is your starting point for all * Bluetooth actions. Once you have the local adapter, you can get a set of * {@link BluetoothDevice} objects representing all paired devices with -- GitLab From 4737654a1d7a11107696a394733f3ec367957496 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Mon, 3 Aug 2015 17:44:47 -0700 Subject: [PATCH 0503/1408] Reset the pointer to the Bluetooth GATT service During error recovery, if the mBluetooth pointer is reset to null, reset the mBluetoothGatt pointer as well. Bug: 21756298 Change-Id: I26204ba47dd3c5465bb7de30cfa5dc0f07eee2fd --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 10a4cd1dae8..50bd544a308 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1506,6 +1506,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Unbind mContext.unbindService(mConnection); } + mBluetoothGatt = null; } SystemClock.sleep(100); @@ -1811,6 +1812,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Unbind mContext.unbindService(mConnection); } + mBluetoothGatt = null; } mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); -- GitLab From e1601facd17c1d0b6f74e86fa19187126060fc45 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Tue, 28 Jul 2015 16:52:09 -0700 Subject: [PATCH 0504/1408] Implement Bluetooth settings factory reset (2/5) Implemented the factory reset function to be used to reset all bluetooth settings on device to factory default Bug: 16161518 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 20 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 97afafa4c8b..1f3ff514871 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1001,6 +1001,25 @@ public final class BluetoothAdapter { return false; } + /** + * Factory reset bluetooth settings. + * + *

        Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * permission + * + * @return true to indicate that the config file was successfully cleared + * + * @hide + */ + public boolean factoryReset() { + try { + if (mService != null) { + return mService.factoryReset(); + } + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Get the UUIDs supported by the local Bluetooth adapter. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 7a894ae67af..66f3418c620 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -92,6 +92,7 @@ interface IBluetooth ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); boolean configHciSnoopLog(boolean enable); + boolean factoryReset(); boolean isMultiAdvertisementSupported(); boolean isPeripheralModeSupported(); -- GitLab From 1d8cdf3934897e3d08f002ba2dbff6dad4839191 Mon Sep 17 00:00:00 2001 From: Joshua Schwarz Date: Tue, 1 Sep 2015 18:03:18 -0700 Subject: [PATCH 0505/1408] Use LOCAL_MAC_ADDRESS permission in bluetoothtests. Change-Id: If2b0797a4bc526e1fdb058d83d4d2bc676875cee --- framework/tests/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index e43ba12bcaa..7f9d8749358 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -21,6 +21,7 @@ + -- GitLab From 71bd291257152ddb6520b326b03526161b5885e3 Mon Sep 17 00:00:00 2001 From: Joe LaPenna Date: Fri, 4 Sep 2015 12:52:42 -0700 Subject: [PATCH 0506/1408] Do not wtf when the systemui is not present. Not all devices, like wearables actually have a system ui. BUG: 23822226 Change-Id: Ibc2d6c3b8db1de668b9cbced3efcec5a7a39f20b --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 50bd544a308..d5c4a41f07b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -273,7 +273,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui", UserHandle.USER_OWNER); } catch (PackageManager.NameNotFoundException e) { - Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e); + // Some platforms, such as wearables do not have a system ui. + Log.w(TAG, "Unable to resolve SystemUI's UID.", e); } mSystemUiUid = sysUiUid; } -- GitLab From 0b0bce04c28537744a927c4b8b34360587659ca3 Mon Sep 17 00:00:00 2001 From: Xiaohui Chen Date: Wed, 16 Sep 2015 09:56:14 -0700 Subject: [PATCH 0507/1408] Cleanup USER_OWNER in various services Bug: 19913735 Change-Id: I980370bab18e1b9ccf4043eed2b9fd721a940f72 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d5c4a41f07b..fd67b4148b8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -271,7 +271,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int sysUiUid = -1; try { sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui", - UserHandle.USER_OWNER); + UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Log.w(TAG, "Unable to resolve SystemUI's UID.", e); -- GitLab From 66b6bbe30515173e6cfa03ce28c7ec78645a6e76 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 28 Oct 2015 21:06:27 -0700 Subject: [PATCH 0508/1408] Declare new interface for headset client bluetooth profile to query. This is needed in order to allow implementations of the HFP HF side to define when audio can be routed to the device. This allows for calls dialed from an AG to be kept on the AG if desired. Bug: 25332357 Change-Id: I35a554cfc53f88c7dd3059bf52df5c69df9c7415 --- .../IBluetoothHeadsetClientController.aidl | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl new file mode 100644 index 00000000000..8f940f31176 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 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; + +/** + * Controller to specify behavior of bluetooth headset client. + * @hide + */ +interface IBluetoothHeadsetClientController { + boolean allowAudioConnect(); +} -- GitLab From b411815bb732c63ead353da27cf94129efdf271c Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Thu, 29 Oct 2015 04:58:38 +0000 Subject: [PATCH 0509/1408] Revert "Declare new interface for headset client bluetooth profile to query." This reverts commit 66b6bbe30515173e6cfa03ce28c7ec78645a6e76. Change-Id: I588206d8aedb96254c27494dddb32ddc3b6ea0ea --- .../IBluetoothHeadsetClientController.aidl | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl deleted file mode 100644 index 8f940f31176..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClientController.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2015 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; - -/** - * Controller to specify behavior of bluetooth headset client. - * @hide - */ -interface IBluetoothHeadsetClientController { - boolean allowAudioConnect(); -} -- GitLab From 6fe0e74aad0216116a09ecb6d85091c9e8a36f6f Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 28 Oct 2015 22:21:54 -0700 Subject: [PATCH 0510/1408] Add ability to set whether audio route is allowed in BluetoothHeadsetClient Bug: 25332357 Change-Id: I942ac0dea3e4f8580c44e027a570d0b59822a257 --- .../bluetooth/BluetoothHeadsetClient.java | 35 +++++++++++++++++++ .../bluetooth/IBluetoothHeadsetClient.aidl | 2 ++ 2 files changed, 37 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index ff4ebee221e..874026fb157 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -1058,6 +1058,41 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; } + /** + * Sets whether audio routing is allowed. + * + * Note: This is an internal function and shouldn't be exposed + */ + public void setAudioRouteAllowed(boolean allowed) { + if (VDBG) log("setAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + mService.setAudioRouteAllowed(allowed); + } 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())); + } + } + + /** + * Returns whether audio routing is allowed. + * + * Note: This is an internal function and shouldn't be exposed + */ + public boolean getAudioRouteAllowed() { + if (VDBG) log("getAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioRouteAllowed(); + } 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; + } + /** * Initiates a connection of audio channel. * diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index e518b7d225f..79ae4e48fa7 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -62,6 +62,8 @@ interface IBluetoothHeadsetClient { int getAudioState(in BluetoothDevice device); boolean connectAudio(); boolean disconnectAudio(); + void setAudioRouteAllowed(boolean allowed); + boolean getAudioRouteAllowed(); Bundle getCurrentAgFeatures(in BluetoothDevice device); } -- GitLab From c3ce4172e58781e13b80c04c7ae10fb6f9c336d9 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 11 Nov 2015 21:43:26 -0800 Subject: [PATCH 0511/1408] Move Bluetooth startup to PHASE_ACTIVITY_MANAGER_READY For some reason BluetoothManagerService recently has been unable to bind to the BluetoothService, causing timeouts, which in turn could lead to Bluetooth not working immediately after boot. While refactoring that code, the "get name and address" code was also removed to make the startup sequence more transparent and easier to debug. Bug: 25597150 Change-Id: I62ffe4589a76ea8a6db75e3c7599f149db677ea3 --- .../bluetooth/BluetoothManagerService.java | 246 ++++-------------- .../server/bluetooth/BluetoothService.java | 5 +- 2 files changed, 47 insertions(+), 204 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index fd67b4148b8..f329cff2c98 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -95,8 +95,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60; private static final int MESSAGE_TIMEOUT_BIND =100; private static final int MESSAGE_TIMEOUT_UNBIND =101; - private static final int MESSAGE_GET_NAME_AND_ADDRESS=200; - private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201; private static final int MESSAGE_USER_SWITCHED = 300; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; @@ -587,15 +585,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** @hide*/ - public void getNameAndAddress() { - if (DBG) { - Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth + - " mBinding = " + mBinding); - } - Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); - mHandler.sendMessage(msg); - } public boolean enableNoAutoConnect() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, @@ -681,14 +670,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mUnbinding) return; mUnbinding = true; if (mBluetooth != null) { - if (!mConnection.isGetNameAddressOnly()) { - //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothCallback",re); - } + //Unregister callback object + try { + mBluetooth.unregisterCallback(mBluetoothCallback); + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister BluetoothCallback",re); } + if (DBG) Log.d(TAG, "Sending unbind request."); mBluetooth = null; //Unbind @@ -780,11 +768,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); } - if (!isNameAndAddressSet()) { - // Sync the Bluetooth name and address from the Bluetooth Adapter - if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address..."); - getNameAndAddress(); - } } /** @@ -957,42 +940,38 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is up */ private void sendBluetoothServiceUpCallback() { - if (!mConnection.isGetNameAddressOnly()) { - if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks"); - try { - int n = mCallbacks.beginBroadcast(); - Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers."); - for (int i=0; i Date: Mon, 16 Nov 2015 08:55:52 -0800 Subject: [PATCH 0512/1408] Add Bluetooth headset API to allow disabling audio route. This functionality is required by devices which have multiple profiles using the SCO channel. For example, a device that is both a HFP AG and HF. In this case, we must explicitly notify the profiles when they can acquire the channel as they are not aware of each other. A similar change was previously added to the Bluetooth Headset Client profile. Bug: 25485578 Change-Id: Ia60cfdd33c2c3c3f185464b24056f8ccb976056d --- .../android/bluetooth/BluetoothHeadset.java | 42 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 2 + 2 files changed, 44 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 25d9aa9bae6..09a15de8778 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -684,6 +684,48 @@ public final class BluetoothHeadset implements BluetoothProfile { return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; } + /** + * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any + * audio to the HF unless explicitly told to. + * This method should be used in cases where the SCO channel is shared between multiple profiles + * and must be delegated by a source knowledgeable + * Note: This is an internal function and shouldn't be exposed + * + * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. + * + * @hide + */ + public void setAudioRouteAllowed(boolean allowed) { + if (VDBG) log("setAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + mService.setAudioRouteAllowed(allowed); + } 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())); + } + } + + /** + * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}. + * Note: This is an internal function and shouldn't be exposed + * + * @hide + */ + public boolean getAudioRouteAllowed() { + if (VDBG) log("getAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioRouteAllowed(); + } 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; + } + /** * Check if Bluetooth SCO audio is connected. * diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 0e23fada831..0bb4088f62c 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,6 +50,8 @@ interface IBluetoothHeadset { boolean isAudioOn(); boolean connectAudio(); boolean disconnectAudio(); + void setAudioRouteAllowed(boolean allowed); + boolean getAudioRouteAllowed(); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); -- GitLab From 6c1177ba5808b4bcc0db48c5c4eb658c4eb8a8f1 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 24 Nov 2015 18:19:06 +0000 Subject: [PATCH 0513/1408] Add thread safety documentation An upcoming change will remove "synchronized" from the API docs. This change documents those cases where the guarantees can be determined from code inspection. Bug: 25767152 Change-Id: I75083ce01513ed315222304fe3f34190d40748cb --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2d825fa6279..3e8a51e47c9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -74,6 +74,8 @@ import java.util.UUID; * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. * + *

        This class is thread safe. + * *

        Note: * Most methods require the {@link android.Manifest.permission#BLUETOOTH} * permission and some also require the @@ -82,7 +84,7 @@ import java.util.UUID; *

        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + * Bluetooth developer guide. *
        * * {@see BluetoothDevice} -- GitLab From 7f8ae923461fd06bb6bd7be615f8556ea625c03b Mon Sep 17 00:00:00 2001 From: Christine Hallstrom Date: Tue, 17 Nov 2015 12:24:07 -0800 Subject: [PATCH 0514/1408] Log API calls to createBond(), cancelBondProcess(), removeBond() In an attempt to better be able to debug Bluetooth-related parts of the system, add info-level logging including calling process and thread whenever the aforementioned functions are called. Bug: 25996120 Change-Id: I5a72b2ab929f805ec1c43cc54879ed079a8dfe34 --- .../java/android/bluetooth/BluetoothDevice.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d27dfa04faf..cd5c205d14d 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -25,6 +25,7 @@ import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -823,6 +824,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, TRANSPORT_AUTO); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -854,6 +858,9 @@ public final class BluetoothDevice implements Parcelable { throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); } try { + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, transport); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -922,6 +929,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "cancelBondProcess() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -943,6 +953,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "removeBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.removeBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; -- GitLab From 25eb356259df2e94cca64dd8b0c1f89219277e70 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Tue, 8 Dec 2015 11:22:31 -0800 Subject: [PATCH 0515/1408] Improve logging in BluetoothHeadsetClientCall. Bug: 26032099 Change-Id: I497edc9aa4c19cc7159993a899071e7b0c26db63 --- .../android/bluetooth/BluetoothHeadsetClientCall.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 7b5a045e27f..1fb7825fb9a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -172,8 +172,12 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } public String toString() { + return toString(false); + } + + public String toString(boolean loggable) { StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); - builder.append(mDevice); + builder.append(loggable ? mDevice.hashCode() : mDevice); builder.append(", mId: "); builder.append(mId); builder.append(", mState: "); @@ -189,7 +193,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { default: builder.append(mState); break; } builder.append(", mNumber: "); - builder.append(mNumber); + builder.append(loggable ? mNumber.hashCode() : mNumber); builder.append(", mMultiParty: "); builder.append(mMultiParty); builder.append(", mOutgoing: "); -- GitLab From 86a9d34edf9398a5afed9b3f0365096828f9e2c6 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Sun, 13 Dec 2015 18:01:17 +0000 Subject: [PATCH 0516/1408] empty commit to trigger build Change-Id: I3ec926126a0d9ee7d64ffc70ca7aa7c6c9e2a9c6 -- GitLab From 7b1e740c286aeea6a38d8f95ccb30d16ff599a19 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 11 Dec 2015 18:00:38 -0800 Subject: [PATCH 0517/1408] Frameworks/base: Use Arrays.toString Fix a couple of cases where Arrays.toString should be used. Bug: 19797138 Change-Id: I905fc79e63face9b26975320a92086c732bf6316 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3e8a51e47c9..eb4cb919ce1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2165,7 +2165,7 @@ public final class BluetoothAdapter { @Deprecated @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { - if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); + if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); if (callback == null) { if (DBG) Log.e(TAG, "startLeScan: null callback"); return false; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index fb81fd1ea9d..ae12c88ff23 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -27,6 +27,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import java.util.Locale; import java.util.UUID; import android.net.LocalSocket; @@ -234,9 +235,9 @@ public final class BluetoothSocket implements Closeable { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); + if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds)); if(fds == null || fds.length != 1) { - Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); + Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds)); as.close(); throw new IOException("bt socket acept failed"); } -- GitLab From a7a6569f2ff54bdab8387bc6695e0804507fb0ea Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 16 Dec 2015 13:32:28 -0800 Subject: [PATCH 0518/1408] Add setting for declaring disabled bluetooth profiles. Bug: 25900899 Change-Id: I420a7c590d72ba10f3e466d15dccfdbb520e810a --- .../java/android/bluetooth/BluetoothProfile.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index cbce22cdea6..6bf3fab71ec 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -130,6 +130,18 @@ public interface BluetoothProfile { */ public static final int HEADSET_CLIENT = 16; + /** + * HID Profile + * @hide + */ + public static final int HID = 17; + + /** + * HDP Profile + * @hide + */ + public static final int HDP = 18; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile -- GitLab From 78ee480752f381c121333c855d1c4dce22ac6d36 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Wed, 16 Dec 2015 15:33:59 -0800 Subject: [PATCH 0519/1408] Add UUID to BluetoothHeadsetClientCall This will allow for unique identification of a call as id only provides uniqueness per session. Bug: 26016489 Change-Id: I643d24bf5ff5680c26e530075649ef2a4f378295 --- .../bluetooth/BluetoothHeadsetClientCall.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 1fb7825fb9a..002f63f7426 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -19,6 +19,8 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import java.util.UUID; + /** * This class represents a single call, its state and properties. * It implements {@link Parcelable} for inter-process message passing. @@ -67,14 +69,21 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private String mNumber; private boolean mMultiParty; private final boolean mOutgoing; + private final UUID mUUID; /** * Creates BluetoothHeadsetClientCall instance. */ public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, boolean multiParty, boolean outgoing) { + this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing); + } + + public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state, + String number, boolean multiParty, boolean outgoing) { mDevice = device; mId = id; + mUUID = uuid; mState = state; mNumber = number != null ? number : ""; mMultiParty = multiParty; @@ -133,6 +142,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mId; } + /** + * Gets call's UUID. + * + * @return call uuid + * @hide + */ + public UUID getUUID() { + return mUUID; + } + /** * Gets call's current state. * @@ -180,6 +199,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { builder.append(loggable ? mDevice.hashCode() : mDevice); builder.append(", mId: "); builder.append(mId); + builder.append(", mUUID: "); + builder.append(mUUID); builder.append(", mState: "); switch (mState) { case CALL_STATE_ACTIVE: builder.append("ACTIVE"); break; @@ -210,8 +231,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { return new BluetoothHeadsetClientCall((BluetoothDevice)in.readParcelable(null), - in.readInt(), in.readInt(), in.readString(), - in.readInt() == 1, in.readInt() == 1); + in.readInt(), UUID.fromString(in.readString()), in.readInt(), + in.readString(), in.readInt() == 1, in.readInt() == 1); } @Override @@ -224,6 +245,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeParcelable(mDevice, 0); out.writeInt(mId); + out.writeString(mUUID.toString()); out.writeInt(mState); out.writeString(mNumber); out.writeInt(mMultiParty ? 1 : 0); -- GitLab From d046af60a998b45cac4e05e4cceee0ae27d6714e Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Fri, 18 Dec 2015 11:34:20 -0800 Subject: [PATCH 0520/1408] Remove duplicate Bluetooth profiles. Change-Id: I04fd867511713dd02ef249b113f7a46c69f22f98 --- .../java/android/bluetooth/BluetoothProfile.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 6bf3fab71ec..cbce22cdea6 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -130,18 +130,6 @@ public interface BluetoothProfile { */ public static final int HEADSET_CLIENT = 16; - /** - * HID Profile - * @hide - */ - public static final int HID = 17; - - /** - * HDP Profile - * @hide - */ - public static final int HDP = 18; - /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile -- GitLab From 11e052ef2d42708a3c41b96551cb3a01edd6a7fe Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 6 Jan 2016 14:51:50 -0700 Subject: [PATCH 0521/1408] Consistent naming for PackageManager methods. When hidden PackageManager methods take a userId argument, they should be named explicitly with the "AsUser" suffix. This fixes several lagging examples so that we can pave the way to safely start passing flags to new methods without scary overloading. Also fix spacing issues in various logging statements. Change-Id: I1e42f7f66427410275df713bea04f6e0445fba28 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f329cff2c98..3c91423a5d1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -268,7 +268,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int sysUiUid = -1; try { - sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui", + sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. -- GitLab From 4c9ad00c777847f2fb1d00b870e8c6e573985b7f Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 7 Jan 2016 18:50:29 -0700 Subject: [PATCH 0522/1408] More progress on triaging PackageManager callers. Catch a bunch of simple cases where the PackageManager flags are obvious. Add the ability to use the MATCH_SYSTEM_ONLY flag on PackageInfo and ApplicationInfo queries. Re-examine recent tasks after a user is unlocked, since some of the activities may now be available and runnable. Bug: 26471205, 26253870 Change-Id: I989d9f8409070e5cae13202b47e2c7de85bf4a5b --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 3c91423a5d1..c1a082b3cb1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -269,7 +269,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int sysUiUid = -1; try { sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", - UserHandle.USER_SYSTEM); + PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Log.w(TAG, "Unable to resolve SystemUI's UID.", e); -- GitLab From 25e84d4f5d76fdb421c597752117089b6c73e5b7 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 21 Oct 2015 18:23:27 -0700 Subject: [PATCH 0523/1408] Add support for AVRCP 1.3. * Add metadata support. * Add player settings support. * Add playback support. A2DP Settings App support. Bluetooth: A2DP Sink support for Settings App - add support for A2DP Sink in Settings App. This will enable connection initiation and updation on Settings App - add framework Apis to support A2DP Sink. Any third party Apps can access A2DP Sink priority of device and playing state of device - add support for key to set priority. This manages priority of device for A2DP Sink profile Change-Id: If5f9139f37cdb9d200387877c7801075205c78a0 --- .../android/bluetooth/BluetoothA2dpSink.java | 83 +++++++ .../bluetooth/BluetoothAvrcpController.java | 208 +++++++++++++++--- .../BluetoothAvrcpPlayerSettings.aidl | 19 ++ .../BluetoothAvrcpPlayerSettings.java | 189 ++++++++++++++++ .../android/bluetooth/BluetoothClass.java | 17 ++ .../android/bluetooth/IBluetoothA2dpSink.aidl | 3 + .../bluetooth/IBluetoothAvrcpController.aidl | 8 + 7 files changed, 501 insertions(+), 26 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothA2dpSink.java create mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl create mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothClass.java mode change 100644 => 100755 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java old mode 100644 new mode 100755 index 2e273459add..74302f27ec1 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -370,6 +370,89 @@ public final class BluetoothA2dpSink implements BluetoothProfile { return null; } + /** + * Set priority of the profile + * + *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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, "Stack:" + 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} + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + /** + * Check if A2DP profile is streaming music. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device BluetoothDevice device + */ + public boolean isA2dpPlaying(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.isA2dpPlaying(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index b53a8fc0f6f..444e4293fe2 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -20,6 +20,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.media.MediaMetadata; +import android.media.session.PlaybackState; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -28,8 +30,8 @@ import java.util.ArrayList; import java.util.List; /** - * This class provides the public APIs to control the Bluetooth AVRCP Controller - * profile. + * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently + * supports player information, playback support and track metadata. * *

        BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get @@ -39,7 +41,7 @@ import java.util.List; */ public final class BluetoothAvrcpController implements BluetoothProfile { private static final String TAG = "BluetoothAvrcpController"; - private static final boolean DBG = true; + private static final boolean DBG = false; private static final boolean VDBG = false; /** @@ -61,7 +63,63 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * receive. */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the change in metadata state of playing track on the AVRCP + * AG. + * + *

        This intent will have the two extras: + *

          + *
        • {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.
        • + *
        • {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback + * state.
        • + *
        + */ + public static final String ACTION_TRACK_EVENT = + "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT"; + + + /** + * Intent used to broadcast the change in player application setting state on AVRCP AG. + * + *

        This intent will have the following extras: + *

          + *
        • {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the + * most recent player setting.
        • + *
        + */ + public static final String ACTION_PLAYER_SETTING = + "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; + + public static final String EXTRA_METADATA = + "android.bluetooth.avrcp-controller.profile.extra.METADATA"; + + public static final String EXTRA_PLAYBACK = + "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK"; + + public static final String EXTRA_PLAYER_SETTING = + "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; + + /* + * KeyCoded for Pass Through Commands + */ + public static final int PASS_THRU_CMD_ID_PLAY = 0x44; + public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; + public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; + public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; + public static final int PASS_THRU_CMD_ID_STOP = 0x45; + public static final int PASS_THRU_CMD_ID_FF = 0x49; + public static final int PASS_THRU_CMD_ID_REWIND = 0x48; + public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; + public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; + /* Key State Variables */ + public static final int KEY_STATE_PRESSED = 0; + public static final int KEY_STATE_RELEASED = 1; + /* Group Navigation Key Codes */ + public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00; + public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01; + private Context mContext; private ServiceListener mServiceListener; @@ -69,33 +127,33 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private BluetoothAdapter mAdapter; 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); - } + 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); + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG,"Binding service..."); + doBind(); } + } catch (Exception re) { + Log.e(TAG,"",re); } } } - }; + } + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local @@ -223,6 +281,104 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); } + /** + * Gets the player application settings. + * + * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error. + */ + public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getPlayerSettings"); + BluetoothAvrcpPlayerSettings settings = null; + if (mService != null && isEnabled()) { + try { + settings = mService.getPlayerSettings(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetadata() " + e); + return null; + } + } + return settings; + } + + /** + * Gets the metadata for the current track. + * + * This should be usually called when application UI needs to be updated, eg. when the track + * changes or immediately after connecting and getting the current state. + * @return the {@link MediaMetadata} or {@link null} if there is an error. + */ + public MediaMetadata getMetadata(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getMetadata"); + MediaMetadata metadata = null; + if (mService != null && isEnabled()) { + try { + metadata = mService.getMetadata(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetadata() " + e); + return null; + } + } + return metadata; + } + + /** + * Gets the playback state for current track. + * + * When the application is first connecting it can use current track state to get playback info. + * For all further updates it should listen to notifications. + * @return the {@link PlaybackState} or {@link null} if there is an error. + */ + public PlaybackState getPlaybackState(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getPlaybackState"); + PlaybackState playbackState = null; + if (mService != null && isEnabled()) { + try { + playbackState = mService.getPlaybackState(device); + } catch (RemoteException e) { + Log.e(TAG, + "Error talking to BT service in getPlaybackState() " + e); + return null; + } + } + return playbackState; + } + + /** + * Sets the player app setting for current player. + * returns true in case setting is supported by remote, false otherwise + */ + public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { + if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); + if (mService != null && isEnabled()) { + try { + return mService.setPlayerApplicationSetting(plAppSetting); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /* + * Send Group Navigation Command to Remote. + * possible keycode values: next_grp, previous_grp defined above + */ + public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { + Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); + if (mService != null && isEnabled()) { + try { + mService.sendGroupNavigationCmd(device, keyCode, keyState); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl new file mode 100644 index 00000000000..590fd63eda8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 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; + +parcelable BluetoothAvrcpPlayerSettings; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java new file mode 100644 index 00000000000..927cb566651 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 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.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class used to identify settings associated with the player on AG. + * + * {@hide} + */ +public final class BluetoothAvrcpPlayerSettings implements Parcelable { + public static final String TAG = "BluetoothAvrcpPlayerSettings"; + + /** + * Equalizer setting. + */ + public static final int SETTING_EQUALIZER = 0x01; + + /** + * Repeat setting. + */ + public static final int SETTING_REPEAT = 0x02; + + /** + * Shuffle setting. + */ + public static final int SETTING_SHUFFLE = 0x04; + + /** + * Scan mode setting. + */ + public static final int SETTING_SCAN = 0x08; + + /** + * Invalid state. + * + * Used for returning error codes. + */ + public static final int STATE_INVALID = -1; + + /** + * OFF state. + * + * Denotes a general OFF state. Applies to all settings. + */ + public static final int STATE_OFF = 0x00; + + /** + * ON state. + * + * Applies to {@link SETTING_EQUALIZER}. + */ + public static final int STATE_ON = 0x01; + + /** + * Single track repeat. + * + * Applies only to {@link SETTING_REPEAT}. + */ + public static final int STATE_SINGLE_TRACK = 0x02; + + /** + * All track repeat/shuffle. + * + * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + */ + public static final int STATE_ALL_TRACK = 0x03; + + /** + * Group repeat/shuffle. + * + * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + */ + public static final int STATE_GROUP = 0x04; + + /** + * List of supported settings ORed. + */ + private int mSettings; + + /** + * Hash map of current capability values. + */ + private Map mSettingsValue = new HashMap(); + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSettings); + out.writeInt(mSettingsValue.size()); + for (int k : mSettingsValue.keySet()) { + out.writeInt(k); + out.writeInt(mSettingsValue.get(k)); + } + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) { + return new BluetoothAvrcpPlayerSettings(in); + } + + public BluetoothAvrcpPlayerSettings[] newArray(int size) { + return new BluetoothAvrcpPlayerSettings[size]; + } + }; + + private BluetoothAvrcpPlayerSettings(Parcel in) { + mSettings = in.readInt(); + int numSettings = in.readInt(); + for (int i = 0; i < numSettings; i++) { + mSettingsValue.put(in.readInt(), in.readInt()); + } + } + + /** + * Create a new player settings object. + * + * @param settings a ORed value of SETTINGS_* defined above. + */ + public BluetoothAvrcpPlayerSettings(int settings) { + mSettings = settings; + } + + /** + * Get the supported settings. + * + * @return int ORed value of supported settings. + */ + public int getSettings() { + return mSettings; + } + + /** + * Add a setting value. + * + * The setting must be part of possible settings in {@link getSettings()}. + * @param setting setting config. + * @param value value for the setting. + * @throws IllegalStateException if the setting is not supported. + */ + public void addSettingValue(int setting, int value) { + if ((setting & mSettings) == 0) { + Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); + throw new IllegalStateException("Setting not supported: " + setting); + } + mSettingsValue.put(setting, value); + } + + /** + * Get a setting value. + * + * The setting must be part of possible settings in {@link getSettings()}. + * @param setting setting config. + * @return value value for the setting. + * @throws IllegalStateException if the setting is not supported. + */ + public int getSettingValue(int setting) { + if ((setting & mSettings) == 0) { + Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); + throw new IllegalStateException("Setting not supported: " + setting); + } + Integer i = mSettingsValue.get(setting); + if (i == null) return -1; + return i; + } +} diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java old mode 100644 new mode 100755 index 54bf4afa93f..4a38287e7ca --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -283,6 +283,8 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_PANU = 4; /** @hide */ public static final int PROFILE_NAP = 5; + /** @hide */ + public static final int PROFILE_A2DP_SINK = 6; /** * Check class bits for possible bluetooth profile support. @@ -310,6 +312,21 @@ public final class BluetoothClass implements Parcelable { default: return false; } + } else if (profile == PROFILE_A2DP_SINK) { + if (hasService(Service.CAPTURE)) { + return true; + } + // By the A2DP spec, srcs must indicate the CAPTURE service. + // However if some device that do not, we try to + // match on some other class bits. + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_SET_TOP_BOX: + case Device.AUDIO_VIDEO_VCR : + return true; + default: + return false; + } } else if (profile == PROFILE_HEADSET) { // The render service class is required by the spec for HFP, so is a // pretty good signal diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl old mode 100644 new mode 100755 index b7c64767700..d1458246dfe --- a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -31,4 +31,7 @@ interface IBluetoothA2dpSink { List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + boolean isA2dpPlaying(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl index f917a50860b..f1288d02229 100644 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -16,7 +16,10 @@ package android.bluetooth; +import android.bluetooth.BluetoothAvrcpPlayerSettings; import android.bluetooth.BluetoothDevice; +import android.media.MediaMetadata; +import android.media.session.PlaybackState; /** * APIs for Bluetooth AVRCP controller service @@ -28,4 +31,9 @@ interface IBluetoothAvrcpController { List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); + BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); + MediaMetadata getMetadata(in BluetoothDevice device); + PlaybackState getPlaybackState(in BluetoothDevice device); + boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); + void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); } -- GitLab From cc8400670b30542f78d2abc3ccb8029a62ecb146 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 29 Dec 2015 13:19:21 -0800 Subject: [PATCH 0524/1408] Implementation of BluetoothDevice.createBondOutOfBand This patch implements out of band pairing that uses optional data. Currently, it works only for LE transport, using Temporary Key value. In the future fields might be added to OOBData to support other options for optional data. Change-Id: I0811182efb72ac5f80521ed4876b32603cb628c1 --- .../android/bluetooth/BluetoothDevice.java | 12 ++-- .../java/android/bluetooth/IBluetooth.aidl | 2 + framework/java/android/bluetooth/OobData.aidl | 19 ++++++ framework/java/android/bluetooth/OobData.java | 63 +++++++++++++++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 framework/java/android/bluetooth/OobData.aidl create mode 100644 framework/java/android/bluetooth/OobData.java diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cd5c205d14d..f43fb30abb1 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -879,18 +879,16 @@ public final class BluetoothDevice implements Parcelable { * *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @param hash - Simple Secure pairing hash - * @param randomizer - The random key obtained using OOB + * @param transport - Transport to use + * @param oobData - Out Of Band data * @return false on immediate error, true if bonding will begin * * @hide */ - public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) { - //TODO(BT) - /* + public boolean createBondOutOfBand(int transport, OobData oobData) { try { - return sService.createBondOutOfBand(this, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ + return sService.createBondOutOfBand(this, transport, oobData); + } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 66f3418c620..74cb0f6cd25 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -20,6 +20,7 @@ import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothStateChangeCallback; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothDevice; +import android.bluetooth.OobData; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; @@ -56,6 +57,7 @@ interface IBluetooth BluetoothDevice[] getBondedDevices(); boolean createBond(in BluetoothDevice device, in int transport); + boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData); boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); diff --git a/framework/java/android/bluetooth/OobData.aidl b/framework/java/android/bluetooth/OobData.aidl new file mode 100644 index 00000000000..d831c647769 --- /dev/null +++ b/framework/java/android/bluetooth/OobData.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable OobData; diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java new file mode 100644 index 00000000000..01f72efb22a --- /dev/null +++ b/framework/java/android/bluetooth/OobData.java @@ -0,0 +1,63 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import android.util.Log; + +/** + * Out Of Band Data for Bluetooth device. + */ +public class OobData implements Parcelable { + private byte[] securityManagerTk; + + public byte[] getSecurityManagerTk() { + return securityManagerTk; + } + + public void setSecurityManagerTk(byte[] securityManagerTk) { + this.securityManagerTk = securityManagerTk; + } + + public OobData() { } + + private OobData(Parcel in) { + securityManagerTk = in.createByteArray(); + } + + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(securityManagerTk); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public OobData createFromParcel(Parcel in) { + return new OobData(in); + } + + public OobData[] newArray(int size) { + return new OobData[size]; + } + }; +} \ No newline at end of file -- GitLab From c94b3a8452558749e8b929c3ea23272bf7d3ee3c Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 24 Nov 2015 18:19:06 +0000 Subject: [PATCH 0525/1408] Add thread safety documentation An upcoming change will remove "synchronized" from the API docs. This change documents those cases where the guarantees can be determined from code inspection. Bug: 25767152 (cherry-picked from commit bf0dc0fba790cf95f76870c37469703f8f20a57c) Change-Id: I328b96328e89950b90d537bf0a6a704242de4993 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1f3ff514871..6b3e7ab4eee 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -76,6 +76,8 @@ import java.util.UUID; * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. * + *

        This class is thread safe. + * *

        Note: * Most methods require the {@link android.Manifest.permission#BLUETOOTH} * permission and some also require the @@ -84,7 +86,7 @@ import java.util.UUID; *

        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + * Bluetooth developer guide. *
        * * {@see BluetoothDevice} -- GitLab From d4dc70d2d425b0e98eb2150af1e4f3b5a0afe35e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 15 Jan 2016 18:14:47 -0800 Subject: [PATCH 0526/1408] Add WorkSource to BLE scanning API This will allow apps that do work on behalf of others to correctly blame those apps for the power implications of BLE scanning. Bug:22718669 Change-Id: Ib64af0b6a5d37721a6067ac4e5c39c01f921b56b --- .../android/bluetooth/IBluetoothGatt.aidl | 5 +- .../bluetooth/le/BluetoothLeScanner.java | 64 +++++++++++++++---- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 3660be7c8eb..6b5f77faacd 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -23,6 +23,7 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; +import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; @@ -35,8 +36,8 @@ interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); void startScan(in int appIf, in boolean isServer, in ScanSettings settings, - in List filters, - in List scanStorages, in String callingPackage); + in List filters, in WorkSource workSource, in List scanStorages, + in String callingPackage); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2ba87744d08..03449ccec35 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.WorkSource; import android.util.Log; import java.util.ArrayList; @@ -89,9 +90,6 @@ public final class BluetoothLeScanner { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(final ScanCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback is null"); - } startScan(null, new ScanSettings.Builder().build(), callback); } @@ -112,14 +110,53 @@ public final class BluetoothLeScanner { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { - startScan(filters, settings, callback, null); + startScan(filters, settings, null, callback, null); + } + + /** + * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to + * specify on behalf of which application(s) the work is being done. + * + * @param workSource {@link WorkSource} identifying the application(s) for which to blame for + * the scan. + * @param callback Callback used to deliver scan results. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { + startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); + } + + /** + * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but + * allows the caller to specify on behalf of which application(s) the work is being done. + * + * @param filters {@link ScanFilter}s for finding exact BLE devices. + * @param settings Settings for the scan. + * @param workSource {@link WorkSource} identifying the application(s) for which to blame for + * the scan. + * @param callback Callback used to deliver scan results. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + public void startScanFromSource(List filters, ScanSettings settings, + final WorkSource workSource, final ScanCallback callback) { + startScan(filters, settings, workSource, callback, null); } private void startScan(List filters, ScanSettings settings, - final ScanCallback callback, List> resultStorages) { + final WorkSource workSource, final ScanCallback callback, + List> resultStorages) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (settings == null || callback == null) { - throw new IllegalArgumentException("settings or callback is null"); + if (callback == null) { + throw new IllegalArgumentException("callback is null"); + } + if (settings == null) { + throw new IllegalArgumentException("settings is null"); } synchronized (mLeScanClients) { if (mLeScanClients.containsKey(callback)) { @@ -152,7 +189,7 @@ public final class BluetoothLeScanner { return; } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, callback, resultStorages); + settings, workSource, callback, resultStorages); wrapper.startRegisteration(); } } @@ -215,7 +252,7 @@ public final class BluetoothLeScanner { scanFilters.add(filter.getFilter()); scanStorages.add(filter.getStorageDescriptors()); } - startScan(scanFilters, settings, callback, scanStorages); + startScan(scanFilters, settings, null, callback, scanStorages); } /** @@ -235,6 +272,7 @@ public final class BluetoothLeScanner { private final ScanCallback mScanCallback; private final List mFilters; + private final WorkSource mWorkSource; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; private List> mResultStorages; @@ -246,10 +284,12 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, - ScanCallback scanCallback, List> resultStorages) { + WorkSource workSource, ScanCallback scanCallback, + List> resultStorages) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; + mWorkSource = workSource; mScanCallback = scanCallback; mClientIf = 0; mResultStorages = resultStorages; @@ -322,7 +362,9 @@ public final class BluetoothLeScanner { mClientIf = clientIf; try { mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, - mResultStorages, ActivityThread.currentOpPackageName()); + mWorkSource, mResultStorages, + ActivityThread.currentOpPackageName()); + } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; -- GitLab From 3b3264d54efc8e9040b7f786e8f359f490b42283 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 4 Dec 2015 17:04:54 -0800 Subject: [PATCH 0527/1408] Record bytes transferred for bluetooth Bug:26039657 Change-Id: Iec42459d12f4106d5733f55313e8544d58930285 --- .../BluetoothActivityEnergyInfo.java | 45 +++++-- .../java/android/bluetooth/UidTraffic.java | 111 ++++++++++++++++++ 2 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 framework/java/android/bluetooth/UidTraffic.java diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index 834a587d83d..e32a47056fe 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -19,6 +19,8 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; + /** * Record of energy and activity information from controller and * underlying bt stack state.Timestamp the record with system @@ -27,11 +29,12 @@ import android.os.Parcelable; */ public final class BluetoothActivityEnergyInfo implements Parcelable { private final long mTimestamp; - private final int mBluetoothStackState; - private final long mControllerTxTimeMs; - private final long mControllerRxTimeMs; - private final long mControllerIdleTimeMs; - private final long mControllerEnergyUsed; + private int mBluetoothStackState; + private long mControllerTxTimeMs; + private long mControllerRxTimeMs; + private long mControllerIdleTimeMs; + private long mControllerEnergyUsed; + private UidTraffic[] mUidTraffic; public static final int BT_STACK_STATE_INVALID = 0; public static final int BT_STACK_STATE_STATE_ACTIVE = 1; @@ -48,6 +51,17 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { mControllerEnergyUsed = energyUsed; } + @SuppressWarnings("unchecked") + BluetoothActivityEnergyInfo(Parcel in) { + mTimestamp = in.readLong(); + mBluetoothStackState = in.readInt(); + mControllerTxTimeMs = in.readLong(); + mControllerRxTimeMs = in.readLong(); + mControllerIdleTimeMs = in.readLong(); + mControllerEnergyUsed = in.readLong(); + mUidTraffic = in.createTypedArray(UidTraffic.CREATOR); + } + @Override public String toString() { return "BluetoothActivityEnergyInfo{" @@ -57,26 +71,22 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { + " mControllerRxTimeMs=" + mControllerRxTimeMs + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + " mControllerEnergyUsed=" + mControllerEnergyUsed + + " mUidTraffic=" + Arrays.toString(mUidTraffic) + " }"; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { - long timestamp = in.readLong(); - int stackState = in.readInt(); - long txTime = in.readLong(); - long rxTime = in.readLong(); - long idleTime = in.readLong(); - long energyUsed = in.readLong(); - return new BluetoothActivityEnergyInfo(timestamp, stackState, - txTime, rxTime, idleTime, energyUsed); + return new BluetoothActivityEnergyInfo(in); } + public BluetoothActivityEnergyInfo[] newArray(int size) { return new BluetoothActivityEnergyInfo[size]; } }; + @SuppressWarnings("unchecked") public void writeToParcel(Parcel out, int flags) { out.writeLong(mTimestamp); out.writeInt(mBluetoothStackState); @@ -84,6 +94,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { out.writeLong(mControllerRxTimeMs); out.writeLong(mControllerIdleTimeMs); out.writeLong(mControllerEnergyUsed); + out.writeTypedArray(mUidTraffic, flags); } public int describeContents() { @@ -133,6 +144,14 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { return mTimestamp; } + public UidTraffic[] getUidTraffic() { + return mUidTraffic; + } + + public void setUidTraffic(UidTraffic[] traffic) { + mUidTraffic = traffic; + } + /** * @return if the record is valid */ diff --git a/framework/java/android/bluetooth/UidTraffic.java b/framework/java/android/bluetooth/UidTraffic.java new file mode 100644 index 00000000000..78013cc0cb0 --- /dev/null +++ b/framework/java/android/bluetooth/UidTraffic.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 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.os.Parcel; +import android.os.Parcelable; + +/** + * Record of data traffic (in bytes) by an application identified by its UID. + * @hide + */ +public class UidTraffic implements Cloneable, Parcelable { + private final int mAppUid; + private long mRxBytes; + private long mTxBytes; + + public UidTraffic(int appUid) { + mAppUid = appUid; + } + + public UidTraffic(int appUid, long rx, long tx) { + mAppUid = appUid; + mRxBytes = rx; + mTxBytes = tx; + } + + UidTraffic(Parcel in) { + mAppUid = in.readInt(); + mRxBytes = in.readLong(); + mTxBytes = in.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAppUid); + dest.writeLong(mRxBytes); + dest.writeLong(mTxBytes); + } + + public void setRxBytes(long bytes) { + mRxBytes = bytes; + } + + public void setTxBytes(long bytes) { + mTxBytes = bytes; + } + + public void addRxBytes(long bytes) { + mRxBytes += bytes; + } + + public void addTxBytes(long bytes) { + mTxBytes += bytes; + } + + public int getUid() { + return mAppUid; + } + + public long getRxBytes() { + return mRxBytes; + } + + public long getTxBytes() { + return mTxBytes; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public UidTraffic clone() { + return new UidTraffic(mAppUid, mRxBytes, mTxBytes); + } + + @Override + public String toString() { + return "UidTraffic{" + + "mAppUid=" + mAppUid + + ", mRxBytes=" + mRxBytes + + ", mTxBytes=" + mTxBytes + + '}'; + } + + public static final Creator CREATOR = new Creator() { + @Override + public UidTraffic createFromParcel(Parcel source) { + return new UidTraffic(source); + } + + @Override + public UidTraffic[] newArray(int size) { + return new UidTraffic[size]; + } + }; +} -- GitLab From 57fe38d7859d18ba8b8e96d689c892ae17a8145f Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 21 Oct 2015 18:23:27 -0700 Subject: [PATCH 0528/1408] Add support for AVRCP 1.3. * Add metadata support. * Add player settings support. * Add playback support. A2DP Settings App support. Bluetooth: A2DP Sink support for Settings App - add support for A2DP Sink in Settings App. This will enable connection initiation and updation on Settings App - add framework Apis to support A2DP Sink. Any third party Apps can access A2DP Sink priority of device and playing state of device - add support for key to set priority. This manages priority of device for A2DP Sink profile Change-Id: If5f9139f37cdb9d200387877c7801075205c78a0 --- .../android/bluetooth/BluetoothA2dpSink.java | 83 +++++++ .../bluetooth/BluetoothAvrcpController.java | 208 +++++++++++++++--- .../BluetoothAvrcpPlayerSettings.aidl | 19 ++ .../BluetoothAvrcpPlayerSettings.java | 189 ++++++++++++++++ .../android/bluetooth/BluetoothClass.java | 17 ++ .../android/bluetooth/IBluetoothA2dpSink.aidl | 3 + .../bluetooth/IBluetoothAvrcpController.aidl | 8 + 7 files changed, 501 insertions(+), 26 deletions(-) mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothA2dpSink.java create mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl create mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java mode change 100644 => 100755 framework/java/android/bluetooth/BluetoothClass.java mode change 100644 => 100755 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java old mode 100644 new mode 100755 index 2e273459add..74302f27ec1 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -370,6 +370,89 @@ public final class BluetoothA2dpSink implements BluetoothProfile { return null; } + /** + * Set priority of the profile + * + *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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, "Stack:" + 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} + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + /** + * Check if A2DP profile is streaming music. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device BluetoothDevice device + */ + public boolean isA2dpPlaying(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.isA2dpPlaying(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index b53a8fc0f6f..444e4293fe2 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -20,6 +20,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.media.MediaMetadata; +import android.media.session.PlaybackState; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -28,8 +30,8 @@ import java.util.ArrayList; import java.util.List; /** - * This class provides the public APIs to control the Bluetooth AVRCP Controller - * profile. + * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently + * supports player information, playback support and track metadata. * *

        BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get @@ -39,7 +41,7 @@ import java.util.List; */ public final class BluetoothAvrcpController implements BluetoothProfile { private static final String TAG = "BluetoothAvrcpController"; - private static final boolean DBG = true; + private static final boolean DBG = false; private static final boolean VDBG = false; /** @@ -61,7 +63,63 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * receive. */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the change in metadata state of playing track on the AVRCP + * AG. + * + *

        This intent will have the two extras: + *

          + *
        • {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.
        • + *
        • {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback + * state.
        • + *
        + */ + public static final String ACTION_TRACK_EVENT = + "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT"; + + + /** + * Intent used to broadcast the change in player application setting state on AVRCP AG. + * + *

        This intent will have the following extras: + *

          + *
        • {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the + * most recent player setting.
        • + *
        + */ + public static final String ACTION_PLAYER_SETTING = + "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; + + public static final String EXTRA_METADATA = + "android.bluetooth.avrcp-controller.profile.extra.METADATA"; + + public static final String EXTRA_PLAYBACK = + "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK"; + + public static final String EXTRA_PLAYER_SETTING = + "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; + + /* + * KeyCoded for Pass Through Commands + */ + public static final int PASS_THRU_CMD_ID_PLAY = 0x44; + public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; + public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; + public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; + public static final int PASS_THRU_CMD_ID_STOP = 0x45; + public static final int PASS_THRU_CMD_ID_FF = 0x49; + public static final int PASS_THRU_CMD_ID_REWIND = 0x48; + public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; + public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; + /* Key State Variables */ + public static final int KEY_STATE_PRESSED = 0; + public static final int KEY_STATE_RELEASED = 1; + /* Group Navigation Key Codes */ + public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00; + public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01; + private Context mContext; private ServiceListener mServiceListener; @@ -69,33 +127,33 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private BluetoothAdapter mAdapter; 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); - } + 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); + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG,"Binding service..."); + doBind(); } + } catch (Exception re) { + Log.e(TAG,"",re); } } } - }; + } + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local @@ -223,6 +281,104 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); } + /** + * Gets the player application settings. + * + * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error. + */ + public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getPlayerSettings"); + BluetoothAvrcpPlayerSettings settings = null; + if (mService != null && isEnabled()) { + try { + settings = mService.getPlayerSettings(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetadata() " + e); + return null; + } + } + return settings; + } + + /** + * Gets the metadata for the current track. + * + * This should be usually called when application UI needs to be updated, eg. when the track + * changes or immediately after connecting and getting the current state. + * @return the {@link MediaMetadata} or {@link null} if there is an error. + */ + public MediaMetadata getMetadata(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getMetadata"); + MediaMetadata metadata = null; + if (mService != null && isEnabled()) { + try { + metadata = mService.getMetadata(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetadata() " + e); + return null; + } + } + return metadata; + } + + /** + * Gets the playback state for current track. + * + * When the application is first connecting it can use current track state to get playback info. + * For all further updates it should listen to notifications. + * @return the {@link PlaybackState} or {@link null} if there is an error. + */ + public PlaybackState getPlaybackState(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getPlaybackState"); + PlaybackState playbackState = null; + if (mService != null && isEnabled()) { + try { + playbackState = mService.getPlaybackState(device); + } catch (RemoteException e) { + Log.e(TAG, + "Error talking to BT service in getPlaybackState() " + e); + return null; + } + } + return playbackState; + } + + /** + * Sets the player app setting for current player. + * returns true in case setting is supported by remote, false otherwise + */ + public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { + if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); + if (mService != null && isEnabled()) { + try { + return mService.setPlayerApplicationSetting(plAppSetting); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /* + * Send Group Navigation Command to Remote. + * possible keycode values: next_grp, previous_grp defined above + */ + public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { + Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); + if (mService != null && isEnabled()) { + try { + mService.sendGroupNavigationCmd(device, keyCode, keyState); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl new file mode 100644 index 00000000000..590fd63eda8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 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; + +parcelable BluetoothAvrcpPlayerSettings; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java new file mode 100644 index 00000000000..927cb566651 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 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.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class used to identify settings associated with the player on AG. + * + * {@hide} + */ +public final class BluetoothAvrcpPlayerSettings implements Parcelable { + public static final String TAG = "BluetoothAvrcpPlayerSettings"; + + /** + * Equalizer setting. + */ + public static final int SETTING_EQUALIZER = 0x01; + + /** + * Repeat setting. + */ + public static final int SETTING_REPEAT = 0x02; + + /** + * Shuffle setting. + */ + public static final int SETTING_SHUFFLE = 0x04; + + /** + * Scan mode setting. + */ + public static final int SETTING_SCAN = 0x08; + + /** + * Invalid state. + * + * Used for returning error codes. + */ + public static final int STATE_INVALID = -1; + + /** + * OFF state. + * + * Denotes a general OFF state. Applies to all settings. + */ + public static final int STATE_OFF = 0x00; + + /** + * ON state. + * + * Applies to {@link SETTING_EQUALIZER}. + */ + public static final int STATE_ON = 0x01; + + /** + * Single track repeat. + * + * Applies only to {@link SETTING_REPEAT}. + */ + public static final int STATE_SINGLE_TRACK = 0x02; + + /** + * All track repeat/shuffle. + * + * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + */ + public static final int STATE_ALL_TRACK = 0x03; + + /** + * Group repeat/shuffle. + * + * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + */ + public static final int STATE_GROUP = 0x04; + + /** + * List of supported settings ORed. + */ + private int mSettings; + + /** + * Hash map of current capability values. + */ + private Map mSettingsValue = new HashMap(); + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mSettings); + out.writeInt(mSettingsValue.size()); + for (int k : mSettingsValue.keySet()) { + out.writeInt(k); + out.writeInt(mSettingsValue.get(k)); + } + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) { + return new BluetoothAvrcpPlayerSettings(in); + } + + public BluetoothAvrcpPlayerSettings[] newArray(int size) { + return new BluetoothAvrcpPlayerSettings[size]; + } + }; + + private BluetoothAvrcpPlayerSettings(Parcel in) { + mSettings = in.readInt(); + int numSettings = in.readInt(); + for (int i = 0; i < numSettings; i++) { + mSettingsValue.put(in.readInt(), in.readInt()); + } + } + + /** + * Create a new player settings object. + * + * @param settings a ORed value of SETTINGS_* defined above. + */ + public BluetoothAvrcpPlayerSettings(int settings) { + mSettings = settings; + } + + /** + * Get the supported settings. + * + * @return int ORed value of supported settings. + */ + public int getSettings() { + return mSettings; + } + + /** + * Add a setting value. + * + * The setting must be part of possible settings in {@link getSettings()}. + * @param setting setting config. + * @param value value for the setting. + * @throws IllegalStateException if the setting is not supported. + */ + public void addSettingValue(int setting, int value) { + if ((setting & mSettings) == 0) { + Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); + throw new IllegalStateException("Setting not supported: " + setting); + } + mSettingsValue.put(setting, value); + } + + /** + * Get a setting value. + * + * The setting must be part of possible settings in {@link getSettings()}. + * @param setting setting config. + * @return value value for the setting. + * @throws IllegalStateException if the setting is not supported. + */ + public int getSettingValue(int setting) { + if ((setting & mSettings) == 0) { + Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); + throw new IllegalStateException("Setting not supported: " + setting); + } + Integer i = mSettingsValue.get(setting); + if (i == null) return -1; + return i; + } +} diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java old mode 100644 new mode 100755 index 54bf4afa93f..4a38287e7ca --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -283,6 +283,8 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_PANU = 4; /** @hide */ public static final int PROFILE_NAP = 5; + /** @hide */ + public static final int PROFILE_A2DP_SINK = 6; /** * Check class bits for possible bluetooth profile support. @@ -310,6 +312,21 @@ public final class BluetoothClass implements Parcelable { default: return false; } + } else if (profile == PROFILE_A2DP_SINK) { + if (hasService(Service.CAPTURE)) { + return true; + } + // By the A2DP spec, srcs must indicate the CAPTURE service. + // However if some device that do not, we try to + // match on some other class bits. + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_SET_TOP_BOX: + case Device.AUDIO_VIDEO_VCR : + return true; + default: + return false; + } } else if (profile == PROFILE_HEADSET) { // The render service class is required by the spec for HFP, so is a // pretty good signal diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl old mode 100644 new mode 100755 index b7c64767700..d1458246dfe --- a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -31,4 +31,7 @@ interface IBluetoothA2dpSink { List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + boolean isA2dpPlaying(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl index f917a50860b..f1288d02229 100644 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -16,7 +16,10 @@ package android.bluetooth; +import android.bluetooth.BluetoothAvrcpPlayerSettings; import android.bluetooth.BluetoothDevice; +import android.media.MediaMetadata; +import android.media.session.PlaybackState; /** * APIs for Bluetooth AVRCP controller service @@ -28,4 +31,9 @@ interface IBluetoothAvrcpController { List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); + BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); + MediaMetadata getMetadata(in BluetoothDevice device); + PlaybackState getPlaybackState(in BluetoothDevice device); + boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); + void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); } -- GitLab From 68c003edf491929b7c0be5b460787d62ba1bf82b Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Thu, 21 Jan 2016 14:29:42 -0800 Subject: [PATCH 0529/1408] DO NOT MERGE ANYWHERE Improve logging in BluetoothHeadsetClientCall. Change-Id: I3b1b3946ca21c0bbc902a2c521938239a486d98a --- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 002f63f7426..c73bc3cb53e 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -196,7 +196,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public String toString(boolean loggable) { StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); - builder.append(loggable ? mDevice.hashCode() : mDevice); + builder.append(loggable ? mDevice : mDevice.hashCode()); builder.append(", mId: "); builder.append(mId); builder.append(", mUUID: "); @@ -214,7 +214,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { default: builder.append(mState); break; } builder.append(", mNumber: "); - builder.append(loggable ? mNumber.hashCode() : mNumber); + builder.append(loggable ? mNumber : mNumber.hashCode()); builder.append(", mMultiParty: "); builder.append(mMultiParty); builder.append(", mOutgoing: "); -- GitLab From 7c798c7a118470195c6d0e7bb6593815fca1051f Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Thu, 21 Jan 2016 14:29:42 -0800 Subject: [PATCH 0530/1408] Improve logging in BluetoothHeadsetClientCall. Change-Id: I3b1b3946ca21c0bbc902a2c521938239a486d98a (cherry picked from commit 68c003edf491929b7c0be5b460787d62ba1bf82b) --- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 002f63f7426..c73bc3cb53e 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -196,7 +196,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public String toString(boolean loggable) { StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); - builder.append(loggable ? mDevice.hashCode() : mDevice); + builder.append(loggable ? mDevice : mDevice.hashCode()); builder.append(", mId: "); builder.append(mId); builder.append(", mUUID: "); @@ -214,7 +214,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { default: builder.append(mState); break; } builder.append(", mNumber: "); - builder.append(loggable ? mNumber.hashCode() : mNumber); + builder.append(loggable ? mNumber : mNumber.hashCode()); builder.append(", mMultiParty: "); builder.append(mMultiParty); builder.append(", mOutgoing: "); -- GitLab From ad548437c92ecfcf0aacb22603d6b3dce9322479 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 12 Jan 2016 16:05:15 -0800 Subject: [PATCH 0531/1408] IBluetooth: remove dump(), support arguments Because IBluetooth is a Binder service, we don't need dump() to support dumpsys, just call the IBinder.dump(). Change-Id: Idcd48f758427b824e0b7eaafd091ba3fb2ff8993 --- .../java/android/bluetooth/IBluetooth.aidl | 2 - .../bluetooth/BluetoothManagerService.java | 43 +++---------------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 66f3418c620..23581168058 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -102,8 +102,6 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // For dumpsys support - void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d5c4a41f07b..a2b1aa84418 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -116,13 +116,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; - private static final String[] DEVICE_TYPE_NAMES = new String[] { - "???", - "BR/EDR", - "LE", - "DUAL" - }; - private final Context mContext; private static int mBleAppCount = 0; @@ -133,6 +126,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ContentResolver mContentResolver; private final RemoteCallbackList mCallbacks; private final RemoteCallbackList mStateChangeCallbacks; + private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; private boolean mBinding; @@ -244,6 +238,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; mBluetooth = null; + mBluetoothBinder = null; mBluetoothGatt = null; mBinding = false; mUnbinding = false; @@ -690,6 +685,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (DBG) Log.d(TAG, "Sending unbind request."); + mBluetoothBinder = null; mBluetooth = null; //Unbind mContext.unbindService(mConnection); @@ -1296,6 +1292,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); mBinding = false; + mBluetoothBinder = service; mBluetooth = IBluetooth.Stub.asInterface(service); try { @@ -1834,41 +1831,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - writer.println("Bluetooth Status"); - writer.println(" enabled: " + mEnable); - writer.println(" state: " + mState); - writer.println(" address: " + mAddress); - writer.println(" name: " + mName + "\n"); - writer.flush(); - - if (mBluetooth == null) { + if (mBluetoothBinder == null) { writer.println("Bluetooth Service not connected"); } else { - ParcelFileDescriptor pfd = null; try { - writer.println("Bonded devices:"); - for (BluetoothDevice device : mBluetooth.getBondedDevices()) { - writer.println(" " + device.getAddress() + - " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " + - device.getName()); - } - writer.flush(); - - pfd = ParcelFileDescriptor.dup(fd); - mBluetooth.dump(pfd); + mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { writer.println("RemoteException while calling Bluetooth Service"); - } catch (IOException ioe) { - writer.println("IOException attempting to dup() fd"); - } finally { - if (pfd != null) { - try { - pfd.close(); - } catch (IOException ioe) { - writer.println("IOException attempting to close() fd"); - } - } } } } -- GitLab From cb60d2763eca79aa11a8c3e7c47ca72e1dbf5594 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 12 Jan 2016 16:05:15 -0800 Subject: [PATCH 0532/1408] IBluetooth: remove dump(), support arguments Because IBluetooth is a Binder service, we don't need dump() to support dumpsys, just call the IBinder.dump(). Change-Id: Idcd48f758427b824e0b7eaafd091ba3fb2ff8993 (cherry picked from commit adbb3ff062e039b2552c6b5ded92e56346ac3934) --- .../java/android/bluetooth/IBluetooth.aidl | 2 - .../bluetooth/BluetoothManagerService.java | 58 +++++-------------- 2 files changed, 13 insertions(+), 47 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 74cb0f6cd25..9cd7d0585af 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -104,8 +104,6 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // For dumpsys support - void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c1a082b3cb1..499b706a2b8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -114,13 +114,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; - private static final String[] DEVICE_TYPE_NAMES = new String[] { - "???", - "BR/EDR", - "LE", - "DUAL" - }; - private final Context mContext; private static int mBleAppCount = 0; @@ -131,6 +124,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ContentResolver mContentResolver; private final RemoteCallbackList mCallbacks; private final RemoteCallbackList mStateChangeCallbacks; + private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; private boolean mBinding; @@ -242,6 +236,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; mBluetooth = null; + mBluetoothBinder = null; mBluetoothGatt = null; mBinding = false; mUnbinding = false; @@ -678,6 +673,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (DBG) Log.d(TAG, "Sending unbind request."); + mBluetoothBinder = null; mBluetooth = null; //Unbind mContext.unbindService(mConnection); @@ -1166,6 +1162,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); mBinding = false; + mBluetoothBinder = service; mBluetooth = IBluetooth.Stub.asInterface(service); try { @@ -1674,44 +1671,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @Override - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - writer.println("Bluetooth Status"); - writer.println(" enabled: " + mEnable); - writer.println(" state: " + mState); - writer.println(" address: " + mAddress); - writer.println(" name: " + mName + "\n"); - writer.flush(); - - if (mBluetooth == null) { - writer.println("Bluetooth Service not connected"); - } else { - ParcelFileDescriptor pfd = null; - try { - writer.println("Bonded devices:"); - for (BluetoothDevice device : mBluetooth.getBondedDevices()) { - writer.println(" " + device.getAddress() + - " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " + - device.getName()); - } - writer.flush(); - - pfd = ParcelFileDescriptor.dup(fd); - mBluetooth.dump(pfd); - } catch (RemoteException re) { - writer.println("RemoteException while calling Bluetooth Service"); - } catch (IOException ioe) { - writer.println("IOException attempting to dup() fd"); - } finally { - if (pfd != null) { - try { - pfd.close(); - } catch (IOException ioe) { - writer.println("IOException attempting to close() fd"); - } - } - } + public void dump(FileDescriptor fd, PrintWriter writer, String args[]) { + if (mBluetoothBinder == null) { + writer.println("Bluetooth Service not connected"); + } else { + try { + mBluetoothBinder.dump(fd, args); + } catch (RemoteException re) { + writer.println("RemoteException while calling Bluetooth Service"); } + } } } -- GitLab From 5442f657017c3c527c64033011abecbbfe4196f7 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 29 Dec 2015 13:19:21 -0800 Subject: [PATCH 0533/1408] Implementation of BluetoothDevice.createBondOutOfBand This patch implements out of band pairing that uses optional data. Currently, it works only for LE transport, using Temporary Key value. In the future fields might be added to OOBData to support other options for optional data. Change-Id: I0811182efb72ac5f80521ed4876b32603cb628c1 --- .../android/bluetooth/BluetoothDevice.java | 12 ++-- .../java/android/bluetooth/IBluetooth.aidl | 2 + framework/java/android/bluetooth/OobData.aidl | 19 ++++++ framework/java/android/bluetooth/OobData.java | 63 +++++++++++++++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 framework/java/android/bluetooth/OobData.aidl create mode 100644 framework/java/android/bluetooth/OobData.java diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d27dfa04faf..9faba1a9f36 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -872,18 +872,16 @@ public final class BluetoothDevice implements Parcelable { * *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @param hash - Simple Secure pairing hash - * @param randomizer - The random key obtained using OOB + * @param transport - Transport to use + * @param oobData - Out Of Band data * @return false on immediate error, true if bonding will begin * * @hide */ - public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) { - //TODO(BT) - /* + public boolean createBondOutOfBand(int transport, OobData oobData) { try { - return sService.createBondOutOfBand(this, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ + return sService.createBondOutOfBand(this, transport, oobData); + } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 23581168058..9cd7d0585af 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -20,6 +20,7 @@ import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothStateChangeCallback; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothDevice; +import android.bluetooth.OobData; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; @@ -56,6 +57,7 @@ interface IBluetooth BluetoothDevice[] getBondedDevices(); boolean createBond(in BluetoothDevice device, in int transport); + boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData); boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); diff --git a/framework/java/android/bluetooth/OobData.aidl b/framework/java/android/bluetooth/OobData.aidl new file mode 100644 index 00000000000..d831c647769 --- /dev/null +++ b/framework/java/android/bluetooth/OobData.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable OobData; diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java new file mode 100644 index 00000000000..01f72efb22a --- /dev/null +++ b/framework/java/android/bluetooth/OobData.java @@ -0,0 +1,63 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import android.util.Log; + +/** + * Out Of Band Data for Bluetooth device. + */ +public class OobData implements Parcelable { + private byte[] securityManagerTk; + + public byte[] getSecurityManagerTk() { + return securityManagerTk; + } + + public void setSecurityManagerTk(byte[] securityManagerTk) { + this.securityManagerTk = securityManagerTk; + } + + public OobData() { } + + private OobData(Parcel in) { + securityManagerTk = in.createByteArray(); + } + + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(securityManagerTk); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public OobData createFromParcel(Parcel in) { + return new OobData(in); + } + + public OobData[] newArray(int size) { + return new OobData[size]; + } + }; +} \ No newline at end of file -- GitLab From 224812b70c0dec6be960324c9c26b78494344d60 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Wed, 17 Feb 2016 11:35:36 -0800 Subject: [PATCH 0534/1408] Restore DUMP permission check for BluetoothManagerService Bug: 27150536 Change-Id: I6e07f8f5f7476c6cd6dca79e2fb5e8c8412493ec --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 499b706a2b8..6d0d9e9090f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1672,6 +1672,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String args[]) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); if (mBluetoothBinder == null) { writer.println("Bluetooth Service not connected"); } else { -- GitLab From aac6f93ea4f08ca6aa923f8488d8b10df87c1cc7 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Tue, 23 Feb 2016 11:54:37 -0800 Subject: [PATCH 0535/1408] Suppress output for Protobuf data if Bluetooth is disabled If Bluetooth is disabled or there is some other error, don't print anything when extracting the Metrics-related data in Protobuf format. Bug: 27315491 Change-Id: Ic1ec5334fbf0b524909400f080e2eac3ec34edf4 --- .../server/bluetooth/BluetoothManagerService.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index a2b1aa84418..1e91a2c3a28 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1831,14 +1831,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + String errorMsg = null; if (mBluetoothBinder == null) { - writer.println("Bluetooth Service not connected"); + errorMsg = "Bluetooth Service not connected"; } else { try { mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { - writer.println("RemoteException while calling Bluetooth Service"); + errorMsg = "RemoteException while calling Bluetooth Service"; } } + if (errorMsg != null) { + // Silently return if we are extracting metrics in Protobuf format + if ((args.length > 0) && args[0].startsWith("--proto")) + return; + writer.println(errorMsg); + } } } -- GitLab From fbbadd9530c38c0668486374b081fbea0adb1e61 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Tue, 23 Feb 2016 11:54:37 -0800 Subject: [PATCH 0536/1408] Suppress output for Protobuf data if Bluetooth is disabled If Bluetooth is disabled or there is some other error, don't print anything when extracting the Metrics-related data in Protobuf format. [Cherry-pick from AOSP] Bug: 27315491 Change-Id: Ic1ec5334fbf0b524909400f080e2eac3ec34edf4 --- .../bluetooth/BluetoothManagerService.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6d0d9e9090f..8cfeb748cce 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1671,16 +1671,23 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @Override - public void dump(FileDescriptor fd, PrintWriter writer, String args[]) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - if (mBluetoothBinder == null) { - writer.println("Bluetooth Service not connected"); - } else { - try { - mBluetoothBinder.dump(fd, args); - } catch (RemoteException re) { - writer.println("RemoteException while calling Bluetooth Service"); + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + String errorMsg = null; + if (mBluetoothBinder == null) { + errorMsg = "Bluetooth Service not connected"; + } else { + try { + mBluetoothBinder.dump(fd, args); + } catch (RemoteException re) { + errorMsg = "RemoteException while calling Bluetooth Service"; + } + } + if (errorMsg != null) { + // Silently return if we are extracting metrics in Protobuf format + if ((args.length > 0) && args[0].startsWith("--proto")) + return; + writer.println(errorMsg); } - } } } -- GitLab From 5055a9204a01e73d0c3e75c9aec227d660e3b068 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Mon, 29 Feb 2016 14:59:07 -0800 Subject: [PATCH 0537/1408] DO NOT MERGE ANYWHERE Add way to query for supported Bluetooth profiles. Currently there is no way to get the profiles supported by the Bluetooth adapter. Asking for a profile proxy of an unsupported profile does not fail and can lead to code indefinitely waiting for the proxy response. This new code will allow for checking the supported profiles before asking for the proxies. Bug: 26451648 Change-Id: Ie209f08058205eea39513c03b626788f5fed3293 Signed-off-by: Bryce Lee --- .../android/bluetooth/BluetoothAdapter.java | 30 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 8 +++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 39 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1f3ff514871..cd040ba10d3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1420,6 +1420,36 @@ public final class BluetoothAdapter { return null; } + /** + * Gets the currently supported profiles by the adapter. + * + *

        This can be used to check whether a profile is supported before attempting + * to connect to its respective proxy. + * + * @return a list of integers indicating the ids of supported profiles as defined in + * {@link BluetoothProfile}. + * @hide + */ + public List getSupportedProfiles() { + final ArrayList supportedProfiles = new ArrayList(); + + try { + synchronized (mManagerCallback) { + if (mService != null) { + final long supportedProfilesBitMask = mService.getSupportedProfiles(); + + for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) { + if ((supportedProfilesBitMask & (1 << i)) != 0) { + supportedProfiles.add(i); + } + } + } + } + } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);} + + return supportedProfiles; + } + /** * Get the current connection state of the local Bluetooth adapter. * This can be used to check whether the local Bluetooth adapter is connected diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index cbce22cdea6..7ed0cd9c471 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -130,6 +130,14 @@ public interface BluetoothProfile { */ public static final int HEADSET_CLIENT = 16; + + /** + * Max profile ID. This value should be updated whenever a new profile is added to match + * the largest value assigned to a profile. + * @hide + */ + public static final int MAX_PROFILE_ID = 16; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 66f3418c620..aeb452fb757 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -59,6 +59,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); + long getSupportedProfiles(); int getConnectionState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); -- GitLab From 882b461ae58d654c3604840a700064fc3fa92ecd Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 29 Feb 2016 16:34:46 -0700 Subject: [PATCH 0538/1408] Parcelable classes should always be final. Also hide ConnectivityMetricsEvent which isn't being used yet. Bug: 27415331 Change-Id: Iacdccddda504f3f669185f807b4f35b8dc2b0212 --- framework/java/android/bluetooth/OobData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 01f72efb22a..2822df7318a 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -24,7 +24,7 @@ import android.util.Log; /** * Out Of Band Data for Bluetooth device. */ -public class OobData implements Parcelable { +public final class OobData implements Parcelable { private byte[] securityManagerTk; public byte[] getSecurityManagerTk() { -- GitLab From 4a5153566b3867a9f151f6de255f4525d84f3479 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 29 Feb 2016 19:31:59 -0800 Subject: [PATCH 0539/1408] Hide android.bluetooth.OobData Bug: 27385555 Change-Id: I1767909ca17b1b23a2f23ea4a5b4a02cc52eecde --- framework/java/android/bluetooth/OobData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 2822df7318a..71949381a4e 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -23,6 +23,7 @@ import android.util.Log; /** * Out Of Band Data for Bluetooth device. + * @hide */ public final class OobData implements Parcelable { private byte[] securityManagerTk; -- GitLab From 3bb4bdd4dabbe9f6d7b42e0d5a6c4030bd83bb75 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 1 Mar 2016 17:28:36 -0700 Subject: [PATCH 0540/1408] Sometimes to step forward, we need to go back. [1/2] Change-Id: Ibff95298dc2e6c09675018ad0c45506589b6312a --- framework/java/android/bluetooth/OobData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 71949381a4e..01f72efb22a 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -23,9 +23,8 @@ import android.util.Log; /** * Out Of Band Data for Bluetooth device. - * @hide */ -public final class OobData implements Parcelable { +public class OobData implements Parcelable { private byte[] securityManagerTk; public byte[] getSecurityManagerTk() { -- GitLab From f05d4584a3b29931766976b53ba267289f048001 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 1 Mar 2016 17:36:23 -0700 Subject: [PATCH 0541/1408] Sometimes to step forward, we need to go back. [2/2] Change-Id: Iccabb95530a6e3c3950cb9a8dfe84e75354d2f5f --- framework/java/android/bluetooth/OobData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 01f72efb22a..70d47ee20b2 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -23,6 +23,7 @@ import android.util.Log; /** * Out Of Band Data for Bluetooth device. + * @hide */ public class OobData implements Parcelable { private byte[] securityManagerTk; -- GitLab From dfeaed13d9d1b6b90485307940cda666c1fbb4fc Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Sat, 5 Mar 2016 14:29:13 -0700 Subject: [PATCH 0542/1408] Bluetooth should use Slog. It's in the system process, so whatever it has to say is probably important enough to stick around. Change-Id: Iaeca99d6b9c4881c53ab89216b5128e345a5ff0f --- .../bluetooth/BluetoothManagerService.java | 233 +++++++++--------- 1 file changed, 115 insertions(+), 118 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8cfeb748cce..f65dbb67b22 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -19,7 +19,6 @@ package com.android.server; import android.Manifest; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; @@ -44,7 +43,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -53,10 +51,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; -import android.util.Log; +import android.util.Slog; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; @@ -176,7 +173,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { String action = intent.getAction(); if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); - if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName); + if (DBG) Slog.d(TAG, "Bluetooth Adapter name changed to " + newName); if (newName != null) { storeNameAndAddress(newName, null); } @@ -195,10 +192,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { st = mBluetooth.getState(); } catch (RemoteException e) { - Log.e(TAG,"Unable to call getState", e); + Slog.e(TAG,"Unable to call getState", e); } } - Log.d(TAG, "state" + st); + Slog.d(TAG, "state" + st); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off @@ -214,16 +211,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = false; } } catch(RemoteException e) { - Log.e(TAG,"Unable to call onBrEdrDown", e); + Slog.e(TAG,"Unable to call onBrEdrDown", e); } } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting - Log.d(TAG, "Calling disable"); + Slog.d(TAG, "Calling disable"); sendDisableMsg(); } } else if (mEnableExternal) { // enable without persisting the setting - Log.d(TAG, "Calling enable"); + Slog.d(TAG, "Calling enable"); sendEnableMsg(mQuietEnableExternal); } } @@ -267,7 +264,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. - Log.w(TAG, "Unable to resolve SystemUI's UID.", e); + Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } mSystemUiUid = sysUiUid; } @@ -320,17 +317,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * in the local cache */ private void loadStoredNameAndAddress() { - if (DBG) Log.d(TAG, "Loading stored name and address"); + if (DBG) Slog.d(TAG, "Loading stored name and address"); if (mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_address_validation) && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) { // if the valid flag is not set, don't load the address and name - if (DBG) Log.d(TAG, "invalid bluetooth name and address stored"); + if (DBG) Slog.d(TAG, "invalid bluetooth name and address stored"); return; } mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); - if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); + if (DBG) Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); } /** @@ -343,14 +340,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (name != null) { Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); mName = name; - if (DBG) Log.d(TAG,"Stored Bluetooth name: " + + if (DBG) Slog.d(TAG,"Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); } if (address != null) { Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); mAddress=address; - if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " + + if (DBG) Slog.d(TAG,"Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); } @@ -361,7 +358,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ if (callback == null) { - Log.w(TAG, "Callback is null in registerAdapter"); + Slog.w(TAG, "Callback is null in registerAdapter"); return null; } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); @@ -374,7 +371,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unregisterAdapter(IBluetoothManagerCallback callback) { if (callback == null) { - Log.w(TAG, "Callback is null in unregisterAdapter"); + Slog.w(TAG, "Callback is null in unregisterAdapter"); return; } mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, @@ -403,7 +400,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean isEnabled() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Log.w(TAG,"isEnabled(): not allowed for non-active and non system user"); + Slog.w(TAG,"isEnabled(): not allowed for non-active and non system user"); return false; } @@ -411,7 +408,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { return (mBluetooth != null && mBluetooth.isEnabled()); } catch (RemoteException e) { - Log.e(TAG, "isEnabled()", e); + Slog.e(TAG, "isEnabled()", e); } } return false; @@ -419,17 +416,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { - if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); if (mBleAppCount > 0) --mBleAppCount; if (mBleAppCount == 0) { - if (DBG) Log.d(TAG, "Disabling LE only mode after application crash"); + if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); try { if (mBluetooth != null) { mBluetooth.onBrEdrDown(); } } catch(RemoteException e) { - Log.e(TAG,"Unable to call onBrEdrDown", e); + Slog.e(TAG,"Unable to call onBrEdrDown", e); } } } @@ -459,7 +456,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { if (mBluetooth != null) mBluetooth.onBrEdrDown(); } catch (RemoteException e) { - Log.e(TAG, "error when disabling bluetooth", e); + Slog.e(TAG, "error when disabling bluetooth", e); } } } @@ -474,11 +471,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void disableBleScanMode() { try { if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { - if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); + if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable"); mEnable = false; } } catch (RemoteException e) { - Log.e(TAG, "getState()", e); + Slog.e(TAG, "getState()", e); } } @@ -496,7 +493,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized (this) { ++mBleAppCount; } - if (DBG) Log.d(TAG, "Registered for death Notification"); + if (DBG) Slog.d(TAG, "Registered for death Notification"); } } else { @@ -508,10 +505,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized (this) { if (mBleAppCount > 0) --mBleAppCount; } - if (DBG) Log.d(TAG, "Unregistered for death Notification"); + if (DBG) Slog.d(TAG, "Unregistered for death Notification"); } } - if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount); + if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount); if (mBleAppCount == 0 && mEnable) { disableBleScanMode(); } @@ -528,7 +525,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** @hide*/ public boolean isBleAppPresent() { - if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount); + if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount); return (mBleAppCount > 0); } @@ -536,7 +533,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Action taken when GattService is turned off */ private void onBluetoothGattServiceUp() { - if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); + if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up"); try{ if (isBleAppPresent() == false && mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { @@ -548,7 +545,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Binder.restoreCallingIdentity(callingIdentity); } } catch(RemoteException e) { - Log.e(TAG,"Unable to call onServiceUp", e); + Slog.e(TAG,"Unable to call onServiceUp", e); } } @@ -557,10 +554,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * and turn off all service and stack if no LE app needs it */ private void sendBrEdrDownCallback() { - if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); + if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks"); if(mBluetooth == null) { - Log.w(TAG, "Bluetooth handle is null"); + Slog.w(TAG, "Bluetooth handle is null"); return; } @@ -568,14 +565,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetooth.onBrEdrDown(); } catch(RemoteException e) { - Log.e(TAG, "Call to onBrEdrDown() failed.", e); + Slog.e(TAG, "Call to onBrEdrDown() failed.", e); } } else { // Need to stay at BLE ON. Disconnect all Gatt connections try{ mBluetoothGatt.unregAll(); } catch(RemoteException e) { - Log.e(TAG, "Unable to disconnect all apps.", e); + Slog.e(TAG, "Unable to disconnect all apps.", e); } } } @@ -586,7 +583,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { "Need BLUETOOTH ADMIN permission"); if (DBG) { - Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + + Slog.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding); } int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); @@ -606,14 +603,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enable() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Log.w(TAG,"enable(): not allowed for non-active and non system user"); + Slog.w(TAG,"enable(): not allowed for non-active and non system user"); return false; } mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); if (DBG) { - Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + + Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding); } @@ -623,7 +620,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // waive WRITE_SECURE_SETTINGS permission check sendEnableMsg(false); } - if (DBG) Log.d(TAG, "enable returning"); + if (DBG) Slog.d(TAG, "enable returning"); return true; } @@ -633,12 +630,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Log.w(TAG,"disable(): not allowed for non-active and non system user"); + Slog.w(TAG,"disable(): not allowed for non-active and non system user"); return false; } if (DBG) { - Log.d(TAG,"disable(): mBluetooth = " + mBluetooth + + Slog.d(TAG,"disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding); } @@ -657,7 +654,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindAndFinish() { if (DBG) { - Log.d(TAG,"unbindAndFinish(): " + mBluetooth + + Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding); } @@ -669,10 +666,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetooth.unregisterCallback(mBluetoothCallback); } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothCallback",re); + Slog.e(TAG, "Unable to unregister BluetoothCallback",re); } - if (DBG) Log.d(TAG, "Sending unbind request."); + if (DBG) Slog.d(TAG, "Sending unbind request."); mBluetoothBinder = null; mBluetooth = null; //Unbind @@ -696,7 +693,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IBluetoothProfileServiceConnection proxy) { if (!mEnable) { if (DBG) { - Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile + + Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile + ", while Bluetooth was disabled"); } return false; @@ -705,7 +702,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); if (psc == null) { if (DBG) { - Log.d(TAG, "Creating new ProfileServiceConnections object for" + Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: " + bluetoothProfile); } @@ -746,7 +743,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mContext.unbindService(psc); } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); } psc.removeAllProxies(); } @@ -759,9 +756,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * PHASE_SYSTEM_SERVICES_READY. */ public void handleOnBootPhase() { - if (DBG) Log.d(TAG, "Bluetooth boot completed"); + if (DBG) Slog.d(TAG, "Bluetooth boot completed"); if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { - if (DBG) Log.d(TAG, "Auto-enabling Bluetooth."); + if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); } } @@ -770,7 +767,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Called when switching to a different foreground user. */ public void handleOnSwitchUser(int userHandle) { - if (DBG) Log.d(TAG, "Bluetooth user switched"); + if (DBG) Slog.d(TAG, "Bluetooth user switched"); mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0)); } @@ -801,7 +798,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); return true; } - Log.w(TAG, "Unable to bind with intent: " + mIntent); + Slog.w(TAG, "Unable to bind with intent: " + mIntent); return false; } @@ -811,7 +808,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try{ proxy.onServiceConnected(mClassName, mService); } catch (RemoteException e) { - Log.e(TAG, "Unable to connect to proxy", e); + Slog.e(TAG, "Unable to connect to proxy", e); } } else { if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) { @@ -828,11 +825,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { proxy.onServiceDisconnected(mClassName); } catch (RemoteException e) { - Log.e(TAG, "Unable to disconnect proxy", e); + Slog.e(TAG, "Unable to disconnect proxy", e); } } } else { - Log.w(TAG, "Trying to remove a null proxy"); + Slog.w(TAG, "Trying to remove a null proxy"); } } @@ -850,11 +847,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mService.linkToDeath(this, 0); } catch (RemoteException e) { - Log.e(TAG, "Unable to linkToDeath", e); + Slog.e(TAG, "Unable to linkToDeath", e); } if (mInvokingProxyCallbacks) { - Log.e(TAG, "Proxy callbacks already in progress."); + Slog.e(TAG, "Proxy callbacks already in progress."); return; } mInvokingProxyCallbacks = true; @@ -865,7 +862,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mProxies.getBroadcastItem(i).onServiceConnected(className, service); } catch (RemoteException e) { - Log.e(TAG, "Unable to connect to proxy", e); + Slog.e(TAG, "Unable to connect to proxy", e); } } } finally { @@ -882,7 +879,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mClassName = null; if (mInvokingProxyCallbacks) { - Log.e(TAG, "Proxy callbacks already in progress."); + Slog.e(TAG, "Proxy callbacks already in progress."); return; } mInvokingProxyCallbacks = true; @@ -893,7 +890,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mProxies.getBroadcastItem(i).onServiceDisconnected(className); } catch (RemoteException e) { - Log.e(TAG, "Unable to disconnect from proxy", e); + Slog.e(TAG, "Unable to disconnect from proxy", e); } } } finally { @@ -905,7 +902,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void binderDied() { if (DBG) { - Log.w(TAG, "Profile service for profile: " + mClassName + Slog.w(TAG, "Profile service for profile: " + mClassName + " died."); } onServiceDisconnected(mClassName); @@ -919,12 +916,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void sendBluetoothStateCallback(boolean isUp) { try { int n = mStateChangeCallbacks.beginBroadcast(); - if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); + if (DBG) Slog.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); for (int i=0; i " + newState); + if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState); // Send broadcast message to everyone else Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); @@ -1498,9 +1495,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newState == BluetoothAdapter.STATE_OFF) { // If Bluetooth is off, send service down event to proxy objects, and unbind - if (DBG) Log.d(TAG, "Bluetooth is complete turn off"); + if (DBG) Slog.d(TAG, "Bluetooth is complete turn off"); if (canUnbindBluetoothService()) { - if (DBG) Log.d(TAG, "Good to unbind!"); + if (DBG) Slog.d(TAG, "Good to unbind!"); sendBluetoothServiceDownCallback(); unbindAndFinish(); sendBleStateChanged(prevState, newState); @@ -1510,12 +1507,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (!intermediate_off) { // connect to GattService - if (DBG) Log.d(TAG, "Bluetooth is in LE only mode"); + if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); if (mBluetoothGatt != null) { - if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp"); + if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); onBluetoothGattServiceUp(); } else { - if (DBG) Log.d(TAG, "Binding Bluetooth GATT service"); + if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); if (mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_BLUETOOTH_LE)) { Intent i = new Intent(IBluetoothGatt.class.getName()); @@ -1527,7 +1524,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { isStandardBroadcast = false; } else if (intermediate_off){ - if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode"); + if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); // For LE only mode, broadcast as is sendBleStateChanged(prevState, newState); sendBluetoothStateCallback(false); // BT is OFF for general users @@ -1583,7 +1580,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; } } catch (RemoteException e) { - Log.e(TAG, "getState()", e); + Slog.e(TAG, "getState()", e); break; } } @@ -1594,7 +1591,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } i++; } - Log.e(TAG,"waitForOnOff time out"); + Slog.e(TAG,"waitForOnOff time out"); return false; } @@ -1619,21 +1616,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); } catch (RemoteException e) { - Log.e(TAG, "getState()", e); + Slog.e(TAG, "getState()", e); } } return false; } private void recoverBluetoothServiceFromError() { - Log.e(TAG,"recoverBluetoothServiceFromError"); + Slog.e(TAG,"recoverBluetoothServiceFromError"); synchronized (mConnection) { if (mBluetooth != null) { //Unregister callback object try { mBluetooth.unregisterCallback(mBluetoothCallback); } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister",re); + Slog.e(TAG, "Unable to unregister",re); } } } -- GitLab From b523d1ab94ce34a89f3e20e21932df4ba04846fb Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Sat, 5 Mar 2016 14:42:58 -0700 Subject: [PATCH 0543/1408] Kick Bluetooth stack after user is unlocked. If Bluetooth was enabled at boot time, but the actual Bluetooth package wasn't encryption aware, we need to kick off another enabled pass once the user is unlocked. Bug: 27326711 Change-Id: I511c4fd8567792e5bc4d49fa95929fd87edd0fbf --- .../bluetooth/BluetoothManagerService.java | 45 ++++++++++++++----- .../server/bluetooth/BluetoothService.java | 13 +++--- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f65dbb67b22..e241a4aa992 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -89,14 +89,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42; - private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60; - private static final int MESSAGE_TIMEOUT_BIND =100; - private static final int MESSAGE_TIMEOUT_UNBIND =101; + private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60; + private static final int MESSAGE_TIMEOUT_BIND = 100; + private static final int MESSAGE_TIMEOUT_UNBIND = 101; private static final int MESSAGE_USER_SWITCHED = 300; + private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; - private static final int MAX_SAVE_RETRIES=3; - private static final int MAX_ERROR_RESTART_RETRIES=6; + private static final int MAX_SAVE_RETRIES = 3; + private static final int MAX_ERROR_RESTART_RETRIES = 6; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF=0; @@ -767,8 +768,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Called when switching to a different foreground user. */ public void handleOnSwitchUser(int userHandle) { - if (DBG) Slog.d(TAG, "Bluetooth user switched"); - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0)); + if (DBG) Slog.d(TAG, "User " + userHandle + " switched"); + mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget(); + } + + /** + * Called when user is unlocked. + */ + public void handleOnUnlockUser(int userHandle) { + if (DBG) Slog.d(TAG, "User " + userHandle + " unlocked"); + mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget(); } /** @@ -1308,12 +1317,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } - case MESSAGE_USER_SWITCHED: - { - if (DBG) { - Slog.d(TAG, "MESSAGE_USER_SWITCHED"); - } + case MESSAGE_USER_SWITCHED: { + if (DBG) Slog.d(TAG, "MESSAGE_USER_SWITCHED"); mHandler.removeMessages(MESSAGE_USER_SWITCHED); + /* disable and enable BT when detect a user switch */ if (mEnable && mBluetooth != null) { synchronized (mConnection) { @@ -1381,6 +1388,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; } + case MESSAGE_USER_UNLOCKED: { + if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED"); + mHandler.removeMessages(MESSAGE_USER_SWITCHED); + + synchronized (mConnection) { + if (mEnable && !mBinding && (mBluetooth == null)) { + // We should be connected, but we gave up for some + // reason; maybe the Bluetooth service wasn't encryption + // aware, so try binding again. + if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock"); + handleEnable(mQuietEnable); + } + } + } } } } diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 019d03df194..1bf4e3a563c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -18,10 +18,8 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; -import android.util.Log; class BluetoothService extends SystemService { - private static final String TAG = "BluetoothService"; private BluetoothManagerService mBluetoothManagerService; public BluetoothService(Context context) { @@ -36,17 +34,20 @@ class BluetoothService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); - publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); + publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, + mBluetoothManagerService); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { - Log.d(TAG, "onBootPhase: PHASE_ACTIVITY_MANAGER_READY"); mBluetoothManagerService.handleOnBootPhase(); } } @Override public void onSwitchUser(int userHandle) { - Log.d(TAG, "onSwitchUser: switching to user " + userHandle); mBluetoothManagerService.handleOnSwitchUser(userHandle); } + + @Override + public void onUnlockUser(int userHandle) { + mBluetoothManagerService.handleOnUnlockUser(userHandle); + } } -- GitLab From c668f0ef9e9e9552076798acc5d014a4cfff925e Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 11 Mar 2016 13:47:20 -0800 Subject: [PATCH 0544/1408] Get adapter name and address on boot Bug: 25836009 Change-Id: I6fe68b116fcd736cd5736be68c97af3052351b33 --- .../server/bluetooth/BluetoothManagerService.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index e241a4aa992..799a763845d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -761,6 +761,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); + } else if (!isNameAndAddressSet()) { + if (DBG) Slog.d(TAG, "Getting adapter name and address"); + enable(); + waitForOnOff(true, false); + disable(true); } } @@ -1001,6 +1006,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } + // mAddress is accessed from outside. // It is alright without a lock. Here, bluetooth is off, no other thread is // changing mAddress @@ -1181,6 +1187,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG,"Unable to call configHciSnoopLog", e); } + if (!isNameAndAddressSet()) { + try { + storeNameAndAddress(mBluetooth.getName(), + mBluetooth.getAddress()); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to grab names", re); + } + } + //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From 6f56b0f6641d666b98471517e21b3c567a7dfc5a Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Fri, 4 Mar 2016 13:02:54 -0800 Subject: [PATCH 0545/1408] Add BluetoothProfile for PBAP PCE role. Create a new Bluetooth profile for Pbap Client. Bug: 27490041 Change-Id: I77d2c7eeeb8e955ea61386d784b02b14f415b318 --- .../android/bluetooth/BluetoothAdapter.java | 9 +- .../bluetooth/BluetoothPbapClient.java | 331 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 6 + .../bluetooth/IBluetoothPbapClient.aidl | 32 ++ 4 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothPbapClient.java create mode 100644 framework/java/android/bluetooth/IBluetoothPbapClient.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index eb4cb919ce1..d762a172727 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015 The Android Open Source Project + * Copyright (C) 2009-2016 The Android Open Source Project * Copyright (C) 2015 Samsung LSI * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -1837,6 +1837,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.SAP) { BluetoothSap sap = new BluetoothSap(context, listener); return true; + } else if (profile == BluetoothProfile.PBAP_CLIENT) { + BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); + return true; } else { return false; } @@ -1905,6 +1908,10 @@ public final class BluetoothAdapter { BluetoothSap sap = (BluetoothSap)proxy; sap.close(); break; + case BluetoothProfile.PBAP_CLIENT: + BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy; + pbapClient.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java new file mode 100644 index 00000000000..736e55d17d8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -0,0 +1,331 @@ +/* + * 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 java.util.List; +import java.util.ArrayList; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +/** + * This class provides the APIs to control the Bluetooth PBAP Client Profile. + *@hide + */ +public final class BluetoothPbapClient implements BluetoothProfile { + + private static final String TAG = "BluetoothPbapClient"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; + + private IBluetoothPbapClient mService; + private BluetoothDevice mDevice; + 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: PBAP CLIENT 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 BluetoothPbapClient proxy object. + */ + BluetoothPbapClient(Context context, ServiceListener l) { + if (DBG) { + Log.d(TAG, "Create BluetoothPbapClient 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(); + } + + private boolean doBind() { + Intent intent = new Intent(IBluetoothPbapClient.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 PBAP Client 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 BluetoothPbapClient will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public synchronized 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; + } + + /** + * Initiate connection. + * Upon successful connection to remote PBAP server the Client will + * attempt to automatically download the users phonebook and call log. + * + * @param device a remote device we want connect to + * @return true if command has been issued successfully; + * false otherwise; + */ + public boolean connect(BluetoothDevice device) { + if (DBG) { + log("connect(" + device + ") for PBAP Client."); + } + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + mDevice = device; + return mService.connect(device); + } 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; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, + * true otherwise + */ + public boolean disconnect() { + if (DBG) { + log("disconnect(" + mDevice + ")"); + } + if (mService != null && isEnabled() && isValidDevice(mDevice)) { + try { + mService.disconnect(mDevice); + return true; + } 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 list of connected devices. + * Currently at most one. + * + * @return list of connected devices + */ + @Override + public List getConnectedDevices() { + if (DBG) { + log("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("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("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; + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) { + log("Proxy object connected"); + } + mService = IBluetoothPbapClient.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, BluetoothPbapClient.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) { + log("Proxy object disconnected"); + } + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); + } + } + }; + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { + return true; + } + log("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; + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index cbce22cdea6..eee66d193fe 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -130,6 +130,12 @@ public interface BluetoothProfile { */ public static final int HEADSET_CLIENT = 16; + /** + * PBAP Client + * @hide + */ + public static final int PBAP_CLIENT = 17; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl new file mode 100644 index 00000000000..b26ea295714 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl @@ -0,0 +1,32 @@ +/* + * 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.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth Phone Book Access Provile Client Side + * + * {@hide} + */ +interface IBluetoothPbapClient { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); +} -- GitLab From c9ba8c7ed1ab4271ef4cd84cac0032d13eb46513 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 1 Mar 2016 18:50:27 -0800 Subject: [PATCH 0546/1408] Change how services are passed up to the stack Right now we pass all services, characteristics and descriptors one by one. This patch changes that - now we pass whole GATT database at once. Bug: 27455533 Change-Id: Ie42cd80072538e411904b9c9b011a978f26158b9 --- .../java/android/bluetooth/BluetoothGatt.java | 120 ++++-------------- .../BluetoothGattCallbackWrapper.java | 27 +--- .../BluetoothGattCharacteristic.aidl | 19 +++ .../BluetoothGattCharacteristic.java | 58 ++++++++- .../bluetooth/BluetoothGattDescriptor.aidl | 19 +++ .../bluetooth/BluetoothGattDescriptor.java | 42 +++++- .../BluetoothGattIncludedService.aidl | 19 +++ .../BluetoothGattIncludedService.java | 110 ++++++++++++++++ .../bluetooth/BluetoothGattService.aidl | 19 +++ .../bluetooth/BluetoothGattService.java | 90 ++++++++++++- .../bluetooth/IBluetoothGattCallback.aidl | 16 +-- 11 files changed, 404 insertions(+), 135 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.java create mode 100644 framework/java/android/bluetooth/BluetoothGattService.aidl diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ea2dca08fd4..ef056654d7e 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -196,98 +196,6 @@ public final class BluetoothGatt implements BluetoothProfile { } } - /** - * A new GATT service has been discovered. - * The service is added to the internal list and the search - * continues. - * @hide - */ - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); - if (!address.equals(mDevice.getAddress())) { - return; - } - mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType)); - } - - /** - * An included service has been found durig GATT discovery. - * The included service is added to the respective parent. - * @hide - */ - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address - + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, - srvcUuid.getUuid(), srvcInstId, srvcType); - BluetoothGattService includedService = getService(mDevice, - inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); - - if (service != null && includedService != null) { - service.addIncludedService(includedService); - } - } - - /** - * A new GATT characteristic has been discovered. - * Add the new characteristic to the relevant service and continue - * the remote device inspection. - * @hide - */ - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + - charUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service != null) { - service.addCharacteristic(new BluetoothGattCharacteristic( - service, charUuid.getUuid(), charInstId, charProps, 0)); - } - } - - /** - * A new GATT descriptor has been discovered. - * Finally, add the descriptor to the related characteristic. - * This should conclude the remote device update. - * @hide - */ - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descUuid) { - if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - characteristic.addDescriptor(new BluetoothGattDescriptor( - characteristic, descUuid.getUuid(), descrInstId, 0)); - } - /** * Remote search has been completed. * The internal object structure should now reflect the state @@ -295,11 +203,37 @@ public final class BluetoothGatt implements BluetoothProfile { * we are done at this point. * @hide */ - public void onSearchComplete(String address, int status) { + public void onSearchComplete(String address, List services, + int status) { if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; } + + for (BluetoothGattService s : services) { + //services we receive don't have device set properly. + s.setDevice(mDevice); + } + + mServices.addAll(services); + + // Fix references to included services, as they doesn't point to right objects. + for (BluetoothGattService fixedService : mServices) { + ArrayList includedServices = + new ArrayList(fixedService.getIncludedServices()); + fixedService.getIncludedServices().clear(); + + for(BluetoothGattService brokenRef : includedServices) { + BluetoothGattService includedService = getService(mDevice, + brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType()); + if (includedService != null) { + fixedService.addIncludedService(includedService); + } else { + Log.e(TAG, "Broken GATT database: can't find included service."); + } + } + } + try { mCallback.onServicesDiscovered(BluetoothGatt.this, status); } catch (Exception ex) { diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 01778b3dc40..64ead54e3d5 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.ScanResult; +import android.bluetooth.BluetoothGattService; import android.os.ParcelUuid; import android.os.RemoteException; @@ -48,30 +49,8 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) - throws RemoteException { - } - - @Override - public void onGetIncludedService(String address, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int inclSrvcType, int inclSrvcInstId, ParcelUuid inclSrvcUuid) - throws RemoteException { - } - - @Override - public void onGetCharacteristic(String address, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int charProps) - throws RemoteException { - } - - @Override - public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid) - throws RemoteException { - } - - @Override - public void onSearchComplete(String address, int status) throws RemoteException { + public void onSearchComplete(String address, List services, + int status) throws RemoteException { } @Override diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl new file mode 100644 index 00000000000..bbb8623e217 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattCharacteristic; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 7cdcc2c9b9f..7d698b3ef5a 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,6 +15,9 @@ */ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -26,7 +29,7 @@ import java.util.UUID; * {@link BluetoothGattService}. The characteristic contains a value as well as * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}. */ -public class BluetoothGattCharacteristic { +public class BluetoothGattCharacteristic implements Parcelable { /** * Characteristic proprty: Characteristic is broadcastable. @@ -242,6 +245,15 @@ public class BluetoothGattCharacteristic { initCharacteristic(service, uuid, instanceId, properties, permissions); } + /** + * Create a new BluetoothGattCharacteristic + * @hide + */ + public BluetoothGattCharacteristic(UUID uuid, int instanceId, + int properties, int permissions) { + initCharacteristic(null, uuid, instanceId, properties, permissions); + } + private void initCharacteristic(BluetoothGattService service, UUID uuid, int instanceId, int properties, int permissions) { @@ -260,6 +272,50 @@ public class BluetoothGattCharacteristic { } } + /** + * @hide + */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstance); + out.writeInt(mProperties); + out.writeInt(mPermissions); + out.writeTypedList(mDescriptors); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattCharacteristic createFromParcel(Parcel in) { + return new BluetoothGattCharacteristic(in); + } + + public BluetoothGattCharacteristic[] newArray(int size) { + return new BluetoothGattCharacteristic[size]; + } + }; + + private BluetoothGattCharacteristic(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstance = in.readInt(); + mProperties = in.readInt(); + mPermissions = in.readInt(); + + mDescriptors = new ArrayList(); + + ArrayList descs = + in.createTypedArrayList(BluetoothGattDescriptor.CREATOR); + if (descs != null) { + for (BluetoothGattDescriptor desc: descs) { + desc.setCharacteristic(this); + mDescriptors.add(desc); + } + } + } + /** * Returns the deisred key size. * @hide diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl new file mode 100644 index 00000000000..43932733167 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattDescriptor; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 5f525dc609a..28317c49604 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.UUID; /** @@ -25,7 +28,7 @@ import java.util.UUID; * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe * the characteristic's features or to control certain behaviours of the characteristic. */ -public class BluetoothGattDescriptor { +public class BluetoothGattDescriptor implements Parcelable { /** * Value used to enable notification for a client configuration descriptor @@ -138,6 +141,13 @@ public class BluetoothGattDescriptor { initDescriptor(characteristic, uuid, instance, permissions); } + /** + * @hide + */ + public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) { + initDescriptor(null, uuid, instance, permissions); + } + private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, int instance, int permissions) { mCharacteristic = characteristic; @@ -146,6 +156,36 @@ public class BluetoothGattDescriptor { mPermissions = permissions; } + /** + * @hide + */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstance); + out.writeInt(mPermissions); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattDescriptor createFromParcel(Parcel in) { + return new BluetoothGattDescriptor(in); + } + + public BluetoothGattDescriptor[] newArray(int size) { + return new BluetoothGattDescriptor[size]; + } + }; + + private BluetoothGattDescriptor(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstance = in.readInt(); + mPermissions = in.readInt(); + } + /** * Returns the characteristic this descriptor belongs to. * @return The characteristic. diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl new file mode 100644 index 00000000000..1ef427edc3a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattIncludedService; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.java b/framework/java/android/bluetooth/BluetoothGattIncludedService.java new file mode 100644 index 00000000000..155dc571d23 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.java @@ -0,0 +1,110 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth GATT Included Service + * @hide + */ +public class BluetoothGattIncludedService implements Parcelable { + + /** + * The UUID of this service. + */ + protected UUID mUuid; + + /** + * Instance ID for this service. + */ + protected int mInstanceId; + + /** + * Service type (Primary/Secondary). + */ + protected int mServiceType; + + /** + * Create a new BluetoothGattIncludedService + */ + public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) { + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstanceId); + out.writeInt(mServiceType); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattIncludedService createFromParcel(Parcel in) { + return new BluetoothGattIncludedService(in); + } + + public BluetoothGattIncludedService[] newArray(int size) { + return new BluetoothGattIncludedService[size]; + } + }; + + private BluetoothGattIncludedService(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstanceId = in.readInt(); + mServiceType = in.readInt(); + } + + /** + * Returns the UUID of this service + * + * @return UUID of this service + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this service + * + *

        If a remote device offers multiple services with the same UUID + * (ex. multiple battery services for different batteries), the instance + * ID is used to distuinguish services. + * + * @return Instance ID of this service + */ + public int getInstanceId() { + return mInstanceId; + } + + /** + * Get the type of this service (primary/secondary) + */ + public int getType() { + return mServiceType; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattService.aidl b/framework/java/android/bluetooth/BluetoothGattService.aidl new file mode 100644 index 00000000000..84314d2072d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattService.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattService; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 52bc0f796cb..a4e1dc01d00 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,6 +15,9 @@ */ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -25,7 +28,7 @@ import java.util.UUID; *

        Gatt Service contains a collection of {@link BluetoothGattCharacteristic}, * as well as referenced services. */ -public class BluetoothGattService { +public class BluetoothGattService implements Parcelable { /** * Primary service @@ -116,6 +119,81 @@ public class BluetoothGattService { mIncludedServices = new ArrayList(); } + /** + * Create a new BluetoothGattService + * @hide + */ + public BluetoothGattService(UUID uuid, int instanceId, int serviceType) { + mDevice = null; + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + mCharacteristics = new ArrayList(); + mIncludedServices = new ArrayList(); + } + + /** + * @hide + */ + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstanceId); + out.writeInt(mServiceType); + out.writeTypedList(mCharacteristics); + + ArrayList includedServices = + new ArrayList(mIncludedServices.size()); + for(BluetoothGattService s : mIncludedServices) { + includedServices.add(new BluetoothGattIncludedService(s.getUuid(), + s.getInstanceId(), s.getType())); + } + out.writeTypedList(includedServices); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattService createFromParcel(Parcel in) { + return new BluetoothGattService(in); + } + + public BluetoothGattService[] newArray(int size) { + return new BluetoothGattService[size]; + } + }; + + private BluetoothGattService(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstanceId = in.readInt(); + mServiceType = in.readInt(); + + mCharacteristics = new ArrayList(); + + ArrayList chrcs = + in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR); + if (chrcs != null) { + for (BluetoothGattCharacteristic chrc : chrcs) { + chrc.setService(this); + mCharacteristics.add(chrc); + } + } + + mIncludedServices = new ArrayList(); + + ArrayList inclSvcs = + in.createTypedArrayList(BluetoothGattIncludedService.CREATOR); + if (chrcs != null) { + for (BluetoothGattIncludedService isvc : inclSvcs) { + mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(), + isvc.getInstanceId(), isvc.getType())); + } + } + } + /** * Returns the device associated with this service. * @hide @@ -124,6 +202,14 @@ public class BluetoothGattService { return mDevice; } + /** + * Returns the device associated with this service. + * @hide + */ + /*package*/ void setDevice(BluetoothDevice device) { + this.mDevice = device; + } + /** * Add an included service to this service. *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -192,7 +278,7 @@ public class BluetoothGattService { * Add an included service to the internal map. * @hide */ - /*package*/ void addIncludedService(BluetoothGattService includedService) { + public void addIncludedService(BluetoothGattService includedService) { mIncludedServices.add(includedService); } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index cbba9f02142..f1772445612 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -16,6 +16,7 @@ package android.bluetooth; import android.os.ParcelUuid; +import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.ScanResult; @@ -29,20 +30,7 @@ oneway interface IBluetoothGattCallback { in boolean connected, in String address); void onScanResult(in ScanResult scanResult); void onBatchScanResults(in List batchResults); - void onGetService(in String address, in int srvcType, in int srvcInstId, - in ParcelUuid srvcUuid); - void onGetIncludedService(in String address, in int srvcType, in int srvcInstId, - in ParcelUuid srvcUuid, in int inclSrvcType, - in int inclSrvcInstId, in ParcelUuid inclSrvcUuid); - void onGetCharacteristic(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int charProps); - void onGetDescriptor(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid); - void onSearchComplete(in String address, in int status); + void onSearchComplete(in String address, in List services, in int status); void onCharacteristicRead(in String address, in int status, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, -- GitLab From 50d03e88903737f13945fcb678167bf4fa0660a2 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 18 Mar 2016 15:38:20 -0700 Subject: [PATCH 0547/1408] Make BluetoothManagerCallback oneway to prevent waiting on response Bug: 27384453 Change-Id: I2cd0f1bc03c7a1e887c89faa18d5baa6ca5be24a --- framework/java/android/bluetooth/IBluetoothManagerCallback.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl index 1385dafca2f..8104d219948 100644 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -23,7 +23,7 @@ import android.bluetooth.IBluetooth; * * {@hide} */ -interface IBluetoothManagerCallback { +oneway interface IBluetoothManagerCallback { void onBluetoothServiceUp(in IBluetooth bluetoothService); void onBluetoothServiceDown(); void onBrEdrDown(); -- GitLab From 47534d8ac6406c0f753aed960c566af3704da0bc Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 18 Mar 2016 15:38:20 -0700 Subject: [PATCH 0548/1408] Make BluetoothManagerCallback oneway to prevent waiting on response Bug: 27384453 Change-Id: I2cd0f1bc03c7a1e887c89faa18d5baa6ca5be24a --- framework/java/android/bluetooth/IBluetoothManagerCallback.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl index 1385dafca2f..8104d219948 100644 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl @@ -23,7 +23,7 @@ import android.bluetooth.IBluetooth; * * {@hide} */ -interface IBluetoothManagerCallback { +oneway interface IBluetoothManagerCallback { void onBluetoothServiceUp(in IBluetooth bluetoothService); void onBluetoothServiceDown(); void onBrEdrDown(); -- GitLab From c6f07ad7177186ec0cce1a8f9e7f2cc84e8ec2e3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Mar 2016 16:00:16 -0700 Subject: [PATCH 0549/1408] Use handles to identify GATT attributes (4/4) Bug: 27778668 Change-Id: I01e095939df2f8dc433f14b473957c815ecade74 --- .../java/android/bluetooth/BluetoothGatt.java | 162 +++++++----------- .../BluetoothGattCallbackWrapper.java | 17 +- .../android/bluetooth/IBluetoothGatt.aidl | 25 +-- .../bluetooth/IBluetoothGattCallback.aidl | 25 +-- 4 files changed, 81 insertions(+), 148 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ef056654d7e..68442ea4070 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -246,11 +246,12 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { + public void onCharacteristicRead(String address, int status, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address - + " UUID=" + charUuid + " Status=" + status); + + " handle=" + handle + " Status=" + status); + + Log.w(TAG, "onCharacteristicRead() - Device=" + address + + " handle=" + handle + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -265,9 +266,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.readCharacteristic(mClientIf, address, - srvcType, srvcInstId, srvcUuid, - charInstId, charUuid, AUTHENTICATION_MITM); + mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM); return; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -276,13 +275,11 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); + if (characteristic == null) { + Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); + return; + } if (status == 0) characteristic.setValue(value); @@ -298,11 +295,9 @@ public final class BluetoothGatt implements BluetoothProfile { * Let the app know how we did... * @hide */ - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { + public void onCharacteristicWrite(String address, int status, int handle) { if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address - + " UUID=" + charUuid + " Status=" + status); + + " handle=" + handle + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -312,12 +307,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) return; if ((status == GATT_INSUFFICIENT_AUTHENTICATION @@ -325,8 +315,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.writeCharacteristic(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), AUTHENTICATION_MITM, characteristic.getValue()); return; @@ -349,21 +338,14 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); + public void onNotify(String address, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) return; characteristic.setValue(value); @@ -379,12 +361,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor has been read. * @hide */ - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descrUuid, - byte[] value) { - if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); + public void onDescriptorRead(String address, int status, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; @@ -394,16 +372,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid(), descrInstId); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; if (status == 0) descriptor.setValue(value); @@ -413,9 +382,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.readDescriptor(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrInstId, descrUuid, AUTHENTICATION_MITM); + mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM); return; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -435,11 +402,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor write operation complete. * @hide */ - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descrUuid) { - if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); + public void onDescriptorWrite(String address, int status, int handle) { + if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; @@ -449,16 +413,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid(), descrInstId); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; if ((status == GATT_INSUFFICIENT_AUTHENTICATION @@ -466,9 +421,8 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.writeDescriptor(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrInstId, descrUuid, characteristic.getWriteType(), + mService.writeDescriptor(mClientIf, address, handle, + descriptor.getCharacteristic().getWriteType(), AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -584,6 +538,37 @@ public final class BluetoothGatt implements BluetoothProfile { } + /** + * Returns a characteristic with id equal to instanceId. + * @hide + */ + /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId); + if (charac.getInstanceId() == instanceId) + return charac; + } + } + return null; + } + + /** + * Returns a descriptor with id equal to instanceId. + * @hide + */ + /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + for(BluetoothGattDescriptor desc : charac.getDescriptors()) { + if (desc.getInstanceId() == instanceId) + return desc; + } + } + } + return null; + } + /** * Register an application callback to start using GATT. * @@ -832,9 +817,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readCharacteristic(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); + characteristic.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -877,11 +860,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeCharacteristic(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - characteristic.getWriteType(), AUTHENTICATION_NONE, - characteristic.getValue()); + characteristic.getInstanceId(), characteristic.getWriteType(), + AUTHENTICATION_NONE, characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -922,11 +902,8 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), - service.getInstanceId(), new ParcelUuid(service.getUuid()), - characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), - descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), - AUTHENTICATION_NONE); + mService.readDescriptor(mClientIf, device.getAddress(), + descriptor.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -966,12 +943,8 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), - service.getInstanceId(), new ParcelUuid(service.getUuid()), - characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), - descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), - characteristic.getWriteType(), AUTHENTICATION_NONE, - descriptor.getValue()); + mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), + characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -1102,10 +1075,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - enable); + characteristic.getInstanceId(), enable); } catch (RemoteException e) { Log.e(TAG,"",e); return false; diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 64ead54e3d5..17e533a5b32 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -54,14 +54,12 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onCharacteristicRead(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) + public void onCharacteristicRead(String address, int status, int handle, byte[] value) throws RemoteException { } @Override - public void onCharacteristicWrite(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid) throws RemoteException { + public void onCharacteristicWrite(String address, int status, int handle) throws RemoteException { } @Override @@ -69,20 +67,15 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, - ParcelUuid descrUuid, byte[] value) throws RemoteException { + public void onDescriptorRead(String address, int status, int handle, byte[] value) throws RemoteException { } @Override - public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, - ParcelUuid descrUuid) throws RemoteException { + public void onDescriptorWrite(String address, int status, int handle) throws RemoteException { } @Override - public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) throws RemoteException { + public void onNotify(String address, int handle, byte[] value) throws RemoteException { } @Override diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 3660be7c8eb..45b512298e6 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -50,28 +50,13 @@ interface IBluetoothGatt { void clientDisconnect(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); - void readCharacteristic(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int authReq); - void writeCharacteristic(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, + void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); + void writeCharacteristic(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); - void readDescriptor(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int descrInstanceId, in ParcelUuid descrUuid, - in int authReq); - void writeDescriptor(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int descrInstanceId, in ParcelUuid descrId, + void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); + void writeDescriptor(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); - void registerForNotification(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in boolean enable); + void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index f1772445612..7163c370694 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -31,27 +31,12 @@ oneway interface IBluetoothGattCallback { void onScanResult(in ScanResult scanResult); void onBatchScanResults(in List batchResults); void onSearchComplete(in String address, in List services, in int status); - void onCharacteristicRead(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in byte[] value); - void onCharacteristicWrite(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid); + void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); + void onCharacteristicWrite(in String address, in int status, in int handle); void onExecuteWrite(in String address, in int status); - void onDescriptorRead(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid, - in byte[] value); - void onDescriptorWrite(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid); - void onNotify(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in byte[] value); + void onDescriptorRead(in String address, in int status, in int handle, in byte[] value); + void onDescriptorWrite(in String address, in int status, in int handle); + void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); void onMultiAdvertiseCallback(in int status, boolean isStart, in AdvertiseSettings advertiseSettings); -- GitLab From 31bee8730fa088f2103a0755644d6cfc7dcf300b Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 1 Mar 2016 18:50:27 -0800 Subject: [PATCH 0550/1408] Change how services are passed up to the stack Right now we pass all services, characteristics and descriptors one by one. This patch changes that - now we pass whole GATT database at once. Bug: 27455533 Change-Id: Ie42cd80072538e411904b9c9b011a978f26158b9 --- .../java/android/bluetooth/BluetoothGatt.java | 120 ++++-------------- .../BluetoothGattCallbackWrapper.java | 27 +--- .../BluetoothGattCharacteristic.aidl | 19 +++ .../BluetoothGattCharacteristic.java | 58 ++++++++- .../bluetooth/BluetoothGattDescriptor.aidl | 19 +++ .../bluetooth/BluetoothGattDescriptor.java | 42 +++++- .../BluetoothGattIncludedService.aidl | 19 +++ .../BluetoothGattIncludedService.java | 110 ++++++++++++++++ .../bluetooth/BluetoothGattService.aidl | 19 +++ .../bluetooth/BluetoothGattService.java | 90 ++++++++++++- .../bluetooth/IBluetoothGattCallback.aidl | 16 +-- 11 files changed, 404 insertions(+), 135 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.aidl create mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.java create mode 100644 framework/java/android/bluetooth/BluetoothGattService.aidl diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ea2dca08fd4..ef056654d7e 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -196,98 +196,6 @@ public final class BluetoothGatt implements BluetoothProfile { } } - /** - * A new GATT service has been discovered. - * The service is added to the internal list and the search - * continues. - * @hide - */ - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); - if (!address.equals(mDevice.getAddress())) { - return; - } - mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType)); - } - - /** - * An included service has been found durig GATT discovery. - * The included service is added to the respective parent. - * @hide - */ - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address - + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, - srvcUuid.getUuid(), srvcInstId, srvcType); - BluetoothGattService includedService = getService(mDevice, - inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); - - if (service != null && includedService != null) { - service.addIncludedService(includedService); - } - } - - /** - * A new GATT characteristic has been discovered. - * Add the new characteristic to the relevant service and continue - * the remote device inspection. - * @hide - */ - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + - charUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service != null) { - service.addCharacteristic(new BluetoothGattCharacteristic( - service, charUuid.getUuid(), charInstId, charProps, 0)); - } - } - - /** - * A new GATT descriptor has been discovered. - * Finally, add the descriptor to the related characteristic. - * This should conclude the remote device update. - * @hide - */ - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descUuid) { - if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); - - if (!address.equals(mDevice.getAddress())) { - return; - } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - characteristic.addDescriptor(new BluetoothGattDescriptor( - characteristic, descUuid.getUuid(), descrInstId, 0)); - } - /** * Remote search has been completed. * The internal object structure should now reflect the state @@ -295,11 +203,37 @@ public final class BluetoothGatt implements BluetoothProfile { * we are done at this point. * @hide */ - public void onSearchComplete(String address, int status) { + public void onSearchComplete(String address, List services, + int status) { if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; } + + for (BluetoothGattService s : services) { + //services we receive don't have device set properly. + s.setDevice(mDevice); + } + + mServices.addAll(services); + + // Fix references to included services, as they doesn't point to right objects. + for (BluetoothGattService fixedService : mServices) { + ArrayList includedServices = + new ArrayList(fixedService.getIncludedServices()); + fixedService.getIncludedServices().clear(); + + for(BluetoothGattService brokenRef : includedServices) { + BluetoothGattService includedService = getService(mDevice, + brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType()); + if (includedService != null) { + fixedService.addIncludedService(includedService); + } else { + Log.e(TAG, "Broken GATT database: can't find included service."); + } + } + } + try { mCallback.onServicesDiscovered(BluetoothGatt.this, status); } catch (Exception ex) { diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 01778b3dc40..64ead54e3d5 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.ScanResult; +import android.bluetooth.BluetoothGattService; import android.os.ParcelUuid; import android.os.RemoteException; @@ -48,30 +49,8 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) - throws RemoteException { - } - - @Override - public void onGetIncludedService(String address, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int inclSrvcType, int inclSrvcInstId, ParcelUuid inclSrvcUuid) - throws RemoteException { - } - - @Override - public void onGetCharacteristic(String address, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int charProps) - throws RemoteException { - } - - @Override - public void onGetDescriptor(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, int descrInstId, ParcelUuid descrUuid) - throws RemoteException { - } - - @Override - public void onSearchComplete(String address, int status) throws RemoteException { + public void onSearchComplete(String address, List services, + int status) throws RemoteException { } @Override diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl new file mode 100644 index 00000000000..bbb8623e217 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattCharacteristic; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 7cdcc2c9b9f..7d698b3ef5a 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,6 +15,9 @@ */ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -26,7 +29,7 @@ import java.util.UUID; * {@link BluetoothGattService}. The characteristic contains a value as well as * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}. */ -public class BluetoothGattCharacteristic { +public class BluetoothGattCharacteristic implements Parcelable { /** * Characteristic proprty: Characteristic is broadcastable. @@ -242,6 +245,15 @@ public class BluetoothGattCharacteristic { initCharacteristic(service, uuid, instanceId, properties, permissions); } + /** + * Create a new BluetoothGattCharacteristic + * @hide + */ + public BluetoothGattCharacteristic(UUID uuid, int instanceId, + int properties, int permissions) { + initCharacteristic(null, uuid, instanceId, properties, permissions); + } + private void initCharacteristic(BluetoothGattService service, UUID uuid, int instanceId, int properties, int permissions) { @@ -260,6 +272,50 @@ public class BluetoothGattCharacteristic { } } + /** + * @hide + */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstance); + out.writeInt(mProperties); + out.writeInt(mPermissions); + out.writeTypedList(mDescriptors); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattCharacteristic createFromParcel(Parcel in) { + return new BluetoothGattCharacteristic(in); + } + + public BluetoothGattCharacteristic[] newArray(int size) { + return new BluetoothGattCharacteristic[size]; + } + }; + + private BluetoothGattCharacteristic(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstance = in.readInt(); + mProperties = in.readInt(); + mPermissions = in.readInt(); + + mDescriptors = new ArrayList(); + + ArrayList descs = + in.createTypedArrayList(BluetoothGattDescriptor.CREATOR); + if (descs != null) { + for (BluetoothGattDescriptor desc: descs) { + desc.setCharacteristic(this); + mDescriptors.add(desc); + } + } + } + /** * Returns the deisred key size. * @hide diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl new file mode 100644 index 00000000000..43932733167 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattDescriptor; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 5f525dc609a..28317c49604 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.UUID; /** @@ -25,7 +28,7 @@ import java.util.UUID; * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe * the characteristic's features or to control certain behaviours of the characteristic. */ -public class BluetoothGattDescriptor { +public class BluetoothGattDescriptor implements Parcelable { /** * Value used to enable notification for a client configuration descriptor @@ -138,6 +141,13 @@ public class BluetoothGattDescriptor { initDescriptor(characteristic, uuid, instance, permissions); } + /** + * @hide + */ + public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) { + initDescriptor(null, uuid, instance, permissions); + } + private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, int instance, int permissions) { mCharacteristic = characteristic; @@ -146,6 +156,36 @@ public class BluetoothGattDescriptor { mPermissions = permissions; } + /** + * @hide + */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstance); + out.writeInt(mPermissions); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattDescriptor createFromParcel(Parcel in) { + return new BluetoothGattDescriptor(in); + } + + public BluetoothGattDescriptor[] newArray(int size) { + return new BluetoothGattDescriptor[size]; + } + }; + + private BluetoothGattDescriptor(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstance = in.readInt(); + mPermissions = in.readInt(); + } + /** * Returns the characteristic this descriptor belongs to. * @return The characteristic. diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl new file mode 100644 index 00000000000..1ef427edc3a --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattIncludedService; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.java b/framework/java/android/bluetooth/BluetoothGattIncludedService.java new file mode 100644 index 00000000000..155dc571d23 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.java @@ -0,0 +1,110 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Represents a Bluetooth GATT Included Service + * @hide + */ +public class BluetoothGattIncludedService implements Parcelable { + + /** + * The UUID of this service. + */ + protected UUID mUuid; + + /** + * Instance ID for this service. + */ + protected int mInstanceId; + + /** + * Service type (Primary/Secondary). + */ + protected int mServiceType; + + /** + * Create a new BluetoothGattIncludedService + */ + public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) { + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstanceId); + out.writeInt(mServiceType); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattIncludedService createFromParcel(Parcel in) { + return new BluetoothGattIncludedService(in); + } + + public BluetoothGattIncludedService[] newArray(int size) { + return new BluetoothGattIncludedService[size]; + } + }; + + private BluetoothGattIncludedService(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstanceId = in.readInt(); + mServiceType = in.readInt(); + } + + /** + * Returns the UUID of this service + * + * @return UUID of this service + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns the instance ID for this service + * + *

        If a remote device offers multiple services with the same UUID + * (ex. multiple battery services for different batteries), the instance + * ID is used to distuinguish services. + * + * @return Instance ID of this service + */ + public int getInstanceId() { + return mInstanceId; + } + + /** + * Get the type of this service (primary/secondary) + */ + public int getType() { + return mServiceType; + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattService.aidl b/framework/java/android/bluetooth/BluetoothGattService.aidl new file mode 100644 index 00000000000..84314d2072d --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattService.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothGattService; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 52bc0f796cb..a4e1dc01d00 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,6 +15,9 @@ */ package android.bluetooth; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -25,7 +28,7 @@ import java.util.UUID; *

        Gatt Service contains a collection of {@link BluetoothGattCharacteristic}, * as well as referenced services. */ -public class BluetoothGattService { +public class BluetoothGattService implements Parcelable { /** * Primary service @@ -116,6 +119,81 @@ public class BluetoothGattService { mIncludedServices = new ArrayList(); } + /** + * Create a new BluetoothGattService + * @hide + */ + public BluetoothGattService(UUID uuid, int instanceId, int serviceType) { + mDevice = null; + mUuid = uuid; + mInstanceId = instanceId; + mServiceType = serviceType; + mCharacteristics = new ArrayList(); + mIncludedServices = new ArrayList(); + } + + /** + * @hide + */ + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeInt(mInstanceId); + out.writeInt(mServiceType); + out.writeTypedList(mCharacteristics); + + ArrayList includedServices = + new ArrayList(mIncludedServices.size()); + for(BluetoothGattService s : mIncludedServices) { + includedServices.add(new BluetoothGattIncludedService(s.getUuid(), + s.getInstanceId(), s.getType())); + } + out.writeTypedList(includedServices); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BluetoothGattService createFromParcel(Parcel in) { + return new BluetoothGattService(in); + } + + public BluetoothGattService[] newArray(int size) { + return new BluetoothGattService[size]; + } + }; + + private BluetoothGattService(Parcel in) { + mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mInstanceId = in.readInt(); + mServiceType = in.readInt(); + + mCharacteristics = new ArrayList(); + + ArrayList chrcs = + in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR); + if (chrcs != null) { + for (BluetoothGattCharacteristic chrc : chrcs) { + chrc.setService(this); + mCharacteristics.add(chrc); + } + } + + mIncludedServices = new ArrayList(); + + ArrayList inclSvcs = + in.createTypedArrayList(BluetoothGattIncludedService.CREATOR); + if (chrcs != null) { + for (BluetoothGattIncludedService isvc : inclSvcs) { + mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(), + isvc.getInstanceId(), isvc.getType())); + } + } + } + /** * Returns the device associated with this service. * @hide @@ -124,6 +202,14 @@ public class BluetoothGattService { return mDevice; } + /** + * Returns the device associated with this service. + * @hide + */ + /*package*/ void setDevice(BluetoothDevice device) { + this.mDevice = device; + } + /** * Add an included service to this service. *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -192,7 +278,7 @@ public class BluetoothGattService { * Add an included service to the internal map. * @hide */ - /*package*/ void addIncludedService(BluetoothGattService includedService) { + public void addIncludedService(BluetoothGattService includedService) { mIncludedServices.add(includedService); } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index cbba9f02142..f1772445612 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -16,6 +16,7 @@ package android.bluetooth; import android.os.ParcelUuid; +import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.ScanResult; @@ -29,20 +30,7 @@ oneway interface IBluetoothGattCallback { in boolean connected, in String address); void onScanResult(in ScanResult scanResult); void onBatchScanResults(in List batchResults); - void onGetService(in String address, in int srvcType, in int srvcInstId, - in ParcelUuid srvcUuid); - void onGetIncludedService(in String address, in int srvcType, in int srvcInstId, - in ParcelUuid srvcUuid, in int inclSrvcType, - in int inclSrvcInstId, in ParcelUuid inclSrvcUuid); - void onGetCharacteristic(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int charProps); - void onGetDescriptor(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid); - void onSearchComplete(in String address, in int status); + void onSearchComplete(in String address, in List services, in int status); void onCharacteristicRead(in String address, in int status, in int srvcType, in int srvcInstId, in ParcelUuid srvcUuid, in int charInstId, in ParcelUuid charUuid, -- GitLab From 4777042d3906988ae476ad3e534b3bd27e666903 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Mar 2016 16:00:16 -0700 Subject: [PATCH 0551/1408] Use handles to identify GATT attributes (4/4) Bug: 27778668 Change-Id: I01e095939df2f8dc433f14b473957c815ecade74 --- .../java/android/bluetooth/BluetoothGatt.java | 162 +++++++----------- .../BluetoothGattCallbackWrapper.java | 17 +- .../android/bluetooth/IBluetoothGatt.aidl | 25 +-- .../bluetooth/IBluetoothGattCallback.aidl | 25 +-- 4 files changed, 81 insertions(+), 148 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ef056654d7e..68442ea4070 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -246,11 +246,12 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { + public void onCharacteristicRead(String address, int status, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address - + " UUID=" + charUuid + " Status=" + status); + + " handle=" + handle + " Status=" + status); + + Log.w(TAG, "onCharacteristicRead() - Device=" + address + + " handle=" + handle + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -265,9 +266,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.readCharacteristic(mClientIf, address, - srvcType, srvcInstId, srvcUuid, - charInstId, charUuid, AUTHENTICATION_MITM); + mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM); return; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -276,13 +275,11 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); + if (characteristic == null) { + Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); + return; + } if (status == 0) characteristic.setValue(value); @@ -298,11 +295,9 @@ public final class BluetoothGatt implements BluetoothProfile { * Let the app know how we did... * @hide */ - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { + public void onCharacteristicWrite(String address, int status, int handle) { if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address - + " UUID=" + charUuid + " Status=" + status); + + " handle=" + handle + " Status=" + status); if (!address.equals(mDevice.getAddress())) { return; @@ -312,12 +307,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) return; if ((status == GATT_INSUFFICIENT_AUTHENTICATION @@ -325,8 +315,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.writeCharacteristic(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, + mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), AUTHENTICATION_MITM, characteristic.getValue()); return; @@ -349,21 +338,14 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ - public void onNotify(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - byte[] value) { - if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); + public void onNotify(String address, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) return; characteristic.setValue(value); @@ -379,12 +361,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor has been read. * @hide */ - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descrUuid, - byte[] value) { - if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); + public void onDescriptorRead(String address, int status, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; @@ -394,16 +372,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid(), descrInstId); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; if (status == 0) descriptor.setValue(value); @@ -413,9 +382,7 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.readDescriptor(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrInstId, descrUuid, AUTHENTICATION_MITM); + mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM); return; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -435,11 +402,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor write operation complete. * @hide */ - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int descrInstId, ParcelUuid descrUuid) { - if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); + public void onDescriptorWrite(String address, int status, int handle) { + if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); if (!address.equals(mDevice.getAddress())) { return; @@ -449,16 +413,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), - srvcInstId, srvcType); - if (service == null) return; - - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid.getUuid(), charInstId); - if (characteristic == null) return; - - BluetoothGattDescriptor descriptor = characteristic.getDescriptor( - descrUuid.getUuid(), descrInstId); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; if ((status == GATT_INSUFFICIENT_AUTHENTICATION @@ -466,9 +421,8 @@ public final class BluetoothGatt implements BluetoothProfile { && mAuthRetry == false) { try { mAuthRetry = true; - mService.writeDescriptor(mClientIf, address, - srvcType, srvcInstId, srvcUuid, charInstId, charUuid, - descrInstId, descrUuid, characteristic.getWriteType(), + mService.writeDescriptor(mClientIf, address, handle, + descriptor.getCharacteristic().getWriteType(), AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -584,6 +538,37 @@ public final class BluetoothGatt implements BluetoothProfile { } + /** + * Returns a characteristic with id equal to instanceId. + * @hide + */ + /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId); + if (charac.getInstanceId() == instanceId) + return charac; + } + } + return null; + } + + /** + * Returns a descriptor with id equal to instanceId. + * @hide + */ + /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + for(BluetoothGattDescriptor desc : charac.getDescriptors()) { + if (desc.getInstanceId() == instanceId) + return desc; + } + } + } + return null; + } + /** * Register an application callback to start using GATT. * @@ -832,9 +817,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readCharacteristic(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); + characteristic.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -877,11 +860,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeCharacteristic(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - characteristic.getWriteType(), AUTHENTICATION_NONE, - characteristic.getValue()); + characteristic.getInstanceId(), characteristic.getWriteType(), + AUTHENTICATION_NONE, characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -922,11 +902,8 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), - service.getInstanceId(), new ParcelUuid(service.getUuid()), - characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), - descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), - AUTHENTICATION_NONE); + mService.readDescriptor(mClientIf, device.getAddress(), + descriptor.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -966,12 +943,8 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), - service.getInstanceId(), new ParcelUuid(service.getUuid()), - characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), - descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), - characteristic.getWriteType(), AUTHENTICATION_NONE, - descriptor.getValue()); + mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), + characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; @@ -1102,10 +1075,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification(mClientIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), - enable); + characteristic.getInstanceId(), enable); } catch (RemoteException e) { Log.e(TAG,"",e); return false; diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 64ead54e3d5..17e533a5b32 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -54,14 +54,12 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onCharacteristicRead(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) + public void onCharacteristicRead(String address, int status, int handle, byte[] value) throws RemoteException { } @Override - public void onCharacteristicWrite(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid) throws RemoteException { + public void onCharacteristicWrite(String address, int status, int handle) throws RemoteException { } @Override @@ -69,20 +67,15 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onDescriptorRead(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, - ParcelUuid descrUuid, byte[] value) throws RemoteException { + public void onDescriptorRead(String address, int status, int handle, byte[] value) throws RemoteException { } @Override - public void onDescriptorWrite(String address, int status, int srvcType, int srvcInstId, - ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, int descrInstId, - ParcelUuid descrUuid) throws RemoteException { + public void onDescriptorWrite(String address, int status, int handle) throws RemoteException { } @Override - public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) throws RemoteException { + public void onNotify(String address, int handle, byte[] value) throws RemoteException { } @Override diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 6b5f77faacd..adb07df2977 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -51,28 +51,13 @@ interface IBluetoothGatt { void clientDisconnect(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); - void readCharacteristic(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int authReq); - void writeCharacteristic(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, + void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); + void writeCharacteristic(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); - void readDescriptor(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int descrInstanceId, in ParcelUuid descrUuid, - in int authReq); - void writeDescriptor(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in int descrInstanceId, in ParcelUuid descrId, + void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); + void writeDescriptor(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); - void registerForNotification(in int clientIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, - in boolean enable); + void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index f1772445612..7163c370694 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -31,27 +31,12 @@ oneway interface IBluetoothGattCallback { void onScanResult(in ScanResult scanResult); void onBatchScanResults(in List batchResults); void onSearchComplete(in String address, in List services, in int status); - void onCharacteristicRead(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in byte[] value); - void onCharacteristicWrite(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid); + void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); + void onCharacteristicWrite(in String address, in int status, in int handle); void onExecuteWrite(in String address, in int status); - void onDescriptorRead(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid, - in byte[] value); - void onDescriptorWrite(in String address, in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in int descrInstId, in ParcelUuid descrUuid); - void onNotify(in String address, in int srvcType, - in int srvcInstId, in ParcelUuid srvcUuid, - in int charInstId, in ParcelUuid charUuid, - in byte[] value); + void onDescriptorRead(in String address, in int status, in int handle, in byte[] value); + void onDescriptorWrite(in String address, in int status, in int handle); + void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); void onMultiAdvertiseCallback(in int status, boolean isStart, in AdvertiseSettings advertiseSettings); -- GitLab From dbde3ed88aa45578de3a2056e19c4db65df37fd9 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 29 Mar 2016 18:55:57 -0700 Subject: [PATCH 0552/1408] Fix log spam in getCharacteristicById() Bug: 27744135 Change-Id: I43f6358484729dedd85eb52432ad9805a66ff81e --- framework/java/android/bluetooth/BluetoothGatt.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 68442ea4070..f6d2268343a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -545,7 +545,6 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) { for(BluetoothGattService svc : mServices) { for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId); if (charac.getInstanceId() == instanceId) return charac; } -- GitLab From 85e6d2bcd1f01c8185af23ebe0df881a15a3b438 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 29 Mar 2016 20:52:38 -0700 Subject: [PATCH 0553/1408] Always use Write Request for GATT descriptor writes According to the Bluetooth Core specification v4.2, Vol 3, Part G, section 4.12.3: "The Attribute Protocol WRITE REQUEST is used used for this sub-procedure". Change-Id: I86e4e1d3a8bfd7d78dfed8419f8abd2d7e89b2bc --- framework/java/android/bluetooth/BluetoothGatt.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index f6d2268343a..b8a40dc6895 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -422,7 +422,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mAuthRetry = true; mService.writeDescriptor(mClientIf, address, handle, - descriptor.getCharacteristic().getWriteType(), + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -943,7 +943,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE, + descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; -- GitLab From 10dfb4bf91cae777ada4eb308415de5589fa501b Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Mon, 21 Mar 2016 15:49:48 -0700 Subject: [PATCH 0554/1408] PBAP client Settings profile. Add content to Settings and SettingsLib to add a checkbox for PBAP client profile, and remove the box for PBAP server profile when the client profile is enabled. Bug: 27642222 Change-Id: I125559904a8e017a01ae90b1f6425129a87cbee3 --- .../bluetooth/BluetoothPbapClient.java | 72 +++++++++++++++++-- .../bluetooth/IBluetoothPbapClient.aidl | 2 + 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 736e55d17d8..eab4c6f5130 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -40,7 +40,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; private IBluetoothPbapClient mService; - private BluetoothDevice mDevice; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -173,7 +172,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { } if (mService != null && isEnabled() && isValidDevice(device)) { try { - mDevice = device; return mService.connect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); @@ -193,13 +191,13 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return false on error, * true otherwise */ - public boolean disconnect() { + public boolean disconnect(BluetoothDevice device) { if (DBG) { - log("disconnect(" + mDevice + ")"); + log("disconnect(" + device + ")" + new Exception() ); } - if (mService != null && isEnabled() && isValidDevice(mDevice)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { - mService.disconnect(mDevice); + mService.disconnect(device); return true; } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); @@ -328,4 +326,66 @@ public final class BluetoothPbapClient implements BluetoothProfile { } return false; } + + /** + * 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 + * @param priority + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) { + log("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("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; + } } diff --git a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl index b26ea295714..6d4c5a6f90b 100644 --- a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl @@ -29,4 +29,6 @@ interface IBluetoothPbapClient { List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); } -- GitLab From 496bf39ba3c20011e558c1cce4801b455a0c1592 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 29 Mar 2016 20:52:38 -0700 Subject: [PATCH 0555/1408] Always use Write Request for GATT descriptor writes According to the Bluetooth Core specification v4.2, Vol 3, Part G, section 4.12.3: "The Attribute Protocol WRITE REQUEST is used used for this sub-procedure". Change-Id: I86e4e1d3a8bfd7d78dfed8419f8abd2d7e89b2bc --- framework/java/android/bluetooth/BluetoothGatt.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 68442ea4070..e3b1bf92d90 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -422,7 +422,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mAuthRetry = true; mService.writeDescriptor(mClientIf, address, handle, - descriptor.getCharacteristic().getWriteType(), + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -944,7 +944,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE, + descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; -- GitLab From 94dde7fab08a7949f22b2dc87e647476eceb2ae4 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 29 Mar 2016 18:55:57 -0700 Subject: [PATCH 0556/1408] Fix log spam in getCharacteristicById() Bug: 27744135 Change-Id: I43f6358484729dedd85eb52432ad9805a66ff81e --- framework/java/android/bluetooth/BluetoothGatt.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 68442ea4070..f6d2268343a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -545,7 +545,6 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) { for(BluetoothGattService svc : mServices) { for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId); if (charac.getInstanceId() == instanceId) return charac; } -- GitLab From 9a12f3128813b89dd5677e37c709077b7c393f3d Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 29 Feb 2016 16:09:14 -0800 Subject: [PATCH 0557/1408] Allow factory reset when bluetooth is off (1/2) Bug: 27348444 Change-Id: I6c2709371b86581709649d7faf09391230449b9b --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6b3e7ab4eee..ceb7e91bf72 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -1017,6 +1018,8 @@ public final class BluetoothAdapter { try { if (mService != null) { return mService.factoryReset(); + } else { + SystemProperties.set("persist.bluetooth.factoryreset", "true"); } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; -- GitLab From 172870b05fc6c65164ed854e3b5edaa19856e055 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 1 Apr 2016 07:17:15 -0700 Subject: [PATCH 0558/1408] Fix GATT Characteristic write type serialization Default writeType for GATT characteristic was not being serialized into Parcel. This cause errors when trying to write into Characteristic. Bug: 27910548 Change-Id: Ib2f88cf991123eaea244f16fa36deb0d773c5a33 --- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 7d698b3ef5a..01f82e693de 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -284,6 +284,8 @@ public class BluetoothGattCharacteristic implements Parcelable { out.writeInt(mInstance); out.writeInt(mProperties); out.writeInt(mPermissions); + out.writeInt(mKeySize); + out.writeInt(mWriteType); out.writeTypedList(mDescriptors); } @@ -303,6 +305,8 @@ public class BluetoothGattCharacteristic implements Parcelable { mInstance = in.readInt(); mProperties = in.readInt(); mPermissions = in.readInt(); + mKeySize = in.readInt(); + mWriteType = in.readInt(); mDescriptors = new ArrayList(); -- GitLab From d684c548283f89b8df6e2bd213b16b76053b5aa0 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 31 Mar 2016 14:14:27 -0700 Subject: [PATCH 0559/1408] Get name and address without enabling Bluetooth Bug: 27665077 Change-Id: I2a06c1a65f22b2f9095a859f5bdd39d4626da165 --- .../android/bluetooth/BluetoothManager.java | 2 +- .../bluetooth/BluetoothManagerService.java | 56 ++++++++++++++----- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index e355a1c0a53..35437a1fd70 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -144,7 +144,7 @@ public final class BluetoothManager { } /** - * + * * Get a list of devices that match any of the given connection * states. * diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 799a763845d..0a814ab579d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -92,6 +92,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60; private static final int MESSAGE_TIMEOUT_BIND = 100; private static final int MESSAGE_TIMEOUT_UNBIND = 101; + private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200; private static final int MESSAGE_USER_SWITCHED = 300; private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; @@ -599,8 +600,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(true); } return true; - } + public boolean enable() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { @@ -763,9 +764,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendEnableMsg(mQuietEnableExternal); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); - enable(); - waitForOnOff(true, false); - disable(true); + Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(getMsg); } } @@ -1076,6 +1076,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); private class BluetoothHandler extends Handler { + boolean mGetNameAddressOnly = false; + public BluetoothHandler(Looper looper) { super(looper); } @@ -1084,6 +1086,37 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void handleMessage(Message msg) { if (DBG) Slog.d (TAG, "Message: " + msg.what); switch (msg.what) { + case MESSAGE_GET_NAME_AND_ADDRESS: + if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); + synchronized(mConnection) { + if ((mBluetooth == null) && (!mBinding)) { + if (DBG) Slog.d(TAG, "Binding to service to get name and address"); + mGetNameAddressOnly = true; + Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); + mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); + Intent i = new Intent(IBluetooth.class.getName()); + if (!doBind(i, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT)) { + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + } else { + mBinding = true; + } + } else if (mBluetooth != null) { + try { + storeNameAndAddress(mBluetooth.getName(), + mBluetooth.getAddress()); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to grab names", re); + } + if (mGetNameAddressOnly && !mEnable) { + unbindAndFinish(); + } + mGetNameAddressOnly = false; + } + } + break; + case MESSAGE_ENABLE: if (DBG) { Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); @@ -1177,6 +1210,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothBinder = service; mBluetooth = IBluetooth.Stub.asInterface(service); + if (!isNameAndAddressSet()) { + Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); + mHandler.sendMessage(getMsg); + if (mGetNameAddressOnly) return; + } + try { boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); @@ -1187,15 +1226,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG,"Unable to call configHciSnoopLog", e); } - if (!isNameAndAddressSet()) { - try { - storeNameAndAddress(mBluetooth.getName(), - mBluetooth.getAddress()); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to grab names", re); - } - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From 75f3ec20467e276ce24fb55758ee02e360ddc9cd Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 29 Feb 2016 16:09:14 -0800 Subject: [PATCH 0560/1408] Allow factory reset when bluetooth is off (1/2) Bug: 27348444 Change-Id: I6c2709371b86581709649d7faf09391230449b9b --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d762a172727..2a7eff88309 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -1015,6 +1016,8 @@ public final class BluetoothAdapter { try { if (mService != null) { return mService.factoryReset(); + } else { + SystemProperties.set("persist.bluetooth.factoryreset", "true"); } } catch (RemoteException e) {Log.e(TAG, "", e);} return false; -- GitLab From 6b31ff83910fe3ceb75f4af508bff62c13d71bc5 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 11 Apr 2016 12:18:18 -0700 Subject: [PATCH 0561/1408] BatteryStats: Introduce Async external stats requests Instead of calling out to external processes with a blocking IPC, pass along a Binder on which the external process can pass back the response. The calling process can then wait for the reply with a timeout. This eliminates watchdog restarts of the system_server when an external process like telephony or bluetooth hangs. Bug:26842468 Change-Id: I1b242e4ed22a63f1a4a0be8c78de8ac4d7bf56c5 --- .../BluetoothActivityEnergyInfo.java | 6 +-- .../android/bluetooth/BluetoothAdapter.java | 53 +++++++++++++++---- .../java/android/bluetooth/IBluetooth.aidl | 10 ++++ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index e32a47056fe..84f6060d3ae 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -156,8 +156,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { * @return if the record is valid */ public boolean isValid() { - return ((mControllerTxTimeMs !=0) || - (mControllerRxTimeMs !=0) || - (mControllerIdleTimeMs !=0)); + return ((mControllerTxTimeMs >=0) && + (mControllerRxTimeMs >=0) && + (mControllerIdleTimeMs >=0)); } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2a7eff88309..e74847711bd 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -31,11 +31,14 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.os.BatteryStats; import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SynchronousResultReceiver; import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -53,6 +56,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; /** * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} @@ -1369,33 +1373,62 @@ public final class BluetoothAdapter { * * @return a record with {@link BluetoothActivityEnergyInfo} or null if * report is unavailable or unsupported + * @deprecated use the asynchronous + * {@link #requestControllerActivityEnergyInfo(int, ResultReceiver)} instead. * @hide */ + @Deprecated public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { - if (getState() != STATE_ON) return null; + SynchronousResultReceiver receiver = new SynchronousResultReceiver(); + requestControllerActivityEnergyInfo(updateType, receiver); + try { + SynchronousResultReceiver.Result result = receiver.awaitResult(1000); + if (result.bundle != null) { + return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY); + } + } catch (TimeoutException e) { + Log.e(TAG, "getControllerActivityEnergyInfo timed out"); + } + return null; + } + + /** + * Request the record of {@link BluetoothActivityEnergyInfo} object that + * has the activity and energy info. This can be used to ascertain what + * the controller has been up to, since the last sample. + * + * A null value for the activity info object may be sent if the bluetooth service is + * unreachable or the device does not support reporting such information. + * + * @param updateType Type of info, cached vs refreshed. + * @param result The callback to which to send the activity info. + * @hide + */ + public void requestControllerActivityEnergyInfo(int updateType, ResultReceiver result) { + if (getState() != STATE_ON) { + result.send(0, null); + return; + } + try { - BluetoothActivityEnergyInfo record; if (!mService.isActivityAndEnergyReportingSupported()) { - return null; + result.send(0, null); + return; } synchronized(this) { if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) { mService.getActivityEnergyInfoFromController(); wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS); } - record = mService.reportActivityInfo(); - if (record.isValid()) { - return record; - } else { - return null; - } + mService.requestActivityInfo(result); } } catch (InterruptedException e) { Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e); + result.send(0, null); } catch (RemoteException e) { Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); + result.send(0, null); } - return null; } /** diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 9cd7d0585af..45d864150d7 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.OobData; import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; +import android.os.ResultReceiver; /** * System private API for talking with the Bluetooth service. @@ -104,6 +105,15 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); + /** + * Requests the controller activity info asynchronously. + * The implementor is expected to reply with the + * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the + * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. + * The result code is ignored. + */ + oneway void requestActivityInfo(in ResultReceiver result); + void onLeServiceUp(); void onBrEdrDown(); } -- GitLab From fd07665afa86de55ce6eba3835549ea84080c1bc Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 14 Apr 2016 06:06:00 -0700 Subject: [PATCH 0562/1408] OobData documentation Bug: 27385555 Change-Id: Ie290914e4d2b9378c6dff3ae14d96d6f8b13ab9d --- framework/java/android/bluetooth/OobData.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 70d47ee20b2..8e659e04d70 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -22,7 +22,11 @@ import android.os.Parcelable; import android.util.Log; /** - * Out Of Band Data for Bluetooth device. + * Out Of Band Data for Bluetooth device pairing. + * + *

        This object represents optional data obtained from a remote device through + * an out-of-band channel (eg. NFC). + * * @hide */ public class OobData implements Parcelable { @@ -32,6 +36,11 @@ public class OobData implements Parcelable { return securityManagerTk; } + /** + * Sets the Temporary Key value to be used by the LE Security Manager during + * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6, + * Part A 1.8 for a detailed description. + */ public void setSecurityManagerTk(byte[] securityManagerTk) { this.securityManagerTk = securityManagerTk; } -- GitLab From 6923ac4005848169b797b926620cef4ecbe9eba2 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Fri, 6 May 2016 12:05:47 -0700 Subject: [PATCH 0563/1408] Fix divergent equals and hashCode behavior Calling Objects.hash with a byte[] will call the identity hashCode on the byte[] and this doesn't agree with the use of Objects.deepEquals in equals. Bug caught by error prone. Also, replaced usage of Objects.deepEquals(mServiceDataUuid, ...) with Objects.equals(mServiceDataUuid, ...), because mServiceDataUuid is an Object of type ParcelUuid. Bug: 28585195 Change-Id: Id92734874339985fedafe1a28286a6a4dcd88d3b --- .../java/android/bluetooth/le/ScanFilter.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 92a38171a16..17a802d59f4 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -385,9 +385,13 @@ public final class ScanFilter implements Parcelable { @Override public int hashCode() { - return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, mManufacturerData, - mManufacturerDataMask, mServiceDataUuid, mServiceData, mServiceDataMask, - mServiceUuid, mServiceUuidMask); + return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, + Arrays.hashCode(mManufacturerData), + Arrays.hashCode(mManufacturerDataMask), + mServiceDataUuid, + Arrays.hashCode(mServiceData), + Arrays.hashCode(mServiceDataMask), + mServiceUuid, mServiceUuidMask); } @Override @@ -401,10 +405,10 @@ public final class ScanFilter implements Parcelable { ScanFilter other = (ScanFilter) obj; return Objects.equals(mDeviceName, other.mDeviceName) && Objects.equals(mDeviceAddress, other.mDeviceAddress) && - mManufacturerId == other.mManufacturerId && + mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - Objects.deepEquals(mServiceDataUuid, other.mServiceDataUuid) && + Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && -- GitLab From 906e9867efb8e066527c595b29b4b27395f6809e Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Fri, 6 May 2016 12:05:47 -0700 Subject: [PATCH 0564/1408] Fix divergent equals and hashCode behavior Calling Objects.hash with a byte[] will call the identity hashCode on the byte[] and this doesn't agree with the use of Objects.deepEquals in equals. Bug caught by error prone. Also, replaced usage of Objects.deepEquals(mServiceDataUuid, ...) with Objects.equals(mServiceDataUuid, ...), because mServiceDataUuid is an Object of type ParcelUuid. Bug: 28585195 Change-Id: Id92734874339985fedafe1a28286a6a4dcd88d3b --- .../java/android/bluetooth/le/ScanFilter.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 92a38171a16..17a802d59f4 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -385,9 +385,13 @@ public final class ScanFilter implements Parcelable { @Override public int hashCode() { - return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, mManufacturerData, - mManufacturerDataMask, mServiceDataUuid, mServiceData, mServiceDataMask, - mServiceUuid, mServiceUuidMask); + return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, + Arrays.hashCode(mManufacturerData), + Arrays.hashCode(mManufacturerDataMask), + mServiceDataUuid, + Arrays.hashCode(mServiceData), + Arrays.hashCode(mServiceDataMask), + mServiceUuid, mServiceUuidMask); } @Override @@ -401,10 +405,10 @@ public final class ScanFilter implements Parcelable { ScanFilter other = (ScanFilter) obj; return Objects.equals(mDeviceName, other.mDeviceName) && Objects.equals(mDeviceAddress, other.mDeviceAddress) && - mManufacturerId == other.mManufacturerId && + mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - Objects.deepEquals(mServiceDataUuid, other.mServiceDataUuid) && + Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && -- GitLab From 39d90151aa8db5f7bde321db5f10af9794d1259e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 10 May 2016 14:00:03 -0700 Subject: [PATCH 0565/1408] BluetoothManager: Make requestControllerActivityInfo one call Instead of making multiple calls into the Bluetooth service, make one call that can timeout. This helps prevent cases when the Bluetooth process hangs and the system_server is calling into it and causes a WATCHDOG restart. Bug:28658141 Change-Id: I37778b7b6e508be420a21bdf23593ae89b38f5b8 --- .../android/bluetooth/BluetoothAdapter.java | 40 ++++++------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e74847711bd..d036d96257e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -472,12 +472,6 @@ public final class BluetoothAdapter { private static final int ADDRESS_LENGTH = 17; - private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30; - /** @hide */ - public static final int ACTIVITY_ENERGY_INFO_CACHED = 0; - /** @hide */ - public static final int ACTIVITY_ENERGY_INFO_REFRESHED = 1; - /** * Lazily initialized singleton. Guaranteed final after first object * constructed. @@ -1374,13 +1368,13 @@ public final class BluetoothAdapter { * @return a record with {@link BluetoothActivityEnergyInfo} or null if * report is unavailable or unsupported * @deprecated use the asynchronous - * {@link #requestControllerActivityEnergyInfo(int, ResultReceiver)} instead. + * {@link #requestControllerActivityEnergyInfo(ResultReceiver)} instead. * @hide */ @Deprecated public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { SynchronousResultReceiver receiver = new SynchronousResultReceiver(); - requestControllerActivityEnergyInfo(updateType, receiver); + requestControllerActivityEnergyInfo(receiver); try { SynchronousResultReceiver.Result result = receiver.awaitResult(1000); if (result.bundle != null) { @@ -1400,34 +1394,24 @@ public final class BluetoothAdapter { * A null value for the activity info object may be sent if the bluetooth service is * unreachable or the device does not support reporting such information. * - * @param updateType Type of info, cached vs refreshed. * @param result The callback to which to send the activity info. * @hide */ - public void requestControllerActivityEnergyInfo(int updateType, ResultReceiver result) { - if (getState() != STATE_ON) { - result.send(0, null); - return; - } - + public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { - if (!mService.isActivityAndEnergyReportingSupported()) { - result.send(0, null); - return; - } - synchronized(this) { - if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) { - mService.getActivityEnergyInfoFromController(); - wait(CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS); + synchronized(mManagerCallback) { + if (mService != null) { + mService.requestActivityInfo(result); + result = null; } - mService.requestActivityInfo(result); } - } catch (InterruptedException e) { - Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e); - result.send(0, null); } catch (RemoteException e) { Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); - result.send(0, null); + } finally { + if (result != null) { + // Only send an immediate result if we failed. + result.send(0, null); + } } } diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 45d864150d7..a4205396367 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -102,7 +102,6 @@ interface IBluetooth boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); - void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); /** -- GitLab From 9fd5a79082001ba2573a70c111e378ac177b2789 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 1 Apr 2016 07:17:15 -0700 Subject: [PATCH 0566/1408] Fix GATT Characteristic write type serialization Default writeType for GATT characteristic was not being serialized into Parcel. This cause errors when trying to write into Characteristic. Bug: 27910548 Change-Id: Ib2f88cf991123eaea244f16fa36deb0d773c5a33 --- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 7d698b3ef5a..01f82e693de 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -284,6 +284,8 @@ public class BluetoothGattCharacteristic implements Parcelable { out.writeInt(mInstance); out.writeInt(mProperties); out.writeInt(mPermissions); + out.writeInt(mKeySize); + out.writeInt(mWriteType); out.writeTypedList(mDescriptors); } @@ -303,6 +305,8 @@ public class BluetoothGattCharacteristic implements Parcelable { mInstance = in.readInt(); mProperties = in.readInt(); mPermissions = in.readInt(); + mKeySize = in.readInt(); + mWriteType = in.readInt(); mDescriptors = new ArrayList(); -- GitLab From 8eaabdf8a1ff6bed1a7633b22619af15adfd9272 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 9 Nov 2015 19:45:53 -0800 Subject: [PATCH 0567/1408] Properly handle registration timeout in BLE. Bug:25384098 Change-Id: I7877f6368c4982fcd91e68810c4da0ab7b5fc6b7 --- .../bluetooth/le/BluetoothLeAdvertiser.java | 19 ++++++++++---- .../bluetooth/le/BluetoothLeScanner.java | 25 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index eaf20d8e19b..d468bd416ed 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -237,8 +237,8 @@ public final class BluetoothLeAdvertiser { private final IBluetoothGatt mBluetoothGatt; // mClientIf 0: not registered - // -1: scan stopped - // >0: registered and scan started + // -1: advertise stopped or registration timeout + // >0: registered and advertising started private int mClientIf; private boolean mIsAdvertising = false; @@ -268,6 +268,10 @@ public final class BluetoothLeAdvertiser { if (mClientIf > 0 && mIsAdvertising) { mLeAdvertisers.put(mAdvertiseCallback, this); } else if (mClientIf <= 0) { + + // Registration timeout, reset mClientIf to -1 so no subsequent operations can + // proceed. + if (mClientIf == 0) mClientIf = -1; // Post internal error if registration failed. postStartFailure(mAdvertiseCallback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); @@ -308,10 +312,15 @@ public final class BluetoothLeAdvertiser { Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { - mClientIf = clientIf; try { - mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, - mScanResponse, mSettings); + if (mClientIf == -1) { + // Registration succeeds after timeout, unregister client. + mBluetoothGatt.unregisterClient(clientIf); + } else { + mClientIf = clientIf; + mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, + mScanResponse, mSettings); + } return; } catch (RemoteException e) { Log.e(TAG, "failed to start advertising", e); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 03449ccec35..5715ff87242 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -278,7 +278,7 @@ public final class BluetoothLeScanner { private List> mResultStorages; // mLeHandle 0: not registered - // -1: scan stopped + // -1: scan stopped or registration failed // > 0: registered and scan started private int mClientIf; @@ -310,6 +310,9 @@ public final class BluetoothLeScanner { if (mClientIf > 0) { mLeScanClients.put(mScanCallback, this); } else { + // Registration timed out or got exception, reset clientIf to -1 so no + // subsequent operations can proceed. + if (mClientIf == 0) mClientIf = -1; postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); } @@ -352,19 +355,19 @@ public final class BluetoothLeScanner { @Override public void onClientRegistered(int status, int clientIf) { Log.d(TAG, "onClientRegistered() - status=" + status + - " clientIf=" + clientIf); + " clientIf=" + clientIf + " mClientIf=" + mClientIf); synchronized (this) { - if (mClientIf == -1) { - if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); - } - if (status == BluetoothGatt.GATT_SUCCESS) { - mClientIf = clientIf; try { - mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, - mWorkSource, mResultStorages, - ActivityThread.currentOpPackageName()); - + if (mClientIf == -1) { + // Registration succeeds after timeout, unregister client. + mBluetoothGatt.unregisterClient(clientIf); + } else { + mClientIf = clientIf; + mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, + mWorkSource, mResultStorages, + ActivityThread.currentOpPackageName()); + } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; -- GitLab From 1945dd9748228ac517a6ff06efa8c146dbae1e6b Mon Sep 17 00:00:00 2001 From: Sungki Kim Date: Thu, 19 May 2016 10:18:07 -0700 Subject: [PATCH 0568/1408] Fix GATT autoConnect race condition As pointed out here: https://code.google.com/p/android/issues/detail?id=69834 registerApp() causes onClientRegistered() to happen before autoConnect is set. This patch fixes that. Bug: 28861330 Change-Id: Ie1174c0f224f5084178439420b383164d22d542c --- framework/java/android/bluetooth/BluetoothGatt.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index b8a40dc6895..800dd434a96 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -645,6 +645,9 @@ public final class BluetoothGatt implements BluetoothProfile { } mConnState = CONN_STATE_CONNECTING; } + + mAutoConnect = autoConnect; + if (!registerApp(callback)) { synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; @@ -653,8 +656,7 @@ public final class BluetoothGatt implements BluetoothProfile { return false; } - // the connection will continue after successful callback registration - mAutoConnect = autoConnect; + // The connection will continue in the onClientRegistered callback return true; } -- GitLab From 183d931fb31509a4a108f2577f70dab75998cf65 Mon Sep 17 00:00:00 2001 From: Sungki Kim Date: Thu, 19 May 2016 10:18:07 -0700 Subject: [PATCH 0569/1408] Fix GATT autoConnect race condition As pointed out here: https://code.google.com/p/android/issues/detail?id=69834 registerApp() causes onClientRegistered() to happen before autoConnect is set. This patch fixes that. Bug: 28861330 Change-Id: Ie1174c0f224f5084178439420b383164d22d542c --- framework/java/android/bluetooth/BluetoothGatt.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index b8a40dc6895..800dd434a96 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -645,6 +645,9 @@ public final class BluetoothGatt implements BluetoothProfile { } mConnState = CONN_STATE_CONNECTING; } + + mAutoConnect = autoConnect; + if (!registerApp(callback)) { synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; @@ -653,8 +656,7 @@ public final class BluetoothGatt implements BluetoothProfile { return false; } - // the connection will continue after successful callback registration - mAutoConnect = autoConnect; + // The connection will continue in the onClientRegistered callback return true; } -- GitLab From 292415fd2fca1ae537459a1a117f4650d6123564 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Thu, 21 Apr 2016 14:10:55 -0700 Subject: [PATCH 0570/1408] While turning OFF do not honor ON requests. Native stack does not handle being put from OFF -> ON state without doing a complete cleanup. Hence instead of going from start -> ON -> OFF -> cleanup it goes start -> ON -> OFF ->ON -> ... usually leads to race conditions down the road in native. This patch is a workaround so that we can throw away the requests if we are in currently "turning off" phase. The side-effect would be that user will need to turn it ON again. The race happens when the turn OFF time is longer but usually it is found to be close to order of seconds hence the wait should be bounded. Bug: b/28318203 Change-Id: I14f6633f31311e5b561e1dcbc8a9d6d2a5dd6fdc --- .../server/bluetooth/BluetoothManagerService.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0a814ab579d..5e8687a904c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -613,7 +613,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding); + " mBinding = " + mBinding + " mState = " + mState); + } + // We do not honor ON requests when the adapter is already turned ON or in the process of + // turning ON. + // As a protective mechanism to make sure that the native stack gets cleaned up properly + // before turning it back ON we ignore requests while the bluetooth is turning OFF. + // Bug: b/28318203 + if (mState == BluetoothAdapter.STATE_BLE_TURNING_OFF || + mState == BluetoothAdapter.STATE_TURNING_OFF || + mState == BluetoothAdapter.STATE_ON || + mState == BluetoothAdapter.STATE_BLE_ON || + mState == BluetoothAdapter.STATE_TURNING_ON || + mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + return false; } synchronized(mReceiver) { -- GitLab From 6d126c81896c8956f36a162382815b121f3e2be1 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sun, 22 May 2016 22:16:41 -0700 Subject: [PATCH 0571/1408] Reduced the impact of "synchronized" statements * Removed "synchronized" statements that are not needed * Replaced "synchronized" statements with Read/Write lock as appropriate. The lock protects the access to and the setting of BluetoothAdapter.mService and BluetoothManagerService.mBluetooth and associated state. Bug: 28734075 Bug: 28799467 Change-Id: I8f8281c505f0a1ae0add1e14a3caba1f5b2a98e4 --- .../android/bluetooth/BluetoothAdapter.java | 367 +++++++++++------- .../bluetooth/BluetoothManagerService.java | 271 +++++++------ 2 files changed, 389 insertions(+), 249 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d036d96257e..4c8657810c7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -57,6 +57,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} @@ -483,6 +484,8 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; + private final ReentrantReadWriteLock mServiceLock = + new ReentrantReadWriteLock(); private final Object mLock = new Object(); private final Map mLeScanClients; @@ -517,8 +520,13 @@ public final class BluetoothAdapter { throw new IllegalArgumentException("bluetooth manager service is null"); } try { + mServiceLock.writeLock().lock(); mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.writeLock().unlock(); + } mManagerService = managerService; mLeScanClients = new HashMap(); mToken = new Binder(); @@ -605,10 +613,14 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { try { - synchronized(mManagerCallback) { - if (mService != null) return mService.isEnabled(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.isEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; } @@ -639,12 +651,12 @@ public final class BluetoothAdapter { * or OFF if BT is in BLE_ON state */ private void notifyUserAction(boolean enable) { - if (mService == null) { - Log.e(TAG, "mService is null"); - return; - } - try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.e(TAG, "mService is null"); + return; + } if (enable) { mService.onLeServiceUp(); //NA:TODO implementation pending } else { @@ -652,6 +664,8 @@ public final class BluetoothAdapter { } } catch (RemoteException e) { Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } } @@ -783,26 +797,28 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { + int state = BluetoothAdapter.STATE_OFF; + try { - synchronized(mManagerCallback) { - if (mService != null) - { - int state= mService.getState(); - if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); - //consider all internal states as OFF - if (state == BluetoothAdapter.STATE_BLE_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) Log.d(TAG, "Consider internal state as OFF"); - state = BluetoothAdapter.STATE_OFF; - } - return state; - } - // TODO(BT) there might be a small gap during STATE_TURNING_ON that - // mService is null, handle that case + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); } - } catch (RemoteException e) {Log.e(TAG, "", e);} - return STATE_OFF; + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + // Consider all internal states as OFF + if (state == BluetoothAdapter.STATE_BLE_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { + if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + state = BluetoothAdapter.STATE_OFF; + } + if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + return state; } /** @@ -825,19 +841,21 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getLeState() { + int state = BluetoothAdapter.STATE_OFF; + try { - synchronized(mManagerCallback) { - if (mService != null) - { - int state= mService.getState(); - if (VDBG) Log.d(TAG,"getLeState() returning " + state); - return state; - } + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); } } catch (RemoteException e) { Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } - return BluetoothAdapter.STATE_OFF; + + if (VDBG) Log.d(TAG,"getLeState() returning " + state); + return state; } boolean getLeAccess() { @@ -879,16 +897,21 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { - int state = STATE_OFF; - if (isEnabled() == true){ + int state = BluetoothAdapter.STATE_OFF; + if (isEnabled() == true) { if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } - //Use service interface to get the exact state - if (mService != null) { - try { - state = mService.getState(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + // Use service interface to get the exact state + try { + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } if (state == BluetoothAdapter.STATE_BLE_ON) { @@ -993,10 +1016,13 @@ public final class BluetoothAdapter { */ public boolean configHciSnoopLog(boolean enable) { try { - synchronized(mManagerCallback) { - if (mService != null) return mService.configHciSnoopLog(enable); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.configHciSnoopLog(enable); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1012,12 +1038,16 @@ public final class BluetoothAdapter { */ public boolean factoryReset() { try { + mServiceLock.readLock().lock(); if (mService != null) { return mService.factoryReset(); - } else { - SystemProperties.set("persist.bluetooth.factoryreset", "true"); } - } catch (RemoteException e) {Log.e(TAG, "", e);} + SystemProperties.set("persist.bluetooth.factoryreset", "true"); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1032,10 +1062,13 @@ public final class BluetoothAdapter { public ParcelUuid[] getUuids() { if (getState() != STATE_ON) return null; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getUuids(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getUuids(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return null; } @@ -1058,10 +1091,13 @@ public final class BluetoothAdapter { public boolean setName(String name) { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.setName(name); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.setName(name); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1086,10 +1122,13 @@ public final class BluetoothAdapter { public int getScanMode() { if (getState() != STATE_ON) return SCAN_MODE_NONE; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getScanMode(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getScanMode(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return SCAN_MODE_NONE; } @@ -1124,10 +1163,13 @@ public final class BluetoothAdapter { public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.setScanMode(mode, duration); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.setScanMode(mode, duration); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1142,10 +1184,13 @@ public final class BluetoothAdapter { public int getDiscoverableTimeout() { if (getState() != STATE_ON) return -1; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getDiscoverableTimeout(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getDiscoverableTimeout(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return -1; } @@ -1153,10 +1198,13 @@ public final class BluetoothAdapter { public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) return; try { - synchronized(mManagerCallback) { - if (mService != null) mService.setDiscoverableTimeout(timeout); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } } /** @@ -1193,10 +1241,13 @@ public final class BluetoothAdapter { public boolean startDiscovery() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.startDiscovery(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.startDiscovery(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1221,10 +1272,13 @@ public final class BluetoothAdapter { public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.cancelDiscovery(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.cancelDiscovery(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1251,10 +1305,13 @@ public final class BluetoothAdapter { public boolean isDiscovering() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null ) return mService.isDiscovering(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.isDiscovering(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1266,9 +1323,12 @@ public final class BluetoothAdapter { public boolean isMultipleAdvertisementSupported() { if (getState() != STATE_ON) return false; try { - return mService.isMultiAdvertisementSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isMultiAdvertisementSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1301,9 +1361,12 @@ public final class BluetoothAdapter { public boolean isPeripheralModeSupported() { if (getState() != STATE_ON) return false; try { - return mService.isPeripheralModeSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isPeripheralModeSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get peripheral mode capability: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1316,9 +1379,12 @@ public final class BluetoothAdapter { public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) return false; try { - return mService.isOffloadedFilteringSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1331,9 +1397,12 @@ public final class BluetoothAdapter { public boolean isOffloadedScanBatchingSupported() { if (!getLeAccess()) return false; try { - return mService.isOffloadedScanBatchingSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1399,15 +1468,15 @@ public final class BluetoothAdapter { */ public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { - synchronized(mManagerCallback) { - if (mService != null) { - mService.requestActivityInfo(result); - result = null; - } + mServiceLock.readLock().lock(); + if (mService != null) { + mService.requestActivityInfo(result); + result = null; } } catch (RemoteException e) { Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); } finally { + mServiceLock.readLock().unlock(); if (result != null) { // Only send an immediate result if we failed. result.send(0, null); @@ -1432,11 +1501,14 @@ public final class BluetoothAdapter { return toDeviceSet(new BluetoothDevice[0]); } try { - synchronized(mManagerCallback) { - if (mService != null) return toDeviceSet(mService.getBondedDevices()); - } + mServiceLock.readLock().lock(); + if (mService != null) return toDeviceSet(mService.getBondedDevices()); return toDeviceSet(new BluetoothDevice[0]); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return null; } @@ -1456,10 +1528,13 @@ public final class BluetoothAdapter { public int getConnectionState() { if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getAdapterConnectionState(); - } - } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getAdapterConnectionState(); + } catch (RemoteException e) { + Log.e(TAG, "getConnectionState:", e); + } finally { + mServiceLock.readLock().unlock(); + } return BluetoothAdapter.STATE_DISCONNECTED; } @@ -1482,11 +1557,12 @@ public final class BluetoothAdapter { public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getProfileConnectionState(profile); - } + mServiceLock.readLock().lock(); + if (mService != null) return mService.getProfileConnectionState(profile); } catch (RemoteException e) { Log.e(TAG, "getProfileConnectionState:", e); + } finally { + mServiceLock.readLock().unlock(); } return BluetoothProfile.STATE_DISCONNECTED; } @@ -1790,7 +1866,9 @@ public final class BluetoothAdapter { byte[] hash; byte[] randomizer; - byte[] ret = mService.readOutOfBandData(); + byte[] ret = null; + mServiceLock.readLock().lock(); + if (mService != null) mService.readOutOfBandData(); if (ret == null || ret.length != 32) return null; @@ -1803,7 +1881,12 @@ public final class BluetoothAdapter { } return new Pair(hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + */ return null; } @@ -1939,17 +2022,21 @@ public final class BluetoothAdapter { new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); - synchronized (mManagerCallback) { - mService = bluetoothService; - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceUp(bluetoothService); - } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + + mServiceLock.writeLock().lock(); + mService = bluetoothService; + mServiceLock.writeLock().unlock(); + + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) { + try { + if (cb != null) { + cb.onBluetoothServiceUp(bluetoothService); + } else { + Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + } + } catch (Exception e) { + Log.e(TAG,"",e); } } } @@ -1957,20 +2044,24 @@ public final class BluetoothAdapter { public void onBluetoothServiceDown() { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); - synchronized (mManagerCallback) { - mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + + mServiceLock.writeLock().lock(); + mService = null; + if (mLeScanClients != null) mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + mServiceLock.writeLock().unlock(); + + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + } + } catch (Exception e) { + Log.e(TAG,"",e); } } } @@ -2033,11 +2124,17 @@ public final class BluetoothAdapter { //TODO(BT) /* try { - return mService.changeApplicationBluetoothState(on, new + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.changeApplicationBluetoothState(on, new StateChangeCallbackWrapper(callback), new Binder()); + } } catch (RemoteException e) { Log.e(TAG, "changeBluetoothState", e); - }*/ + } finally { + mServiceLock.readLock().unlock(); + } + */ return false; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5e8687a904c..f30a126ba60 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -52,6 +52,7 @@ import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -126,6 +127,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; + private final ReentrantReadWriteLock mBluetoothLock = + new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; // used inside handler thread @@ -190,12 +193,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } int st = BluetoothAdapter.STATE_OFF; - if (mBluetooth != null) { - try { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { st = mBluetooth.getState(); - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call getState", e); } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + } finally { + mBluetoothLock.readLock().unlock(); } Slog.d(TAG, "state" + st); @@ -208,12 +214,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); mEnableExternal = false; } - } catch(RemoteException e) { + } catch (RemoteException e) { Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().lock(); } } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting @@ -366,9 +375,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); msg.obj = callback; mHandler.sendMessage(msg); - synchronized(mConnection) { - return mBluetooth; - } + + return mBluetooth; } public void unregisterAdapter(IBluetoothManagerCallback callback) { @@ -406,12 +414,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - synchronized(mConnection) { - try { - return (mBluetooth != null && mBluetooth.isEnabled()); - } catch (RemoteException e) { - Slog.e(TAG, "isEnabled()", e); - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.isEnabled(); + } catch (RemoteException e) { + Slog.e(TAG, "isEnabled()", e); + } finally { + mBluetoothLock.readLock().unlock(); } return false; } @@ -424,11 +433,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBleAppCount == 0) { if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); } - } catch(RemoteException e) { + } catch (RemoteException e) { Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } @@ -456,9 +468,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { disableBleScanMode(); clearBleApps(); try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) mBluetooth.onBrEdrDown(); } catch (RemoteException e) { Slog.e(TAG, "error when disabling bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } @@ -472,12 +487,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Disable ble scan only mode. private void disableBleScanMode() { try { + mBluetoothLock.writeLock().lock(); if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable"); mEnable = false; } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -536,7 +554,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void onBluetoothGattServiceUp() { if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up"); - try{ + try { + mBluetoothLock.readLock().lock(); if (isBleAppPresent() == false && mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { mBluetooth.onLeServiceUp(); @@ -546,8 +565,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); Binder.restoreCallingIdentity(callingIdentity); } - } catch(RemoteException e) { - Slog.e(TAG,"Unable to call onServiceUp", e); + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onServiceUp", e); + } finally { + mBluetoothLock.readLock().unlock(); } } @@ -558,22 +579,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void sendBrEdrDownCallback() { if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks"); - if(mBluetooth == null) { + if (mBluetooth == null) { Slog.w(TAG, "Bluetooth handle is null"); return; } if (isBleAppPresent() == false) { try { - mBluetooth.onBrEdrDown(); - } catch(RemoteException e) { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) mBluetooth.onBrEdrDown(); + } catch (RemoteException e) { Slog.e(TAG, "Call to onBrEdrDown() failed.", e); + } finally { + mBluetoothLock.readLock().unlock(); } } else { // Need to stay at BLE ON. Disconnect all Gatt connections - try{ + try { mBluetoothGatt.unregAll(); - } catch(RemoteException e) { + } catch (RemoteException e) { Slog.e(TAG, "Unable to disconnect all apps.", e); } } @@ -673,7 +697,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { " mBinding = " + mBinding); } - synchronized (mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (mUnbinding) return; mUnbinding = true; if (mBluetooth != null) { @@ -695,6 +720,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mUnbinding=false; } mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -1010,14 +1037,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return BluetoothAdapter.DEFAULT_MAC_ADDRESS; } - synchronized(mConnection) { - if (mBluetooth != null) { - try { - return mBluetooth.getAddress(); - } catch (RemoteException e) { - Slog.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e); - } - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.getAddress(); + } catch (RemoteException e) { + Slog.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e); + } finally { + mBluetoothLock.readLock().unlock(); } // mAddress is accessed from outside. @@ -1036,15 +1062,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return null; } - synchronized(mConnection) { - if (mBluetooth != null) { - try { - return mBluetooth.getName(); - } catch (RemoteException e) { - Slog.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e); - } - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.getName(); + } catch (RemoteException e) { + Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e); + } finally { + mBluetoothLock.readLock().unlock(); } + // mName is accessed from outside. // It alright without a lock. Here, bluetooth is off, no other thread is // changing mName @@ -1101,7 +1127,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { switch (msg.what) { case MESSAGE_GET_NAME_AND_ADDRESS: if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { if (DBG) Slog.d(TAG, "Binding to service to get name and address"); mGetNameAddressOnly = true; @@ -1127,6 +1154,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mGetNameAddressOnly = false; } + } finally { + mBluetoothLock.writeLock().unlock(); } break; @@ -1209,7 +1238,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); IBinder service = (IBinder) msg.obj; - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); onBluetoothGattServiceUp(); @@ -1264,6 +1294,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException e) { Slog.e(TAG,"Unable to call enable()",e); } + } finally { + mBluetoothLock.writeLock().unlock(); } if (!mEnable) { @@ -1275,9 +1307,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_TIMEOUT_BIND: { Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); - synchronized(mConnection) { - mBinding = false; - } + mBluetoothLock.writeLock().lock(); + mBinding = false; + mBluetoothLock.writeLock().unlock(); + break; } case MESSAGE_BLUETOOTH_STATE_CHANGE: @@ -1312,7 +1345,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTH) { // if service is unbinded already, do nothing and return if (mBluetooth == null) break; @@ -1324,6 +1358,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "Bad msg.arg1: " + msg.arg1); break; } + } finally { + mBluetoothLock.writeLock().unlock(); } if (mEnable) { @@ -1369,9 +1405,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_TIMEOUT_UNBIND: { Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); - synchronized(mConnection) { - mUnbinding = false; - } + mBluetoothLock.writeLock().lock(); + mUnbinding = false; + mBluetoothLock.writeLock().unlock(); break; } @@ -1381,15 +1417,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /* disable and enable BT when detect a user switch */ if (mEnable && mBluetooth != null) { - synchronized (mConnection) { + try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister",re); - } + mBluetooth.unregisterCallback(mBluetoothCallback); } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); } if (mState == BluetoothAdapter.STATE_TURNING_OFF) { @@ -1420,14 +1456,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); sendBluetoothServiceDownCallback(); - synchronized (mConnection) { - if (mBluetooth != null) { - mBluetooth = null; - //Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; + + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); } + mBluetoothGatt = null; + mBluetoothLock.writeLock().unlock(); + SystemClock.sleep(100); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); @@ -1450,14 +1488,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED"); mHandler.removeMessages(MESSAGE_USER_SWITCHED); - synchronized (mConnection) { - if (mEnable && !mBinding && (mBluetooth == null)) { - // We should be connected, but we gave up for some - // reason; maybe the Bluetooth service wasn't encryption - // aware, so try binding again. - if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock"); - handleEnable(mQuietEnable); - } + if (mEnable && !mBinding && (mBluetooth == null)) { + // We should be connected, but we gave up for some + // reason; maybe the Bluetooth service wasn't encryption + // aware, so try binding again. + if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock"); + handleEnable(mQuietEnable); } } } @@ -1467,7 +1503,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void handleEnable(boolean quietMode) { mQuietEnable = quietMode; - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { //Start bind timeout and bind Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); @@ -1496,6 +1533,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG,"Unable to call enable()",e); } } + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -1510,18 +1549,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void handleDisable() { - synchronized(mConnection) { + try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { if (DBG) Slog.d(TAG,"Sending off request."); - - try { - if(!mBluetooth.disable()) { - Slog.e(TAG,"IBluetooth.disable() returned false"); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call disable()",e); + if (!mBluetooth.disable()) { + Slog.e(TAG,"IBluetooth.disable() returned false"); } } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call disable()",e); + } finally { + mBluetoothLock.readLock().unlock(); } } @@ -1648,20 +1687,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean waitForOnOff(boolean on, boolean off) { int i = 0; while (i < 10) { - synchronized(mConnection) { - try { - if (mBluetooth == null) break; - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; - } - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - break; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) break; + if (on) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; + } else if (off) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; + } else { + if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; } + } catch (RemoteException e) { + Slog.e(TAG, "getState()", e); + break; + } finally { + mBluetoothLock.readLock().unlock(); } if (on || off) { SystemClock.sleep(300); @@ -1684,34 +1724,36 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean canUnbindBluetoothService() { - synchronized(mConnection) { + try { //Only unbind with mEnable flag not set //For race condition: disable and enable back-to-back //Avoid unbind right after enable due to callback from disable //Only unbind with Bluetooth at OFF state //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message - try { - if (mEnable || (mBluetooth == null)) return false; - if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; - return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - } + mBluetoothLock.readLock().lock(); + if (mEnable || (mBluetooth == null)) return false; + if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; + return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); + } catch (RemoteException e) { + Slog.e(TAG, "getState()", e); + } finally { + mBluetoothLock.readLock().unlock(); } return false; } private void recoverBluetoothServiceFromError() { Slog.e(TAG,"recoverBluetoothServiceFromError"); - synchronized (mConnection) { + try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister",re); - } + mBluetooth.unregisterCallback(mBluetoothCallback); } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); } SystemClock.sleep(500); @@ -1722,14 +1764,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { waitForOnOff(false, true); sendBluetoothServiceDownCallback(); - synchronized (mConnection) { - if (mBluetooth != null) { - mBluetooth = null; - //Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; + + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); } + mBluetoothGatt = null; + mBluetoothLock.writeLock().unlock(); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; -- GitLab From 08eec363400b9c305072d87e8b242397b7eba024 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sun, 22 May 2016 22:16:41 -0700 Subject: [PATCH 0572/1408] Reduced the impact of "synchronized" statements * Removed "synchronized" statements that are not needed * Replaced "synchronized" statements with Read/Write lock as appropriate. The lock protects the access to and the setting of BluetoothAdapter.mService and BluetoothManagerService.mBluetooth and associated state. Bug: 28734075 Bug: 28799467 Change-Id: I8f8281c505f0a1ae0add1e14a3caba1f5b2a98e4 (cherry picked from commit eb50a39e98acb78d16465041bb5c172aa1637e97) --- .../android/bluetooth/BluetoothAdapter.java | 367 +++++++++++------- .../bluetooth/BluetoothManagerService.java | 283 ++++++++------ 2 files changed, 400 insertions(+), 250 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ceb7e91bf72..15b16f73144 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -53,6 +53,8 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} @@ -487,6 +489,8 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; + private final ReentrantReadWriteLock mServiceLock = + new ReentrantReadWriteLock(); private final Object mLock = new Object(); private final Map mLeScanClients; @@ -521,8 +525,13 @@ public final class BluetoothAdapter { throw new IllegalArgumentException("bluetooth manager service is null"); } try { + mServiceLock.writeLock().lock(); mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.writeLock().unlock(); + } mManagerService = managerService; mLeScanClients = new HashMap(); mToken = new Binder(); @@ -609,10 +618,14 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { try { - synchronized(mManagerCallback) { - if (mService != null) return mService.isEnabled(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.isEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; } @@ -643,12 +656,12 @@ public final class BluetoothAdapter { * or OFF if BT is in BLE_ON state */ private void notifyUserAction(boolean enable) { - if (mService == null) { - Log.e(TAG, "mService is null"); - return; - } - try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.e(TAG, "mService is null"); + return; + } if (enable) { mService.onLeServiceUp(); //NA:TODO implementation pending } else { @@ -656,6 +669,8 @@ public final class BluetoothAdapter { } } catch (RemoteException e) { Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } } @@ -787,26 +802,28 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { + int state = BluetoothAdapter.STATE_OFF; + try { - synchronized(mManagerCallback) { - if (mService != null) - { - int state= mService.getState(); - if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); - //consider all internal states as OFF - if (state == BluetoothAdapter.STATE_BLE_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) Log.d(TAG, "Consider internal state as OFF"); - state = BluetoothAdapter.STATE_OFF; - } - return state; - } - // TODO(BT) there might be a small gap during STATE_TURNING_ON that - // mService is null, handle that case + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); } - } catch (RemoteException e) {Log.e(TAG, "", e);} - return STATE_OFF; + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + // Consider all internal states as OFF + if (state == BluetoothAdapter.STATE_BLE_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { + if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + state = BluetoothAdapter.STATE_OFF; + } + if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + return state; } /** @@ -829,19 +846,21 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getLeState() { + int state = BluetoothAdapter.STATE_OFF; + try { - synchronized(mManagerCallback) { - if (mService != null) - { - int state= mService.getState(); - if (VDBG) Log.d(TAG,"getLeState() returning " + state); - return state; - } + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); } } catch (RemoteException e) { Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } - return BluetoothAdapter.STATE_OFF; + + if (VDBG) Log.d(TAG,"getLeState() returning " + state); + return state; } boolean getLeAccess() { @@ -883,16 +902,21 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { - int state = STATE_OFF; - if (isEnabled() == true){ + int state = BluetoothAdapter.STATE_OFF; + if (isEnabled() == true) { if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } - //Use service interface to get the exact state - if (mService != null) { - try { - state = mService.getState(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + // Use service interface to get the exact state + try { + mServiceLock.readLock().lock(); + if (mService != null) { + state = mService.getState(); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } if (state == BluetoothAdapter.STATE_BLE_ON) { @@ -997,10 +1021,13 @@ public final class BluetoothAdapter { */ public boolean configHciSnoopLog(boolean enable) { try { - synchronized(mManagerCallback) { - if (mService != null) return mService.configHciSnoopLog(enable); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.configHciSnoopLog(enable); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1016,12 +1043,16 @@ public final class BluetoothAdapter { */ public boolean factoryReset() { try { + mServiceLock.readLock().lock(); if (mService != null) { return mService.factoryReset(); - } else { - SystemProperties.set("persist.bluetooth.factoryreset", "true"); } - } catch (RemoteException e) {Log.e(TAG, "", e);} + SystemProperties.set("persist.bluetooth.factoryreset", "true"); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1036,10 +1067,13 @@ public final class BluetoothAdapter { public ParcelUuid[] getUuids() { if (getState() != STATE_ON) return null; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getUuids(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getUuids(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return null; } @@ -1062,10 +1096,13 @@ public final class BluetoothAdapter { public boolean setName(String name) { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.setName(name); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.setName(name); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1090,10 +1127,13 @@ public final class BluetoothAdapter { public int getScanMode() { if (getState() != STATE_ON) return SCAN_MODE_NONE; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getScanMode(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getScanMode(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return SCAN_MODE_NONE; } @@ -1128,10 +1168,13 @@ public final class BluetoothAdapter { public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.setScanMode(mode, duration); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.setScanMode(mode, duration); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1146,10 +1189,13 @@ public final class BluetoothAdapter { public int getDiscoverableTimeout() { if (getState() != STATE_ON) return -1; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getDiscoverableTimeout(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getDiscoverableTimeout(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return -1; } @@ -1157,10 +1203,13 @@ public final class BluetoothAdapter { public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) return; try { - synchronized(mManagerCallback) { - if (mService != null) mService.setDiscoverableTimeout(timeout); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) mService.setDiscoverableTimeout(timeout); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } } /** @@ -1197,10 +1246,13 @@ public final class BluetoothAdapter { public boolean startDiscovery() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.startDiscovery(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.startDiscovery(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1225,10 +1277,13 @@ public final class BluetoothAdapter { public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.cancelDiscovery(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.cancelDiscovery(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1255,10 +1310,13 @@ public final class BluetoothAdapter { public boolean isDiscovering() { if (getState() != STATE_ON) return false; try { - synchronized(mManagerCallback) { - if (mService != null ) return mService.isDiscovering(); - } - } catch (RemoteException e) {Log.e(TAG, "", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.isDiscovering(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return false; } @@ -1270,9 +1328,12 @@ public final class BluetoothAdapter { public boolean isMultipleAdvertisementSupported() { if (getState() != STATE_ON) return false; try { - return mService.isMultiAdvertisementSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isMultiAdvertisementSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1305,9 +1366,12 @@ public final class BluetoothAdapter { public boolean isPeripheralModeSupported() { if (getState() != STATE_ON) return false; try { - return mService.isPeripheralModeSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isPeripheralModeSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get peripheral mode capability: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1320,9 +1384,12 @@ public final class BluetoothAdapter { public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) return false; try { - return mService.isOffloadedFilteringSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1335,9 +1402,12 @@ public final class BluetoothAdapter { public boolean isOffloadedScanBatchingSupported() { if (!getLeAccess()) return false; try { - return mService.isOffloadedScanBatchingSupported(); + mServiceLock.readLock().lock(); + if (mService != null) return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); } return false; } @@ -1376,9 +1446,12 @@ public final class BluetoothAdapter { public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { if (getState() != STATE_ON) return null; try { + mServiceLock.readLock().lock(); BluetoothActivityEnergyInfo record; - if (!mService.isActivityAndEnergyReportingSupported()) { - return null; + if (mService != null) { + if (!mService.isActivityAndEnergyReportingSupported()) { + return null; + } } synchronized(this) { if (updateType == ACTIVITY_ENERGY_INFO_REFRESHED) { @@ -1396,6 +1469,8 @@ public final class BluetoothAdapter { Log.e(TAG, "getControllerActivityEnergyInfoCallback wait interrupted: " + e); } catch (RemoteException e) { Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); + } finally { + mServiceLock.readLock().unlock(); } return null; } @@ -1417,11 +1492,14 @@ public final class BluetoothAdapter { return toDeviceSet(new BluetoothDevice[0]); } try { - synchronized(mManagerCallback) { - if (mService != null) return toDeviceSet(mService.getBondedDevices()); - } + mServiceLock.readLock().lock(); + if (mService != null) return toDeviceSet(mService.getBondedDevices()); return toDeviceSet(new BluetoothDevice[0]); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } return null; } @@ -1441,10 +1519,13 @@ public final class BluetoothAdapter { public int getConnectionState() { if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getAdapterConnectionState(); - } - } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);} + mServiceLock.readLock().lock(); + if (mService != null) return mService.getAdapterConnectionState(); + } catch (RemoteException e) { + Log.e(TAG, "getConnectionState:", e); + } finally { + mServiceLock.readLock().unlock(); + } return BluetoothAdapter.STATE_DISCONNECTED; } @@ -1467,11 +1548,12 @@ public final class BluetoothAdapter { public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { - synchronized(mManagerCallback) { - if (mService != null) return mService.getProfileConnectionState(profile); - } + mServiceLock.readLock().lock(); + if (mService != null) return mService.getProfileConnectionState(profile); } catch (RemoteException e) { Log.e(TAG, "getProfileConnectionState:", e); + } finally { + mServiceLock.readLock().unlock(); } return BluetoothProfile.STATE_DISCONNECTED; } @@ -1775,7 +1857,9 @@ public final class BluetoothAdapter { byte[] hash; byte[] randomizer; - byte[] ret = mService.readOutOfBandData(); + byte[] ret = null; + mServiceLock.readLock().lock(); + if (mService != null) mService.readOutOfBandData(); if (ret == null || ret.length != 32) return null; @@ -1788,7 +1872,12 @@ public final class BluetoothAdapter { } return new Pair(hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + */ return null; } @@ -1917,17 +2006,21 @@ public final class BluetoothAdapter { new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); - synchronized (mManagerCallback) { - mService = bluetoothService; - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceUp(bluetoothService); - } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + + mServiceLock.writeLock().lock(); + mService = bluetoothService; + mServiceLock.writeLock().unlock(); + + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) { + try { + if (cb != null) { + cb.onBluetoothServiceUp(bluetoothService); + } else { + Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + } + } catch (Exception e) { + Log.e(TAG,"",e); } } } @@ -1935,20 +2028,24 @@ public final class BluetoothAdapter { public void onBluetoothServiceDown() { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); - synchronized (mManagerCallback) { - mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); - } - } catch (Exception e) { Log.e(TAG,"",e);} + + mServiceLock.writeLock().lock(); + mService = null; + if (mLeScanClients != null) mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + mServiceLock.writeLock().unlock(); + + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + } + } catch (Exception e) { + Log.e(TAG,"",e); } } } @@ -2011,11 +2108,17 @@ public final class BluetoothAdapter { //TODO(BT) /* try { - return mService.changeApplicationBluetoothState(on, new + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.changeApplicationBluetoothState(on, new StateChangeCallbackWrapper(callback), new Binder()); + } } catch (RemoteException e) { Log.e(TAG, "changeBluetoothState", e); - }*/ + } finally { + mServiceLock.readLock().unlock(); + } + */ return false; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1e91a2c3a28..e97f6a3e9c3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -54,6 +54,7 @@ import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.IOException; @@ -129,6 +130,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; + private final ReentrantReadWriteLock mBluetoothLock = + new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; // used inside handler thread @@ -193,12 +196,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } int st = BluetoothAdapter.STATE_OFF; - if (mBluetooth != null) { - try { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { st = mBluetooth.getState(); - } catch (RemoteException e) { - Log.e(TAG,"Unable to call getState", e); } + } catch (RemoteException e) { + Log.e(TAG, "Unable to call getState", e); + } finally { + mBluetoothLock.readLock().unlock(); } Log.d(TAG, "state" + st); @@ -211,12 +217,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); mEnableExternal = false; } } catch(RemoteException e) { Log.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().lock(); } } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting @@ -369,9 +378,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); msg.obj = callback; mHandler.sendMessage(msg); - synchronized(mConnection) { - return mBluetooth; - } + + return mBluetooth; } public void unregisterAdapter(IBluetoothManagerCallback callback) { @@ -409,12 +417,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - synchronized(mConnection) { - try { - return (mBluetooth != null && mBluetooth.isEnabled()); - } catch (RemoteException e) { - Log.e(TAG, "isEnabled()", e); - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.isEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "isEnabled()", e); + } finally { + mBluetoothLock.readLock().unlock(); } return false; } @@ -427,11 +436,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBleAppCount == 0) { if (DBG) Log.d(TAG, "Disabling LE only mode after application crash"); try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); } } catch(RemoteException e) { Log.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } @@ -459,9 +471,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { disableBleScanMode(); clearBleApps(); try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) mBluetooth.onBrEdrDown(); } catch (RemoteException e) { Log.e(TAG, "error when disabling bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } @@ -475,12 +490,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Disable ble scan only mode. private void disableBleScanMode() { try { + mBluetoothLock.writeLock().lock(); if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable"); mEnable = false; } } catch (RemoteException e) { Log.e(TAG, "getState()", e); + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -539,7 +557,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void onBluetoothGattServiceUp() { if (DBG) Log.d(TAG,"BluetoothGatt Service is Up"); - try{ + try { + mBluetoothLock.readLock().lock(); if (isBleAppPresent() == false && mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { mBluetooth.onLeServiceUp(); @@ -549,8 +568,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); Binder.restoreCallingIdentity(callingIdentity); } - } catch(RemoteException e) { - Log.e(TAG,"Unable to call onServiceUp", e); + } catch (RemoteException e) { + Log.e(TAG,"Unable to call onServiceUp", e); + } finally { + mBluetoothLock.readLock().unlock(); } } @@ -561,22 +582,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void sendBrEdrDownCallback() { if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks"); - if(mBluetooth == null) { + if (mBluetooth == null) { Log.w(TAG, "Bluetooth handle is null"); return; } if (isBleAppPresent() == false) { try { - mBluetooth.onBrEdrDown(); - } catch(RemoteException e) { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) mBluetooth.onBrEdrDown(); + } catch (RemoteException e) { Log.e(TAG, "Call to onBrEdrDown() failed.", e); + } finally { + mBluetoothLock.readLock().unlock(); } } else { // Need to stay at BLE ON. Disconnect all Gatt connections - try{ + try { mBluetoothGatt.unregAll(); - } catch(RemoteException e) { + } catch (RemoteException e) { Log.e(TAG, "Unable to disconnect all apps.", e); } } @@ -672,7 +696,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { " mBinding = " + mBinding); } - synchronized (mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (mUnbinding) return; mUnbinding = true; if (mBluetooth != null) { @@ -695,6 +720,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mUnbinding=false; } mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -1007,15 +1034,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return BluetoothAdapter.DEFAULT_MAC_ADDRESS; } - synchronized(mConnection) { - if (mBluetooth != null) { - try { - return mBluetooth.getAddress(); - } catch (RemoteException e) { - Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e); - } - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.getAddress(); + } catch (RemoteException e) { + Log.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e); + } finally { + mBluetoothLock.readLock().unlock(); } + // mAddress is accessed from outside. // It is alright without a lock. Here, bluetooth is off, no other thread is // changing mAddress @@ -1032,15 +1059,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return null; } - synchronized(mConnection) { - if (mBluetooth != null) { - try { - return mBluetooth.getName(); - } catch (RemoteException e) { - Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e); - } - } + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.getName(); + } catch (RemoteException e) { + Log.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e); + } finally { + mBluetoothLock.readLock().unlock(); } + // mName is accessed from outside. // It alright without a lock. Here, bluetooth is off, no other thread is // changing mName @@ -1104,10 +1131,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void handleMessage(Message msg) { if (DBG) Log.d (TAG, "Message: " + msg.what); switch (msg.what) { - case MESSAGE_GET_NAME_AND_ADDRESS: { - if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS"); - synchronized(mConnection) { - //Start bind request + case MESSAGE_GET_NAME_AND_ADDRESS: + if (DBG) Log.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); + try { + mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { if (DBG) Log.d(TAG, "Binding to service to get name and address"); mConnection.setGetNameAddressOnly(true); @@ -1134,23 +1161,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS); } } + } finally { + mBluetoothLock.writeLock().unlock(); } break; - } + case MESSAGE_SAVE_NAME_AND_ADDRESS: { boolean unbind = false; if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS"); - synchronized(mConnection) { + try { + mBluetoothLock.readLock().lock(); if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) { - try { - mBluetooth.enable(); - } catch (RemoteException e) { - Log.e(TAG,"Unable to call enable()",e); - } + mBluetooth.enable(); } + } catch (RemoteException e) { + Log.e(TAG,"Unable to call enable()",e); + } finally { + mBluetoothLock.readLock().unlock(); } + if (mBluetooth != null && !mConnection.isGetNameAddressOnly()) waitForOnOff(true, false); - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (mBluetooth != null) { String name = null; String address = null; @@ -1193,6 +1225,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } + } finally { + mBluetoothLock.writeLock().unlock(); } if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) { waitForOnOff(false, true); @@ -1281,7 +1315,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); IBinder service = (IBinder) msg.obj; - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); onBluetoothGattServiceUp(); @@ -1338,6 +1373,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException e) { Log.e(TAG,"Unable to call enable()",e); } + } finally { + mBluetoothLock.writeLock().unlock(); } if (!mEnable) { @@ -1349,9 +1386,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_TIMEOUT_BIND: { Log.e(TAG, "MESSAGE_TIMEOUT_BIND"); - synchronized(mConnection) { - mBinding = false; - } + mBluetoothLock.writeLock().lock(); + mBinding = false; + mBluetoothLock.writeLock().unlock(); break; } case MESSAGE_BLUETOOTH_STATE_CHANGE: @@ -1386,7 +1423,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTH) { // if service is unbinded already, do nothing and return if (mBluetooth == null) break; @@ -1398,6 +1436,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Log.e(TAG, "Bad msg.arg1: " + msg.arg1); break; } + } finally { + mBluetoothLock.writeLock().unlock(); } if (mEnable) { @@ -1445,9 +1485,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_TIMEOUT_UNBIND: { Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); - synchronized(mConnection) { - mUnbinding = false; - } + mBluetoothLock.writeLock().lock(); + mUnbinding = false; + mBluetoothLock.writeLock().unlock(); break; } @@ -1459,15 +1499,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_USER_SWITCHED); /* disable and enable BT when detect a user switch */ if (mEnable && mBluetooth != null) { - synchronized (mConnection) { + try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister",re); - } + mBluetooth.unregisterCallback(mBluetoothCallback); } + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); } if (mState == BluetoothAdapter.STATE_TURNING_OFF) { @@ -1498,14 +1538,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); sendBluetoothServiceDownCallback(); - synchronized (mConnection) { - if (mBluetooth != null) { - mBluetooth = null; - //Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; + + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); } + mBluetoothGatt = null; + mBluetoothLock.writeLock().unlock(); + SystemClock.sleep(100); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); @@ -1531,7 +1573,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void handleEnable(boolean quietMode) { mQuietEnable = quietMode; - synchronized(mConnection) { + try { + mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { //Start bind timeout and bind Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); @@ -1576,6 +1619,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Log.e(TAG,"Unable to call enable()",e); } } + } finally { + mBluetoothLock.writeLock().unlock(); } } @@ -1590,20 +1635,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void handleDisable() { - synchronized(mConnection) { - // don't need to disable if GetNameAddressOnly is set, - // service will be unbinded after Name and Address are saved - if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { if (DBG) Log.d(TAG,"Sending off request."); - - try { - if(!mBluetooth.disable()) { - Log.e(TAG,"IBluetooth.disable() returned false"); - } - } catch (RemoteException e) { - Log.e(TAG,"Unable to call disable()",e); + if (!mBluetooth.disable()) { + Log.e(TAG,"IBluetooth.disable() returned false"); } } + } catch (RemoteException e) { + Log.e(TAG,"Unable to call disable()",e); + } finally { + mBluetoothLock.readLock().unlock(); } } @@ -1730,20 +1773,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean waitForOnOff(boolean on, boolean off) { int i = 0; while (i < 10) { - synchronized(mConnection) { - try { - if (mBluetooth == null) break; - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; - } - } catch (RemoteException e) { - Log.e(TAG, "getState()", e); - break; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) break; + if (on) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; + } else if (off) { + if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; + } else { + if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; } + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + break; + } finally { + mBluetoothLock.readLock().unlock(); } if (on || off) { SystemClock.sleep(300); @@ -1766,34 +1810,36 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean canUnbindBluetoothService() { - synchronized(mConnection) { + try { //Only unbind with mEnable flag not set //For race condition: disable and enable back-to-back //Avoid unbind right after enable due to callback from disable //Only unbind with Bluetooth at OFF state //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message - try { - if (mEnable || (mBluetooth == null)) return false; - if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; - return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); - } catch (RemoteException e) { - Log.e(TAG, "getState()", e); - } + mBluetoothLock.readLock().lock(); + if (mEnable || (mBluetooth == null)) return false; + if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; + return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); + } catch (RemoteException e) { + Log.e(TAG, "getState()", e); + } finally { + mBluetoothLock.readLock().unlock(); } return false; } private void recoverBluetoothServiceFromError() { Log.e(TAG,"recoverBluetoothServiceFromError"); - synchronized (mConnection) { + try { + mBluetoothLock.readLock().lock(); if (mBluetooth != null) { //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister",re); - } + mBluetooth.unregisterCallback(mBluetoothCallback); } + } catch (RemoteException re) { + Log.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); } SystemClock.sleep(500); @@ -1804,14 +1850,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { waitForOnOff(false, true); sendBluetoothServiceDownCallback(); - synchronized (mConnection) { - if (mBluetooth != null) { - mBluetooth = null; - //Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; + + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); } + mBluetoothGatt = null; + mBluetoothLock.writeLock().unlock(); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; -- GitLab From 0f8b2fda07e04ecd2f5c87e56fd1be297417b9d8 Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Tue, 24 May 2016 15:22:55 -0700 Subject: [PATCH 0573/1408] Add missing @Deprecated annotations. Add missing @Deprecated annotations for methods with @deprecated tag in javadoc. Change-Id: I35b78ccb97832d86b914c21a300444ba29e33844 --- framework/java/android/bluetooth/BluetoothGatt.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 800dd434a96..5419143ff42 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1045,6 +1045,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * @deprecated Use {@link #abortReliableWrite()} */ + @Deprecated public void abortReliableWrite(BluetoothDevice mDevice) { abortReliableWrite(); } -- GitLab From 630c0a3a0748855515a6544da3380c12f83a619e Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Tue, 24 May 2016 15:28:41 -0700 Subject: [PATCH 0574/1408] Add missing "try ... finally" safeguards Safeguards for code protected by ReentrantReadWriteLock. Bug: 28734075 Bug: 28799467 Change-Id: Ib7f598a92e8df6bd855ca48cdd094c1c73a935f2 --- .../android/bluetooth/BluetoothAdapter.java | 15 ++++---- .../bluetooth/BluetoothManagerService.java | 34 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4c8657810c7..9390bcd13de 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2045,12 +2045,15 @@ public final class BluetoothAdapter { public void onBluetoothServiceDown() { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); - mServiceLock.writeLock().lock(); - mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - mServiceLock.writeLock().unlock(); + try { + mServiceLock.writeLock().lock(); + mService = null; + if (mLeScanClients != null) mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + } finally { + mServiceLock.writeLock().unlock(); + } synchronized (mProxyServiceStateCallbacks) { for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f30a126ba60..1f88be55ec6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1457,14 +1457,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { BluetoothAdapter.STATE_OFF); sendBluetoothServiceDownCallback(); - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } - mBluetoothGatt = null; - mBluetoothLock.writeLock().unlock(); SystemClock.sleep(100); @@ -1765,14 +1768,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothServiceDownCallback(); - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } - mBluetoothGatt = null; - mBluetoothLock.writeLock().unlock(); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; -- GitLab From f453e34eac30e6587d0764a57ddf98f9ced69c25 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Tue, 24 May 2016 15:28:41 -0700 Subject: [PATCH 0575/1408] Add missing "try ... finally" safeguards Safeguards for code protected by ReentrantReadWriteLock. Bug: 28734075 Bug: 28799467 Change-Id: Ib7f598a92e8df6bd855ca48cdd094c1c73a935f2 (cherry picked from commit e957a8a0b4100d001f79c866e7904d2426ac8da0) --- .../android/bluetooth/BluetoothAdapter.java | 15 ++++---- .../bluetooth/BluetoothManagerService.java | 34 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 15b16f73144..36e041ae0cb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2029,12 +2029,15 @@ public final class BluetoothAdapter { public void onBluetoothServiceDown() { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); - mServiceLock.writeLock().lock(); - mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - mServiceLock.writeLock().unlock(); + try { + mServiceLock.writeLock().lock(); + mService = null; + if (mLeScanClients != null) mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + } finally { + mServiceLock.writeLock().unlock(); + } synchronized (mProxyServiceStateCallbacks) { for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index e97f6a3e9c3..60005685cf9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1539,14 +1539,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { BluetoothAdapter.STATE_OFF); sendBluetoothServiceDownCallback(); - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } - mBluetoothGatt = null; - mBluetoothLock.writeLock().unlock(); SystemClock.sleep(100); @@ -1851,14 +1854,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothServiceDownCallback(); - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } - mBluetoothGatt = null; - mBluetoothLock.writeLock().unlock(); mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; -- GitLab From 5c096a71f709e8b4543f94967d95e744ef9bd85a Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Wed, 25 May 2016 10:34:17 -0700 Subject: [PATCH 0576/1408] Clean up Bluetooth test code. Change-Id: I502b03df842c628c7b504e2a32a17bce6d25b4f8 --- .../bluetooth/BluetoothStressTest.java | 200 ++++++++---------- .../android/bluetooth/BluetoothTestUtils.java | 58 +---- 2 files changed, 94 insertions(+), 164 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java index 755e7c4504c..31ce95eea1d 100644 --- a/framework/tests/src/android/bluetooth/BluetoothStressTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -35,6 +35,7 @@ public class BluetoothStressTest extends InstrumentationTestCase { /** The amount of time to sleep between issuing start/stop SCO in ms. */ private static final long SCO_SLEEP_TIME = 2 * 1000; + private BluetoothAdapter mAdapter; private BluetoothTestUtils mTestUtils; @Override @@ -42,13 +43,18 @@ public class BluetoothStressTest extends InstrumentationTestCase { super.setUp(); Context context = getInstrumentation().getTargetContext(); + mAdapter = BluetoothAdapter.getDefaultAdapter(); mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); + + // Start all tests in a disabled state. + if (mAdapter.isEnabled()) { + mTestUtils.disable(mAdapter); + } } @Override protected void tearDown() throws Exception { super.tearDown(); - mTestUtils.close(); } @@ -61,13 +67,10 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils.disable(adapter); - for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations); - mTestUtils.enable(adapter); - mTestUtils.disable(adapter); + mTestUtils.enable(mAdapter); + mTestUtils.disable(mAdapter); } } @@ -80,18 +83,14 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.undiscoverable(adapter); + mTestUtils.enable(mAdapter); + mTestUtils.undiscoverable(mAdapter); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations); - mTestUtils.discoverable(adapter); - mTestUtils.undiscoverable(adapter); + mTestUtils.discoverable(mAdapter); + mTestUtils.undiscoverable(mAdapter); } - - mTestUtils.disable(adapter); } /** @@ -103,18 +102,14 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.stopScan(adapter); + mTestUtils.enable(mAdapter); + mTestUtils.stopScan(mAdapter); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations); - mTestUtils.startScan(adapter); - mTestUtils.stopScan(adapter); + mTestUtils.startScan(mAdapter); + mTestUtils.stopScan(mAdapter); } - - mTestUtils.disable(adapter); } /** @@ -125,19 +120,16 @@ public class BluetoothStressTest extends InstrumentationTestCase { if (iterations == 0) { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.disablePan(adapter); + + mTestUtils.enable(mAdapter); + mTestUtils.disablePan(mAdapter); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of " + iterations); - mTestUtils.enablePan(adapter); - mTestUtils.disablePan(adapter); + mTestUtils.enablePan(mAdapter); + mTestUtils.disablePan(mAdapter); } - - mTestUtils.disable(adapter); } /** @@ -152,19 +144,16 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.unpair(adapter, device); + mTestUtils.unpair(mAdapter, device); } - mTestUtils.disable(adapter); } /** @@ -178,19 +167,16 @@ public class BluetoothStressTest extends InstrumentationTestCase { if (iterations == 0) { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations); - mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.unpair(adapter, device); + mTestUtils.unpair(mAdapter, device); } - mTestUtils.disable(adapter); } /** @@ -205,25 +191,22 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.A2DP, String.format("connectA2dp(device=%s)", device)); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, String.format("disconnectA2dp(device=%s)", device)); } - mTestUtils.unpair(adapter, device); - mTestUtils.disable(adapter); + mTestUtils.unpair(mAdapter, device); } /** @@ -238,25 +221,22 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, String.format("connectHeadset(device=%s)", device)); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, String.format("disconnectHeadset(device=%s)", device)); } - mTestUtils.unpair(adapter, device); - mTestUtils.disable(adapter); + mTestUtils.unpair(mAdapter, device); } /** @@ -271,25 +251,22 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, String.format("connectInput(device=%s)", device)); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, String.format("disconnectInput(device=%s)", device)); } - mTestUtils.unpair(adapter, device); - mTestUtils.disable(adapter); + mTestUtils.unpair(mAdapter, device); } /** @@ -304,22 +281,19 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectPan(adapter, device); - mTestUtils.disconnectPan(adapter, device); + mTestUtils.connectPan(mAdapter, device); + mTestUtils.disconnectPan(mAdapter, device); } - mTestUtils.unpair(adapter, device); - mTestUtils.disable(adapter); + mTestUtils.unpair(mAdapter, device); } /** @@ -334,26 +308,23 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.disablePan(adapter); - mTestUtils.enablePan(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.disablePan(mAdapter); + mTestUtils.enablePan(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of " + iterations); - mTestUtils.incomingPanConnection(adapter, device); - mTestUtils.incomingPanDisconnection(adapter, device); + mTestUtils.incomingPanConnection(mAdapter, device); + mTestUtils.incomingPanDisconnection(mAdapter, device); } - mTestUtils.unpair(adapter, device); - mTestUtils.disablePan(adapter); - mTestUtils.disable(adapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.disablePan(mAdapter); } /** @@ -368,28 +339,25 @@ public class BluetoothStressTest extends InstrumentationTestCase { return; } - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - mTestUtils.unpair(adapter, device); - mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey, + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.unpair(mAdapter, device); + mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.stopSco(adapter, device); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.stopSco(mAdapter, device); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations); - mTestUtils.startSco(adapter, device); + mTestUtils.startSco(mAdapter, device); sleep(SCO_SLEEP_TIME); - mTestUtils.stopSco(adapter, device); + mTestUtils.stopSco(mAdapter, device); sleep(SCO_SLEEP_TIME); } - mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.unpair(adapter, device); - mTestUtils.disable(adapter); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); + mTestUtils.unpair(mAdapter, device); } private void sleep(long time) { diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 0d9980ad205..08151729b9f 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -425,33 +425,14 @@ public class BluetoothTestUtils extends Assert { public void enable(BluetoothAdapter adapter) { int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG); - long start = -1; + long start = System.currentTimeMillis(); BluetoothReceiver receiver = getBluetoothReceiver(mask); - int state = adapter.getState(); - switch (state) { - case BluetoothAdapter.STATE_ON: - assertTrue(adapter.isEnabled()); - removeReceiver(receiver); - return; - case BluetoothAdapter.STATE_TURNING_ON: - assertFalse(adapter.isEnabled()); - mask = 0; // Don't check for received intents since we might have missed them. - break; - case BluetoothAdapter.STATE_OFF: - assertFalse(adapter.isEnabled()); - start = System.currentTimeMillis(); - assertTrue(adapter.enable()); - break; - case BluetoothAdapter.STATE_TURNING_OFF: - start = System.currentTimeMillis(); - assertTrue(adapter.enable()); - break; - default: - removeReceiver(receiver); - fail(String.format("enable() invalid state: state=%d", state)); - } + writeOutput("Enabling Bluetooth adapter."); + assertFalse(adapter.isEnabled()); + assertTrue(adapter.enable()); + int state = BluetoothAdapter.STATE_OFF; long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { state = adapter.getState(); @@ -485,33 +466,14 @@ public class BluetoothTestUtils extends Assert { public void disable(BluetoothAdapter adapter) { int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG | BluetoothReceiver.SCAN_MODE_NONE_FLAG); - long start = -1; + long start = System.currentTimeMillis(); BluetoothReceiver receiver = getBluetoothReceiver(mask); - int state = adapter.getState(); - switch (state) { - case BluetoothAdapter.STATE_OFF: - assertFalse(adapter.isEnabled()); - removeReceiver(receiver); - return; - case BluetoothAdapter.STATE_TURNING_ON: - assertFalse(adapter.isEnabled()); - start = System.currentTimeMillis(); - break; - case BluetoothAdapter.STATE_ON: - assertTrue(adapter.isEnabled()); - start = System.currentTimeMillis(); - assertTrue(adapter.disable()); - break; - case BluetoothAdapter.STATE_TURNING_OFF: - assertFalse(adapter.isEnabled()); - mask = 0; // Don't check for received intents since we might have missed them. - break; - default: - removeReceiver(receiver); - fail(String.format("disable() invalid state: state=%d", state)); - } + writeOutput("Disabling Bluetooth adapter."); + assertTrue(adapter.isEnabled()); + assertTrue(adapter.disable()); + int state = BluetoothAdapter.STATE_OFF; long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { state = adapter.getState(); -- GitLab From 302a1abcdc87fe9b0f70137c8dd27c47d16cd975 Mon Sep 17 00:00:00 2001 From: Christine Hallstrom Date: Wed, 25 May 2016 15:49:08 -0700 Subject: [PATCH 0577/1408] Properly check if BT is off when shutting down radios Use BluetoothAdapter#getState() instead of BluetoothAdapter#isEnabled() when checking if Bluetooth has turned off, as isEnabled() is set to false early in the shutdown process and getState() provides a better indication of the state of BT. Bug: 27354612 Change-Id: Ic7828f0726491d49c9a14ba5d654b24f66743662 --- .../android/bluetooth/IBluetoothManager.aidl | 1 + .../bluetooth/BluetoothManagerService.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 0b81ee8c547..2b853a373b5 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -37,6 +37,7 @@ interface IBluetoothManager boolean enable(); boolean enableNoAutoConnect(); boolean disable(boolean persist); + int getState(); IBluetoothGatt getBluetoothGatt(); boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1f88be55ec6..831ce01daca 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -425,6 +425,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + public int getState() { + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && + (!checkIfCallerIsForegroundUser())) { + Slog.w(TAG, "getState(): not allowed for non-active and non system user"); + return BluetoothAdapter.STATE_OFF; + } + + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) return mBluetooth.getState(); + } catch (RemoteException e) { + Slog.e(TAG, "getState()", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + return BluetoothAdapter.STATE_OFF; + } + class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); -- GitLab From 3246de65dc16fe8882b1f1152a48964e22b109d7 Mon Sep 17 00:00:00 2001 From: Mudumba Ananth Date: Mon, 29 Feb 2016 02:14:36 -0800 Subject: [PATCH 0578/1408] HFP 1.7 profile update (3/4) -> Android Framework changes to add support for an API to send indicator change in AG. -> Added a system intent for broadcasting assigned number(ID) of the supported HF indicators and their values (if received) Bug: 19983867 Change-Id: If26a7ae5da5686da72ebca9ec3decfe086e2ffb6 --- .../android/bluetooth/BluetoothHeadset.java | 63 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 2 files changed, 64 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 09a15de8778..f46a3b3c733 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -220,6 +220,46 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ + + /** + * Intent used to broadcast the headset's indicator status + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by + the headset ( as indicated by AT+BIND + command in the SLC sequence).or whose value + is changed (indicated by AT+BIEV command)
        • + *
        • {@link #EXTRA_IND_VALUE}- The updated value of headset indicator.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + *

        {@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are + * given an assigned number. Below shows the assigned number of Indicator added so far + * - Enhanced Safety - 1 + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * @hide + */ + public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = + "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; + + /** + * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG) + * that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_ID = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; + + /** + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the value of the Headset indicator that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_VALUE = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; + public static final int STATE_AUDIO_CONNECTED = 12; private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; @@ -969,6 +1009,29 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Send Headset the BIND response from AG to report change in the status of the + * HF indicators to the headset + * + * @param ind_id Assigned Number of the indicator (defined by SIG) + * @param ind_status + * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator + * true-Indicator is enabled, value changes may be sent for this indicator + * @hide + */ + public void bindResponse(int ind_id, boolean ind_status) { + if (mService != null && isEnabled()) { + try { + mService.bindResponse(ind_id, ind_status); + } 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())); + } + } + private final IBluetoothProfileServiceConnection mConnection = new IBluetoothProfileServiceConnection.Stub() { @Override diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 0bb4088f62c..6ad442b6138 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -59,4 +59,5 @@ interface IBluetoothHeadset { String number, int type); boolean enableWBS(); boolean disableWBS(); + void bindResponse(int ind_id, boolean ind_status); } -- GitLab From a4201975ca287ca4d3640a3800e6d379a8f4f80a Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 1 Jun 2016 22:25:18 -0700 Subject: [PATCH 0579/1408] Delay handleEnable() until Bluetooth state is OFF During shutdown of the Bluetooth stack, if a request to enable Bluetooth is received, the request is processed immediately. As a result, we don't let the Bluetooth service to shutdown cleanly, and the Bluetooth process with the native backend doesn't go away. This creates various issues in the native backend stack. Now, after an enable request is received, if necessary we delay acting on it, until the Bluetooth state is OFF. Bug: 17723234 Change-Id: I55db4bbd4f45227aa49eae996dcc328f76557a9b --- .../bluetooth/BluetoothManagerService.java | 72 ++++++++----------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1f88be55ec6..3eadb5ca731 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -639,19 +639,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); } - // We do not honor ON requests when the adapter is already turned ON or in the process of - // turning ON. - // As a protective mechanism to make sure that the native stack gets cleaned up properly - // before turning it back ON we ignore requests while the bluetooth is turning OFF. - // Bug: b/28318203 - if (mState == BluetoothAdapter.STATE_BLE_TURNING_OFF || - mState == BluetoothAdapter.STATE_TURNING_OFF || - mState == BluetoothAdapter.STATE_ON || - mState == BluetoothAdapter.STATE_BLE_ON || - mState == BluetoothAdapter.STATE_TURNING_ON || - mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { - return false; - } synchronized(mReceiver) { mQuietEnableExternal = false; @@ -701,6 +688,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().lock(); if (mUnbinding) return; mUnbinding = true; + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); if (mBluetooth != null) { //Unregister callback object try { @@ -1165,7 +1153,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; - handleEnable(msg.arg1 == 1); + if (mBluetooth == null) { + handleEnable(msg.arg1 == 1); + } else { + // + // We need to wait until transitioned to STATE_OFF and + // the previous Bluetooth process has exited. The + // waiting period has three components: + // (a) Wait until the local state is STATE_OFF. This + // is accomplished by "waitForOnOff(false, true)". + // (b) Wait until the STATE_OFF state is updated to + // all components. + // (c) Wait until the Bluetooth process exits, and + // ActivityManager detects it. + // The waiting for (b) and (c) is accomplished by + // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE + // message. On slower devices, that delay needs to be + // on the order of (2 * SERVICE_RESTART_TIME_MS). + // + waitForOnOff(false, true); + mQuietEnable = (msg.arg1 == 1); + Message restartMsg = mHandler.obtainMessage( + MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, + 2 * SERVICE_RESTART_TIME_MS); + } break; case MESSAGE_DISABLE: @@ -1617,14 +1629,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newState == BluetoothAdapter.STATE_OFF) { // If Bluetooth is off, send service down event to proxy objects, and unbind if (DBG) Slog.d(TAG, "Bluetooth is complete turn off"); - if (canUnbindBluetoothService()) { - if (DBG) Slog.d(TAG, "Good to unbind!"); - sendBluetoothServiceDownCallback(); - unbindAndFinish(); - sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; - } + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; } else if (!intermediate_off) { // connect to GattService @@ -1726,25 +1735,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { quietMode ? 1 : 0, 0)); } - private boolean canUnbindBluetoothService() { - try { - //Only unbind with mEnable flag not set - //For race condition: disable and enable back-to-back - //Avoid unbind right after enable due to callback from disable - //Only unbind with Bluetooth at OFF state - //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message - mBluetoothLock.readLock().lock(); - if (mEnable || (mBluetooth == null)) return false; - if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; - return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return false; - } - private void recoverBluetoothServiceFromError() { Slog.e(TAG,"recoverBluetoothServiceFromError"); try { -- GitLab From 7e80e3851e11d593ac9669a09e6e65f2aceb57e3 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Thu, 21 Apr 2016 14:10:55 -0700 Subject: [PATCH 0580/1408] While turning OFF do not honor ON requests. Native stack does not handle being put from OFF -> ON state without doing a complete cleanup. Hence instead of going from start -> ON -> OFF -> cleanup it goes start -> ON -> OFF ->ON -> ... usually leads to race conditions down the road in native. This patch is a workaround so that we can throw away the requests if we are in currently "turning off" phase. The side-effect would be that user will need to turn it ON again. The race happens when the turn OFF time is longer but usually it is found to be close to order of seconds hence the wait should be bounded. Bug: b/28318203 Change-Id: I14f6633f31311e5b561e1dcbc8a9d6d2a5dd6fdc --- .../server/bluetooth/BluetoothManagerService.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 60005685cf9..d0f20b3d8da 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -649,7 +649,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { "Need BLUETOOTH ADMIN permission"); if (DBG) { Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding); + " mBinding = " + mBinding + " mState = " + mState); + } + // We do not honor ON requests when the adapter is already turned ON or in the process of + // turning ON. + // As a protective mechanism to make sure that the native stack gets cleaned up properly + // before turning it back ON we ignore requests while the bluetooth is turning OFF. + // Bug: b/28318203 + if (mState == BluetoothAdapter.STATE_BLE_TURNING_OFF || + mState == BluetoothAdapter.STATE_TURNING_OFF || + mState == BluetoothAdapter.STATE_ON || + mState == BluetoothAdapter.STATE_BLE_ON || + mState == BluetoothAdapter.STATE_TURNING_ON || + mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + return false; } synchronized(mReceiver) { -- GitLab From 9c5da667e73cc5deacf74902cb669cadcd3fae1f Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 1 Jun 2016 22:25:18 -0700 Subject: [PATCH 0581/1408] Delay handleEnable() until Bluetooth state is OFF During shutdown of the Bluetooth stack, if a request to enable Bluetooth is received, the request is processed immediately. As a result, we don't let the Bluetooth service to shutdown cleanly, and the Bluetooth process with the native backend doesn't go away. This creates various issues in the native backend stack. Now, after an enable request is received, if necessary we delay acting on it, until the Bluetooth state is OFF. Bug: 17723234 Change-Id: I55db4bbd4f45227aa49eae996dcc328f76557a9b (cherry picked from commit e47ec14318f64c29bf16b5a6bb662bc19206d6b0) --- .../bluetooth/BluetoothManagerService.java | 73 ++++++++----------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d0f20b3d8da..83486d60792 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -651,19 +651,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Log.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); } - // We do not honor ON requests when the adapter is already turned ON or in the process of - // turning ON. - // As a protective mechanism to make sure that the native stack gets cleaned up properly - // before turning it back ON we ignore requests while the bluetooth is turning OFF. - // Bug: b/28318203 - if (mState == BluetoothAdapter.STATE_BLE_TURNING_OFF || - mState == BluetoothAdapter.STATE_TURNING_OFF || - mState == BluetoothAdapter.STATE_ON || - mState == BluetoothAdapter.STATE_BLE_ON || - mState == BluetoothAdapter.STATE_TURNING_ON || - mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { - return false; - } synchronized(mReceiver) { mQuietEnableExternal = false; @@ -713,6 +700,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().lock(); if (mUnbinding) return; mUnbinding = true; + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); if (mBluetooth != null) { if (!mConnection.isGetNameAddressOnly()) { //Unregister callback object @@ -1255,7 +1243,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; - handleEnable(msg.arg1 == 1); + if (mBluetooth == null) { + handleEnable(msg.arg1 == 1); + } else { + // + // We need to wait until transitioned to STATE_OFF and + // the previous Bluetooth process has exited. The + // waiting period has three components: + // (a) Wait until the local state is STATE_OFF. This + // is accomplished by "waitForOnOff(false, true)". + // (b) Wait until the STATE_OFF state is updated to + // all components. + // (c) Wait until the Bluetooth process exits, and + // ActivityManager detects it. + // The waiting for (b) and (c) is accomplished by + // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE + // message. On slower devices, that delay needs to be + // on the order of (2 * SERVICE_RESTART_TIME_MS). + // + waitForOnOff(false, true); + mQuietEnable = (msg.arg1 == 1); + Message restartMsg = mHandler.obtainMessage( + MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, + 2 * SERVICE_RESTART_TIME_MS); + } break; case MESSAGE_DISABLE: @@ -1716,15 +1728,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newState == BluetoothAdapter.STATE_OFF) { // If Bluetooth is off, send service down event to proxy objects, and unbind if (DBG) Log.d(TAG, "Bluetooth is complete turn off"); - if (canUnbindBluetoothService()) { - if (DBG) Log.d(TAG, "Good to unbind!"); - sendBluetoothServiceDownCallback(); - unbindAndFinish(); - sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; - } - + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; } else if (!intermediate_off) { // connect to GattService if (DBG) Log.d(TAG, "Bluetooth is in LE only mode"); @@ -1825,25 +1833,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { quietMode ? 1 : 0, 0)); } - private boolean canUnbindBluetoothService() { - try { - //Only unbind with mEnable flag not set - //For race condition: disable and enable back-to-back - //Avoid unbind right after enable due to callback from disable - //Only unbind with Bluetooth at OFF state - //Only unbind without any MESSAGE_BLUETOOTH_STATE_CHANGE message - mBluetoothLock.readLock().lock(); - if (mEnable || (mBluetooth == null)) return false; - if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false; - return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF); - } catch (RemoteException e) { - Log.e(TAG, "getState()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return false; - } - private void recoverBluetoothServiceFromError() { Log.e(TAG,"recoverBluetoothServiceFromError"); try { -- GitLab From fcfe0a47f1ca7faf32c4d2330364bdb24deea8e5 Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Tue, 7 Jun 2016 13:35:59 -0700 Subject: [PATCH 0582/1408] Fix flaky discoverable stress test. Bug: 28980767 Change-Id: I6858456f58118a3385b05d42361a5c9223ebbdc4 --- .../android/bluetooth/BluetoothTestUtils.java | 108 ++++++++++-------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 08151729b9f..624ffc599a3 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -35,6 +35,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; public class BluetoothTestUtils extends Assert { @@ -505,40 +507,47 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void discoverable(BluetoothAdapter adapter) { - int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; - if (!adapter.isEnabled()) { fail("discoverable() bluetooth not enabled"); } int scanMode = adapter.getScanMode(); - if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) { return; } - BluetoothReceiver receiver = getBluetoothReceiver(mask); + final Semaphore completionSemaphore = new Semaphore(0); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) { + return; + } + final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, + BluetoothAdapter.SCAN_MODE_NONE); + if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + completionSemaphore.release(); + } + } + }; - assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode); - long start = System.currentTimeMillis(); + final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); + mContext.registerReceiver(receiver, filter); assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); - - while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) { - scanMode = adapter.getScanMode(); - if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE - && (receiver.getFiredFlags() & mask) == mask) { - writeOutput(String.format("discoverable() completed in %d ms", - (receiver.getCompletedTime() - start))); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); + boolean success = false; + try { + success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, + TimeUnit.MILLISECONDS); + writeOutput(String.format("discoverable() completed in 0 ms"); + } catch (final InterruptedException e) { + // This should never happen but just in case it does, the test will fail anyway. + } + mContext.unregisterReceiver(receiver); + if (!success) { + fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode, + BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x " - + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, - firedFlags, mask)); } /** @@ -548,40 +557,47 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void undiscoverable(BluetoothAdapter adapter) { - int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG; - if (!adapter.isEnabled()) { fail("undiscoverable() bluetooth not enabled"); } int scanMode = adapter.getScanMode(); - if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { return; } - BluetoothReceiver receiver = getBluetoothReceiver(mask); + final Semaphore completionSemaphore = new Semaphore(0); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) { + return; + } + final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, + BluetoothAdapter.SCAN_MODE_NONE); + if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + completionSemaphore.release(); + } + } + }; - assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode); - long start = System.currentTimeMillis(); + final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); + mContext.registerReceiver(receiver, filter); assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); - - while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) { - scanMode = adapter.getScanMode(); - if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE - && (receiver.getFiredFlags() & mask) == mask) { - writeOutput(String.format("undiscoverable() completed in %d ms", - (receiver.getCompletedTime() - start))); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); + boolean success = false; + try { + success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, + TimeUnit.MILLISECONDS); + writeOutput(String.format("undiscoverable() completed in 0 ms"); + } catch (InterruptedException e) { + // This should never happen but just in case it does, the test will fail anyway. + } + mContext.unregisterReceiver(receiver); + if (!success) { + fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode, + BluetoothAdapter.SCAN_MODE_CONNECTABLE)); } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x " - + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags, - mask)); } /** -- GitLab From 134ed48df045a34566f51b09889fe0119e232672 Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Tue, 7 Jun 2016 16:41:16 -0700 Subject: [PATCH 0583/1408] Fix build break by adding missing parenthesis. Change-Id: Ia9a47e24b25e3093f19aeea297a86964c8f00722 --- framework/tests/src/android/bluetooth/BluetoothTestUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index 624ffc599a3..a7ea43edf7e 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -539,7 +539,7 @@ public class BluetoothTestUtils extends Assert { try { success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, TimeUnit.MILLISECONDS); - writeOutput(String.format("discoverable() completed in 0 ms"); + writeOutput(String.format("discoverable() completed in 0 ms")); } catch (final InterruptedException e) { // This should never happen but just in case it does, the test will fail anyway. } @@ -589,7 +589,7 @@ public class BluetoothTestUtils extends Assert { try { success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, TimeUnit.MILLISECONDS); - writeOutput(String.format("undiscoverable() completed in 0 ms"); + writeOutput(String.format("undiscoverable() completed in 0 ms")); } catch (InterruptedException e) { // This should never happen but just in case it does, the test will fail anyway. } -- GitLab From 2618f26d08a53814c01098c556dd0cd6ab5e6535 Mon Sep 17 00:00:00 2001 From: Yuchao Zhou Date: Wed, 8 Jun 2016 10:07:39 -0700 Subject: [PATCH 0584/1408] Update enable/disable code to reduce flakiness Change-Id: I9d3f69ca583b365bb5f89cfe16adcd6e604614c6 --- .../android/bluetooth/BluetoothTestUtils.java | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index a7ea43edf7e..ee159788ad2 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -425,38 +425,40 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void enable(BluetoothAdapter adapter) { - int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG - | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG); - long start = System.currentTimeMillis(); - BluetoothReceiver receiver = getBluetoothReceiver(mask); - writeOutput("Enabling Bluetooth adapter."); assertFalse(adapter.isEnabled()); - assertTrue(adapter.enable()); - - int state = BluetoothAdapter.STATE_OFF; - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { - state = adapter.getState(); - if (state == BluetoothAdapter.STATE_ON - && (receiver.getFiredFlags() & mask) == mask) { - assertTrue(adapter.isEnabled()); - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("enable() completed in %d ms", (finish - start))); - } else { - writeOutput("enable() completed"); + int btState = adapter.getState(); + final Semaphore completionSemaphore = new Semaphore(0); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { + return; + } + final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + BluetoothAdapter.ERROR); + if (state == BluetoothAdapter.STATE_ON) { + completionSemaphore.release(); } - removeReceiver(receiver); - return; } - sleep(POLL_TIME); - } + }; - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - state, BluetoothAdapter.STATE_ON, firedFlags, mask)); + final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(receiver, filter); + assertTrue(adapter.enable()); + boolean success = false; + try { + success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); + writeOutput(String.format("enable() completed in 0 ms")); + } catch (final InterruptedException e) { + // This should never happen but just in case it does, the test will fail anyway. + } + mContext.unregisterReceiver(receiver); + if (!success) { + fail(String.format("enable() timeout: state=%d (expected %d)", btState, + BluetoothAdapter.STATE_ON)); + } } /** @@ -466,38 +468,40 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. */ public void disable(BluetoothAdapter adapter) { - int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG - | BluetoothReceiver.SCAN_MODE_NONE_FLAG); - long start = System.currentTimeMillis(); - BluetoothReceiver receiver = getBluetoothReceiver(mask); - writeOutput("Disabling Bluetooth adapter."); assertTrue(adapter.isEnabled()); - assertTrue(adapter.disable()); - - int state = BluetoothAdapter.STATE_OFF; - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) { - state = adapter.getState(); - if (state == BluetoothAdapter.STATE_OFF - && (receiver.getFiredFlags() & mask) == mask) { - assertFalse(adapter.isEnabled()); - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("disable() completed in %d ms", (finish - start))); - } else { - writeOutput("disable() completed"); + int btState = adapter.getState(); + final Semaphore completionSemaphore = new Semaphore(0); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { + return; + } + final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + BluetoothAdapter.ERROR); + if (state == BluetoothAdapter.STATE_OFF) { + completionSemaphore.release(); } - removeReceiver(receiver); - return; } - sleep(POLL_TIME); - } + }; - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - state, BluetoothAdapter.STATE_OFF, firedFlags, mask)); + final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(receiver, filter); + assertTrue(adapter.disable()); + boolean success = false; + try { + success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); + writeOutput(String.format("disable() completed in 0 ms")); + } catch (final InterruptedException e) { + // This should never happen but just in case it does, the test will fail anyway. + } + mContext.unregisterReceiver(receiver); + if (!success) { + fail(String.format("disable() timeout: state=%d (expected %d)", btState, + BluetoothAdapter.STATE_OFF)); + } } /** -- GitLab From c5ad1f87b25822ff086c75f5c165d131db0251da Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Thu, 9 Jun 2016 12:58:07 -0700 Subject: [PATCH 0585/1408] Unlock mBluetoothLock.readLock() instead of double-locking it Bug: 29011117 Change-Id: Ie9e359c3ee079c2cecc33f11cfff7c0dc6406be1 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 3eadb5ca731..9154b8eced4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -222,7 +222,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException e) { Slog.e(TAG,"Unable to call onBrEdrDown", e); } finally { - mBluetoothLock.readLock().lock(); + mBluetoothLock.readLock().unlock(); } } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting -- GitLab From 6bc50092b5f0c579a4f964495b1a300a5dfec023 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Thu, 9 Jun 2016 12:58:07 -0700 Subject: [PATCH 0586/1408] Unlock mBluetoothLock.readLock() instead of double-locking it Bug: 29011117 Change-Id: Ie9e359c3ee079c2cecc33f11cfff7c0dc6406be1 (cherry picked from commit 7ee53be300573c9bdc71607d32d4a642e4ad3dc8) --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 83486d60792..5da353fae23 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -225,7 +225,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch(RemoteException e) { Log.e(TAG,"Unable to call onBrEdrDown", e); } finally { - mBluetoothLock.readLock().lock(); + mBluetoothLock.readLock().unlock(); } } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting -- GitLab From d1c5127c223fd416963364a3bb34942746ea7c40 Mon Sep 17 00:00:00 2001 From: ugo_yu Date: Mon, 30 May 2016 20:56:28 +0800 Subject: [PATCH 0587/1408] Correct typo in BluetoothSap binder This patch fixed IBluetoothSap typo as IBluetoothMap in doBind. Fixes: 29025598 Change-Id: Ieab3d1583839db0a33b1cfa4d2de1f27c9cb3fe4 --- framework/java/android/bluetooth/BluetoothSap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 014cb22c57b..e70c936e253 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -138,7 +138,7 @@ public final class BluetoothSap implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothMap.class.getName()); + Intent intent = new Intent(IBluetoothSap.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, -- GitLab From 20f11b34f738d5586e0ad6b1cd50bbe4239de58d Mon Sep 17 00:00:00 2001 From: Calvin On Date: Wed, 15 Jun 2016 17:58:23 -0700 Subject: [PATCH 0588/1408] Fix race with BT disable in BLE_ON_STATE This will restart the BT stack when it detects a transition into OFF state while the user enable flag (mEnable) is set. Bug: 29363429 Change-Id: I9839119b34c4694ad92e96240c6989008b2f8d52 --- .../android/bluetooth/BluetoothAdapter.java | 18 ------ .../bluetooth/BluetoothManagerService.java | 59 ++++++++++++++----- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9390bcd13de..47ae68796f6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -897,28 +897,10 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { - int state = BluetoothAdapter.STATE_OFF; if (isEnabled() == true) { if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); return true; } - // Use service interface to get the exact state - try { - mServiceLock.readLock().lock(); - if (mService != null) { - state = mService.getState(); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - if (state == BluetoothAdapter.STATE_BLE_ON) { - Log.e(TAG, "BT is in BLE_ON State"); - notifyUserAction(true); - return true; - } try { return mManagerService.enable(); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 9154b8eced4..59e4f284964 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1153,8 +1153,27 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; + + // Use service interface to get the exact state + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + int state = mBluetooth.getState(); + if (state == BluetoothAdapter.STATE_BLE_ON) { + Slog.w(TAG, "BT is in BLE_ON State"); + mBluetooth.onLeServiceUp(); + break; + } + } + } catch (RemoteException e) { + Slog.e(TAG, "", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + + mQuietEnable = (msg.arg1 == 1); if (mBluetooth == null) { - handleEnable(msg.arg1 == 1); + handleEnable(mQuietEnable); } else { // // We need to wait until transitioned to STATE_OFF and @@ -1172,7 +1191,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // on the order of (2 * SERVICE_RESTART_TIME_MS). // waitForOnOff(false, true); - mQuietEnable = (msg.arg1 == 1); Message restartMsg = mHandler.obtainMessage( MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, @@ -1335,17 +1353,30 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // handle error state transition case from TURNING_ON to OFF // unbind and rebind bluetooth service and enable bluetooth if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && - (newState == BluetoothAdapter.STATE_OFF) && - (mBluetooth != null) && mEnable) { + (newState == BluetoothAdapter.STATE_OFF) && + (mBluetooth != null) && mEnable) { recoverBluetoothServiceFromError(); } if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && - (newState == BluetoothAdapter.STATE_BLE_ON) && - (mBluetooth != null) && mEnable) { + (newState == BluetoothAdapter.STATE_BLE_ON) && + (mBluetooth != null) && mEnable) { recoverBluetoothServiceFromError(); } + // If we tried to enable BT while BT was in the process of shutting down, + // wait for the BT process to fully tear down and then force a restart + // here. This is a bit of a hack (b/29363429). + if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && + (newState == BluetoothAdapter.STATE_OFF)) { + if (mEnable) { + Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); + waitForOnOff(false, true); + Message restartMsg = mHandler.obtainMessage( + MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); + } + } if (newState == BluetoothAdapter.STATE_ON || - newState == BluetoothAdapter.STATE_BLE_ON) { + newState == BluetoothAdapter.STATE_BLE_ON) { // bluetooth is working, reset the counter if (mErrorRecoveryRetryCounter != 0) { Slog.w(TAG, "bluetooth is recovered from error"); @@ -1388,7 +1419,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Send BT state broadcast to update // the BT icon correctly if ((mState == BluetoothAdapter.STATE_TURNING_ON) || - (mState == BluetoothAdapter.STATE_ON)) { + (mState == BluetoothAdapter.STATE_ON)) { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); mState = BluetoothAdapter.STATE_TURNING_OFF; @@ -1621,8 +1652,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { boolean isStandardBroadcast = true; if (prevState != newState) { //Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_BLE_ON - || newState == BluetoothAdapter.STATE_OFF) { + if (newState == BluetoothAdapter.STATE_BLE_ON || + newState == BluetoothAdapter.STATE_OFF) { boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF && newState == BluetoothAdapter.STATE_BLE_ON); @@ -1667,13 +1698,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothStateCallback(isUp); sendBleStateChanged(prevState, newState); - } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON - || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || + newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { sendBleStateChanged(prevState, newState); isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_TURNING_ON - || newState == BluetoothAdapter.STATE_TURNING_OFF) { + } else if (newState == BluetoothAdapter.STATE_TURNING_ON || + newState == BluetoothAdapter.STATE_TURNING_OFF) { sendBleStateChanged(prevState, newState); } -- GitLab From 5822d7e47b82cf951a758785754b474e487a0388 Mon Sep 17 00:00:00 2001 From: Christine Hallstrom Date: Fri, 17 Jun 2016 16:00:25 -0700 Subject: [PATCH 0589/1408] Fix early termination of while loop in BluetoothSocket#write While loop exits too early and misses writing remaining bytes. Also restructured the loop itself to be more readable. Change-Id: I71e9b331d20b5ae70175450c3346be43ab56c40c --- .../android/bluetooth/BluetoothSocket.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index ae12c88ff23..ec01beff931 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -532,22 +532,19 @@ public final class BluetoothSocket implements Closeable { if(length <= mMaxTxPacketSize) { mSocketOS.write(b, offset, length); } else { - int tmpOffset = offset; - int tmpLength = mMaxTxPacketSize; - int endIndex = offset + length; - boolean done = false; if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" + "Packet will be divided into SDU packets of size " + mMaxTxPacketSize); - do{ + int tmpOffset = offset; + int bytesToWrite = length; + while (bytesToWrite > 0) { + int tmpLength = (bytesToWrite > mMaxTxPacketSize) + ? mMaxTxPacketSize + : bytesToWrite; mSocketOS.write(b, tmpOffset, tmpLength); - tmpOffset += mMaxTxPacketSize; - if((tmpOffset + mMaxTxPacketSize) > endIndex) { - tmpLength = endIndex - tmpOffset; - done = true; - } - } while(!done); - + tmpOffset += tmpLength; + bytesToWrite -= tmpLength; + } } } else { mSocketOS.write(b, offset, length); -- GitLab From f242df2f7ac42405d7d4d83bb745c2bc40088c13 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Mon, 20 Jun 2016 10:26:31 -0700 Subject: [PATCH 0590/1408] Fix links to Bluetooth Guide Change-Id: I5798c3d71c7cc9c509e0f7b04fa140168b0fdc11 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothServerSocket.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 36e041ae0cb..3479e8066ad 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -89,7 +89,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; *

        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide. + * Bluetooth developer guide. *

        * * {@see BluetoothDevice} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f43fb30abb1..fa70c3f7486 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -60,7 +60,7 @@ import java.util.UUID; *
        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + * Bluetooth developer guide.

        *
        * * {@see BluetoothAdapter} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index c15852dcdaa..4860c938405 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -60,7 +60,7 @@ import java.io.IOException; *
        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + * Bluetooth developer guide.

        *
        * * {@see BluetoothSocket} diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index fb81fd1ea9d..07ec9df0ead 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -75,7 +75,7 @@ import java.nio.ByteBuffer; *
        *

        Developer Guides

        *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + * Bluetooth developer guide.

        *
        * * {@see BluetoothServerSocket} -- GitLab From 5399954098e379a181eb68c2b3e1dbba0d0b2e2b Mon Sep 17 00:00:00 2001 From: Calvin On Date: Mon, 20 Jun 2016 15:59:48 -0700 Subject: [PATCH 0591/1408] Guard concurrent accesses to BluetoothA2dp service object This fixes potential NPEs that happen on methods that access mService after checking nullness, i.e. getConnectedDevices. Bug: 29514788 Change-Id: Ic97054fd5a3563a374c0e863fb116c52535a6509 --- .../java/android/bluetooth/BluetoothA2dp.java | 272 ++++++++++-------- 1 file changed, 159 insertions(+), 113 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index f66b5ff466c..353c6400ffd 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -30,8 +30,11 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -114,7 +117,8 @@ public final class BluetoothA2dp implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothA2dp mService; + private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("mServiceLock") private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -122,25 +126,27 @@ public final class BluetoothA2dp implements BluetoothProfile { 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); - } + if (VDBG) Log.d(TAG, "Unbinding service..."); + try { + mServiceLock.writeLock().lock(); + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); } } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG,"",re); + try { + mServiceLock.readLock().lock(); + if (mService == null) { + if (VDBG) Log.d(TAG,"Binding service..."); + doBind(); } + } catch (Exception re) { + Log.e(TAG,"",re); + } finally { + mServiceLock.readLock().unlock(); } } } @@ -189,15 +195,16 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - synchronized (mConnection) { + try { + mServiceLock.writeLock().lock(); if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } + mService = null; + mContext.unbindService(mConnection); } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); } } @@ -229,17 +236,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && + isValidDevice(device)) { return mService.connect(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -270,17 +280,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && + isValidDevice(device)) { return mService.disconnect(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -288,16 +301,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.getConnectedDevices(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); } /** @@ -305,16 +321,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); } /** @@ -322,17 +341,20 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.getConnectionState(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.STATE_DISCONNECTED; } /** @@ -352,21 +374,24 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON){ - return false; - } - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } return mService.setPriority(device, priority); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -385,17 +410,20 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.getPriority(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; } /** @@ -406,16 +434,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { return mService.isAvrcpAbsoluteVolumeSupported(); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -433,16 +464,17 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public void adjustAvrcpAbsoluteVolume(int direction) { if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { mService.adjustAvrcpAbsoluteVolume(direction); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); - return; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); } /** @@ -453,16 +485,17 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); - if (mService != null && isEnabled()) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { mService.setAvrcpAbsoluteVolume(volume); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - return; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); } /** @@ -473,17 +506,20 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { - try { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { return mService.isA2dpPlaying(device); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; } /** @@ -534,7 +570,12 @@ public final class BluetoothA2dp implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dp.Stub.asInterface(service); + try { + mServiceLock.writeLock().lock(); + mService = IBluetoothA2dp.Stub.asInterface(service); + } finally { + mServiceLock.writeLock().unlock(); + } if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); @@ -542,7 +583,12 @@ public final class BluetoothA2dp implements BluetoothProfile { } public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + try { + mServiceLock.writeLock().lock(); + mService = null; + } finally { + mServiceLock.writeLock().unlock(); + } if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); } -- GitLab From 3ad02fff15ef49d705caab6836d76067c489389f Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Mon, 27 Jun 2016 15:25:18 -0700 Subject: [PATCH 0592/1408] Add extra waiting when switching users If disabling Bluetooth times out, wait for an additional amount of time to ensure the process is shut down completely before attempting to restart. Bug: 29738770 Change-Id: I43dec35a1e03d12cb07863babea97d55baa32528 --- .../server/bluetooth/BluetoothManagerService.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 59e4f284964..172025b3833 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -61,7 +61,7 @@ import java.util.Map; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; - private static final boolean DBG = false; + private static final boolean DBG = true; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -1494,7 +1494,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - waitForOnOff(false, true); + boolean didDisableTimeout = !waitForOnOff(false, true); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -1512,7 +1512,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().unlock(); } - SystemClock.sleep(100); + // + // If disabling Bluetooth times out, wait for an + // additional amount of time to ensure the process is + // shut down completely before attempting to restart. + // + if (didDisableTimeout) { + SystemClock.sleep(3000); + } else { + SystemClock.sleep(100); + } mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; -- GitLab From b61166fbeaef2d5a788d93d75e485ad80e3c57f6 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Mon, 27 Jun 2016 20:13:54 -0700 Subject: [PATCH 0593/1408] Use UUIDs for call management in Headset Client (HF) Using UUIDs that are managed by the service gives more control and error handling in the service since it has control over assigning them. Bug: b/29788044 Change-Id: I8483f8e61a33302ba95d544828947d7fb4a21be9 (cherry picked from commit dbeab2c6e12693fe9b06b6a680677da5325c9230) --- .../bluetooth/BluetoothHeadsetClient.java | 73 ++++--------------- .../bluetooth/BluetoothHeadsetClientCall.java | 12 +++ .../bluetooth/IBluetoothHeadsetClient.aidl | 6 +- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 874026fb157..93790feecd1 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -27,6 +27,7 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * Public API to control Hands Free Profile (HFP role only). @@ -799,7 +800,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Works only when Extended Call Control is supported by Audio Gateway. * * @param device remote device - * @param index index of the call to be terminated + * @param call Handle of call obtained in {@link dial()} or obtained via + * {@link ACTION_CALL_CHANGED}. {@code call} may be null in which + * case we will hangup all active calls. * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_CALL_CHANGED} @@ -809,12 +812,12 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_AG_FEATURE_ECC}. * This method invocation will fail silently when feature is not supported.

        */ - public boolean terminateCall(BluetoothDevice device, int index) { + public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.terminateCall(device, index); + return mService.terminateCall(device, call); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -882,42 +885,19 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } - /** - * Redials last number from Audio Gateway. - * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; - */ - public boolean redial(BluetoothDevice device) { - if (DBG) log("redial()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { - return mService.redial(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - /** * Places a call with specified number. * * @param device remote device * @param number valid phone number - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; + * @return {@link BluetoothHeadsetClientCall} call if command has been + * issued successfully; + * {@link null} otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; */ - public boolean dial(BluetoothDevice device, String number) { + public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); if (mService != null && isEnabled() && isValidDevice(device)) { @@ -928,32 +908,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - - /** - * Places a call to the number under specified memory location. - * - * @param device remote device - * @param location valid memory location - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; - */ - public boolean dialMemory(BluetoothDevice device, int location) { - if (DBG) log("dialMemory()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { - return mService.dialMemory(device, location); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return null; } /** diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index c73bc3cb53e..420c079f5fe 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import java.util.UUID; @@ -70,6 +71,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private boolean mMultiParty; private final boolean mOutgoing; private final UUID mUUID; + private final long mCreationElapsedMilli; /** * Creates BluetoothHeadsetClientCall instance. @@ -88,6 +90,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mNumber = number != null ? number : ""; mMultiParty = multiParty; mOutgoing = outgoing; + mCreationElapsedMilli = SystemClock.elapsedRealtime(); } /** @@ -170,6 +173,15 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mNumber; } + /** + * Gets call's creation time in millis since epoch. + * + * @return long representing the creation time. + */ + public long getCreationElapsedMilli() { + return mCreationElapsedMilli; + } + /** * Checks if call is an active call in a conference mode (aka multi party). * diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index 79ae4e48fa7..a351bd2d707 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -47,14 +47,12 @@ interface IBluetoothHeadsetClient { boolean acceptCall(in BluetoothDevice device, int flag); boolean holdCall(in BluetoothDevice device); boolean rejectCall(in BluetoothDevice device); - boolean terminateCall(in BluetoothDevice device, int index); + boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call); boolean enterPrivateMode(in BluetoothDevice device, int index); boolean explicitCallTransfer(in BluetoothDevice device); - boolean redial(in BluetoothDevice device); - boolean dial(in BluetoothDevice device, String number); - boolean dialMemory(in BluetoothDevice device, int location); + BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number); boolean sendDTMF(in BluetoothDevice device, byte code); boolean getLastVoiceTagNumber(in BluetoothDevice device); -- GitLab From 1fff3cdf4b1e4fc7920a5b76d4ae92eb6824325d Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 29 Jun 2016 17:31:44 -0700 Subject: [PATCH 0594/1408] Add Bluetooth toggle prompts - framework If permission review is enabled toggling bluetoth on or off results in a user prompt to collect consent. This applies only to legacy apps, i.e. ones that don't support runtime permissions as they target SDK 22. Also added a configuration resource which controls whether permission review mode is enabled. By default it is not and an OEM can change this via an overlay. For now we also keep the old mechanism to toggle review mode via a build property which is still used and will be removed when clients have transitioned. bug:28715749 Change-Id: I94c5828ad6c8aa6b363622a26ff9da4fc2e2fac7 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++- .../android/bluetooth/IBluetoothManager.aidl | 5 +- .../bluetooth/BluetoothManagerService.java | 83 +++++++++++++++---- 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cd1a3dd02b6..073196cf7c0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -251,6 +252,29 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + *

        This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + *

        Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

        Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.

        @@ -775,7 +799,7 @@ public final class BluetoothAdapter { try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -902,7 +926,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -934,7 +958,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -952,7 +976,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2b853a373b5..3af4a4d7b50 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable( String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); @@ -50,3 +50,4 @@ interface IBluetoothManager int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } + diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8c5887f7a51..8b243e7f751 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,10 +35,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -151,6 +153,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map mProfileServices = new HashMap (); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -243,6 +247,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -644,15 +653,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + public boolean enable(String packageName) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; + } } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); @@ -668,14 +688,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -696,6 +726,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + -- GitLab From 8d7adf284bab6292ef6f54435a726bf7293e2998 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 1 Apr 2016 07:51:45 -0700 Subject: [PATCH 0595/1408] GATT Server refactoring (2/4) Bug: 27999121 Change-Id: Ia5f91298a4b01b62adebc8adc30f27f757259588 --- .../java/android/bluetooth/BluetoothGatt.java | 3 - .../BluetoothGattCharacteristic.java | 12 +- .../bluetooth/BluetoothGattDescriptor.java | 8 + .../bluetooth/BluetoothGattServer.java | 195 +++++++++--------- .../bluetooth/BluetoothGattService.java | 1 - .../android/bluetooth/IBluetoothGatt.aidl | 21 +- .../IBluetoothGattServerCallback.aidl | 41 +--- 7 files changed, 126 insertions(+), 155 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 800dd434a96..abebd6363e8 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -250,9 +250,6 @@ public final class BluetoothGatt implements BluetoothProfile { if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + " handle=" + handle + " Status=" + status); - Log.w(TAG, "onCharacteristicRead() - Device=" + address - + " handle=" + handle + " Status=" + status); - if (!address.equals(mDevice.getAddress())) { return; } diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 01f82e693de..1cc2270be56 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -321,10 +321,10 @@ public class BluetoothGattCharacteristic implements Parcelable { } /** - * Returns the deisred key size. + * Returns the desired key size. * @hide */ - /*package*/ int getKeySize() { + public int getKeySize() { return mKeySize; } @@ -392,6 +392,14 @@ public class BluetoothGattCharacteristic implements Parcelable { return mInstance; } + /** + * Force the instance ID. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstance = instanceId; + } + /** * Returns the properties of this characteristic. * diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 28317c49604..1a4fa487a4d 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -226,6 +226,14 @@ public class BluetoothGattDescriptor implements Parcelable { return mInstance; } + /** + * Force the instance ID. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstance = instanceId; + } + /** * Returns the permissions for this descriptor. * diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index f4513405b19..d3c6444b1f9 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -52,6 +52,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private Object mServerIfLock = new Object(); private int mServerIf; private int mTransport; + private BluetoothGattService mPendingService; private List mServices; private static final int CALLBACK_REG_TIMEOUT = 10000; @@ -109,17 +110,37 @@ public final class BluetoothGattServer implements BluetoothProfile { * Service has been added * @hide */ - public void onServiceAdded(int status, int srvcType, - int srvcInstId, ParcelUuid srvcId) { - UUID srvcUuid = srvcId.getUuid(); - if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid - + "status=" + status); + public void onServiceAdded(int status, BluetoothGattService service) { + if (DBG) Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId() + + " uuid=" + service.getUuid() + " status=" + status); + + if (mPendingService == null) + return; + + BluetoothGattService tmp = mPendingService; + mPendingService = null; + + // Rewrite newly assigned handles to existing service. + tmp.setInstanceId(service.getInstanceId()); + List temp_chars = tmp.getCharacteristics(); + List svc_chars = service.getCharacteristics(); + for (int i=0; i temp_descs = temp_char.getDescriptors(); + List svc_descs = svc_char.getDescriptors(); + for (int j=0; j(); } + /** + * Returns a characteristic with given handle. + * @hide + */ + /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + if (charac.getInstanceId() == handle) + return charac; + } + } + return null; + } + + /** + * Returns a descriptor with given handle. + * @hide + */ + /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) { + for(BluetoothGattService svc : mServices) { + for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + for(BluetoothGattDescriptor desc : charac.getDescriptors()) { + if (desc.getInstanceId() == handle) + return desc; + } + } + } + return null; + } + /** * Close this GATT server instance. * @@ -537,9 +558,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.sendNotification(mServerIf, device.getAddress(), - service.getType(), service.getInstanceId(), - new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), - new ParcelUuid(characteristic.getUuid()), confirm, + characteristic.getInstanceId(), confirm, characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); @@ -568,39 +587,10 @@ public final class BluetoothGattServer implements BluetoothProfile { if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); if (mService == null || mServerIf == 0) return false; - mServices.add(service); + mPendingService = service; try { - mService.beginServiceDeclaration(mServerIf, service.getType(), - service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); - - List includedServices = service.getIncludedServices(); - for (BluetoothGattService includedService : includedServices) { - mService.addIncludedService(mServerIf, - includedService.getType(), - includedService.getInstanceId(), - new ParcelUuid(includedService.getUuid())); - } - - List characteristics = service.getCharacteristics(); - for (BluetoothGattCharacteristic characteristic : characteristics) { - int permission = ((characteristic.getKeySize() - 7) << 12) - + characteristic.getPermissions(); - mService.addCharacteristic(mServerIf, - new ParcelUuid(characteristic.getUuid()), - characteristic.getProperties(), permission); - - List descriptors = characteristic.getDescriptors(); - for (BluetoothGattDescriptor descriptor: descriptors) { - permission = ((characteristic.getKeySize() - 7) << 12) - + descriptor.getPermissions(); - mService.addDescriptor(mServerIf, - new ParcelUuid(descriptor.getUuid()), permission); - } - } - - mService.endServiceDeclaration(mServerIf); + mService.addService(mServerIf, service); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -626,8 +616,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (intService == null) return false; try { - mService.removeService(mServerIf, service.getType(), - service.getInstanceId(), new ParcelUuid(service.getUuid())); + mService.removeService(mServerIf, service.getInstanceId()); mServices.remove(intService); } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index a4e1dc01d00..c888a451e9e 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -250,7 +250,6 @@ public class BluetoothGattService implements Parcelable { /** * Force the instance ID. - * This is needed for conformance testing only. * @hide */ public void setInstanceId(int instanceId) { diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 45b512298e6..d613cd60b1e 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; @@ -65,26 +66,14 @@ interface IBluetoothGatt { void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); - void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport); + void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); - void beginServiceDeclaration(in int serverIf, in int srvcType, - in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId, boolean advertisePreferred); - void addIncludedService(in int serverIf, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId); - void addCharacteristic(in int serverIf, in ParcelUuid charId, - in int properties, in int permissions); - void addDescriptor(in int serverIf, in ParcelUuid descId, - in int permissions); - void endServiceDeclaration(in int serverIf); - void removeService(in int serverIf, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId); + void addService(in int serverIf, in BluetoothGattService service); + void removeService(in int serverIf, in int handle); void clearServices(in int serverIf); void sendResponse(in int serverIf, in String address, in int requestId, in int status, in int offset, in byte[] value); - void sendNotification(in int serverIf, in String address, in int srvcType, - in int srvcInstanceId, in ParcelUuid srvcId, - in int charInstanceId, in ParcelUuid charId, + void sendNotification(in int serverIf, in String address, in int handle, in boolean confirm, in byte[] value); void disconnectAll(); void unregAll(); diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index 8b202b23cc6..0bcb07b9564 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -15,8 +15,7 @@ */ package android.bluetooth; -import android.os.ParcelUuid; - +import android.bluetooth.BluetoothGattService; /** * Callback definitions for interacting with BLE / GATT @@ -27,36 +26,18 @@ oneway interface IBluetoothGattServerCallback { void onScanResult(in String address, in int rssi, in byte[] advData); void onServerConnectionState(in int status, in int serverIf, in boolean connected, in String address); - void onServiceAdded(in int status, in int srvcType, - in int srvcInstId, in ParcelUuid srvcId); - void onCharacteristicReadRequest(in String address, in int transId, - in int offset, in boolean isLong, - in int srvcType, - in int srvcInstId, in ParcelUuid srvcId, - in int charInstId, in ParcelUuid charId); + void onServiceAdded(in int status, in BluetoothGattService service); + void onCharacteristicReadRequest(in String address, in int transId, in int offset, + in boolean isLong, in int handle); void onDescriptorReadRequest(in String address, in int transId, in int offset, in boolean isLong, - in int srvcType, - in int srvcInstId, in ParcelUuid srvcId, - in int charInstId, in ParcelUuid charId, - in ParcelUuid descrId); - void onCharacteristicWriteRequest(in String address, in int transId, - in int offset, in int length, - in boolean isPrep, - in boolean needRsp, - in int srvcType, - in int srvcInstId, in ParcelUuid srvcId, - in int charInstId, in ParcelUuid charId, - in byte[] value); - void onDescriptorWriteRequest(in String address, in int transId, - in int offset, in int length, - in boolean isPrep, - in boolean needRsp, - in int srvcType, - in int srvcInstId, in ParcelUuid srvcId, - in int charInstId, in ParcelUuid charId, - in ParcelUuid descrId, - in byte[] value); + in int handle); + void onCharacteristicWriteRequest(in String address, in int transId, in int offset, + in int length, in boolean isPrep, in boolean needRsp, + in int handle, in byte[] value); + void onDescriptorWriteRequest(in String address, in int transId, in int offset, + in int length, in boolean isPrep, in boolean needRsp, + in int handle, in byte[] value); void onExecuteWrite(in String address, in int transId, in boolean execWrite); void onNotificationSent(in String address, in int status); void onMtuChanged(in String address, in int mtu); -- GitLab From 2a5ef68862f80e04620151c402154e08393f7956 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 30 Mar 2016 22:58:17 -0700 Subject: [PATCH 0596/1408] Remove write type from GATT descriptor writes (1/4) According to the Bluetooth Core specification v4.2, Vol 3, Part G, section 4.12.3: "The Attribute Protocol WRITE REQUEST is used for this sub-procedure". Change-Id: I141dd24ed5911d5d485b52a1b661835b9960921a --- framework/java/android/bluetooth/BluetoothGatt.java | 4 +--- framework/java/android/bluetooth/IBluetoothGatt.aidl | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index abebd6363e8..9cfe4175b60 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -419,7 +419,6 @@ public final class BluetoothGatt implements BluetoothProfile { try { mAuthRetry = true; mService.writeDescriptor(mClientIf, address, handle, - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -942,8 +941,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE, - descriptor.getValue()); + AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index d613cd60b1e..124d39b614b 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -56,7 +56,7 @@ interface IBluetoothGatt { in int writeType, in int authReq, in byte[] value); void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); void writeDescriptor(in int clientIf, in String address, in int handle, - in int writeType, in int authReq, in byte[] value); + in int authReq, in byte[] value); void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); -- GitLab From c7ec3685ba59545c046c08e3fc2ac566ac925b36 Mon Sep 17 00:00:00 2001 From: Mudumba Ananth Date: Mon, 29 Feb 2016 02:14:36 -0800 Subject: [PATCH 0597/1408] HFP 1.7 profile update (3/4) -> Android Framework changes to add support for an API to send indicator change in AG. -> Added a system intent for broadcasting assigned number(ID) of the supported HF indicators and their values (if received) Bug: 19983867 Change-Id: If26a7ae5da5686da72ebca9ec3decfe086e2ffb6 (cherry picked from commit 3246de65dc16fe8882b1f1152a48964e22b109d7) --- .../android/bluetooth/BluetoothHeadset.java | 63 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 2 files changed, 64 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 09a15de8778..f46a3b3c733 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -220,6 +220,46 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ + + /** + * Intent used to broadcast the headset's indicator status + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by + the headset ( as indicated by AT+BIND + command in the SLC sequence).or whose value + is changed (indicated by AT+BIEV command)
        • + *
        • {@link #EXTRA_IND_VALUE}- The updated value of headset indicator.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + *

        {@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are + * given an assigned number. Below shows the assigned number of Indicator added so far + * - Enhanced Safety - 1 + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * @hide + */ + public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = + "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; + + /** + * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG) + * that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_ID = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; + + /** + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the value of the Headset indicator that is being sent. + * @hide + */ + public static final String EXTRA_HF_INDICATORS_IND_VALUE = + "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; + public static final int STATE_AUDIO_CONNECTED = 12; private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; @@ -969,6 +1009,29 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Send Headset the BIND response from AG to report change in the status of the + * HF indicators to the headset + * + * @param ind_id Assigned Number of the indicator (defined by SIG) + * @param ind_status + * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator + * true-Indicator is enabled, value changes may be sent for this indicator + * @hide + */ + public void bindResponse(int ind_id, boolean ind_status) { + if (mService != null && isEnabled()) { + try { + mService.bindResponse(ind_id, ind_status); + } 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())); + } + } + private final IBluetoothProfileServiceConnection mConnection = new IBluetoothProfileServiceConnection.Stub() { @Override diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 0bb4088f62c..6ad442b6138 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -59,4 +59,5 @@ interface IBluetoothHeadset { String number, int type); boolean enableWBS(); boolean disableWBS(); + void bindResponse(int ind_id, boolean ind_status); } -- GitLab From c672f49cb7943b3f42eeb0ab91aa8d85d8511bca Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 4 Aug 2016 13:16:32 -0700 Subject: [PATCH 0598/1408] Fix bad index usage Bug: 27999121 Change-Id: Ie2ba6f71bbf6f789a3c1016ba2f0051b809ac87e --- framework/java/android/bluetooth/BluetoothGattServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index d3c6444b1f9..c2bcbb2df63 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -133,7 +133,7 @@ public final class BluetoothGattServer implements BluetoothProfile { List temp_descs = temp_char.getDescriptors(); List svc_descs = svc_char.getDescriptors(); for (int j=0; j Date: Thu, 28 Jul 2016 05:21:36 -0700 Subject: [PATCH 0599/1408] Add LE Secure Connection data parsing (1/4) Bug: 30460956 Change-Id: I8d6e721b3b04f5ca9e3e02f7f2b90487482e1b37 --- framework/java/android/bluetooth/OobData.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 01f72efb22a..53ca97417a6 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -26,6 +26,8 @@ import android.util.Log; */ public class OobData implements Parcelable { private byte[] securityManagerTk; + private byte[] leSecureConnectionsConfirmation; + private byte[] leSecureConnectionsRandom; public byte[] getSecurityManagerTk() { return securityManagerTk; @@ -35,10 +37,28 @@ public class OobData implements Parcelable { this.securityManagerTk = securityManagerTk; } + public byte[] getLeSecureConnectionsConfirmation() { + return leSecureConnectionsConfirmation; + } + + public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) { + this.leSecureConnectionsConfirmation = leSecureConnectionsConfirmation; + } + + public byte[] getLeSecureConnectionsRandom() { + return leSecureConnectionsRandom; + } + + public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) { + this.leSecureConnectionsRandom = leSecureConnectionsRandom; + } + public OobData() { } private OobData(Parcel in) { securityManagerTk = in.createByteArray(); + leSecureConnectionsConfirmation = in.createByteArray(); + leSecureConnectionsRandom = in.createByteArray(); } public int describeContents() { @@ -48,6 +68,8 @@ public class OobData implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeByteArray(securityManagerTk); + out.writeByteArray(leSecureConnectionsConfirmation); + out.writeByteArray(leSecureConnectionsRandom); } public static final Parcelable.Creator CREATOR -- GitLab From e2cf867851868c34fac467aa356ae59c6649c85e Mon Sep 17 00:00:00 2001 From: Hemal Patel Date: Wed, 17 Aug 2016 13:18:14 -0700 Subject: [PATCH 0600/1408] Docs: Fixed the Bluetooth guide link Fixed the link that points to the Bluetooth guide. Bug: 29268546 Change-Id: I51c48cebf45c78481f8853a93ff7bcd8483d69ba --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 +++++-- framework/java/android/bluetooth/BluetoothDevice.java | 7 +++++-- framework/java/android/bluetooth/BluetoothManager.java | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 47ae68796f6..246a752b3a5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -89,8 +89,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * *

        *

        Developer Guides

        - *

        For more information about using Bluetooth, read the - * Bluetooth developer guide. + *

        + * For more information about using Bluetooth, read the Bluetooth developer + * guide. + *

        *
        * * {@see BluetoothDevice} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f43fb30abb1..189147efa82 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -59,8 +59,11 @@ import java.util.UUID; * *
        *

        Developer Guides

        - *

        For more information about using Bluetooth, read the - * Bluetooth developer guide.

        + *

        + * For more information about using Bluetooth, read the Bluetooth developer + * guide. + *

        *
        * * {@see BluetoothAdapter} diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 35437a1fd70..00058a97909 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -38,8 +38,11 @@ import java.util.List; * *
        *

        Developer Guides

        - *

        For more information about using BLUETOOTH, read the - * Bluetooth developer guide.

        + *

        + * For more information about using BLUETOOTH, read the Bluetooth developer + * guide. + *

        *
        * * @see Context#getSystemService -- GitLab From ab0fd3594f0fe45cffceb360e022302a51cecfee Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Wed, 17 Aug 2016 11:46:34 -0700 Subject: [PATCH 0601/1408] Remove permission review build property - framework Change-Id: Ifcfd436f2d57a6006ef804292d2875434e4669da --- .../server/bluetooth/BluetoothManagerService.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8b243e7f751..9d880202f7a 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -28,6 +28,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -248,8 +249,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; - mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED - || context.getResources().getBoolean( + mPermissionReviewRequired = context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); mBluetooth = null; @@ -742,7 +742,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Legacy apps in permission review mode trigger a user prompt if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { Intent intent = new Intent(intentAction); - mContext.startActivity(intent); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + // Shouldn't happen + Slog.e(TAG, "Intent to handle action " + intentAction + " missing"); + return false; + } return true; } } catch (PackageManager.NameNotFoundException e) { -- GitLab From 2d8d818366dad8ff4a1f57205b1923abc2f42bcd Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 22 Aug 2016 11:17:13 -0700 Subject: [PATCH 0602/1408] Add WorkSource to BLE scanning API This will allow apps that do work on behalf of others to correctly blame those apps for the power implications of BLE scanning. Bug:22718669 Change-Id: I1cbca8cf7bbe1ec5b804228f466fd9dd6fc68183 --- .../android/bluetooth/IBluetoothGatt.aidl | 5 +- .../bluetooth/le/BluetoothLeScanner.java | 64 +++++++++++++++---- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 124d39b614b..74980385a60 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -24,6 +24,7 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; +import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; @@ -36,8 +37,8 @@ interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); void startScan(in int appIf, in boolean isServer, in ScanSettings settings, - in List filters, - in List scanStorages, in String callingPackage); + in List filters, in WorkSource workSource, in List scanStorages, + in String callingPackage); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2ba87744d08..03449ccec35 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.WorkSource; import android.util.Log; import java.util.ArrayList; @@ -89,9 +90,6 @@ public final class BluetoothLeScanner { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(final ScanCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback is null"); - } startScan(null, new ScanSettings.Builder().build(), callback); } @@ -112,14 +110,53 @@ public final class BluetoothLeScanner { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { - startScan(filters, settings, callback, null); + startScan(filters, settings, null, callback, null); + } + + /** + * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to + * specify on behalf of which application(s) the work is being done. + * + * @param workSource {@link WorkSource} identifying the application(s) for which to blame for + * the scan. + * @param callback Callback used to deliver scan results. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { + startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); + } + + /** + * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but + * allows the caller to specify on behalf of which application(s) the work is being done. + * + * @param filters {@link ScanFilter}s for finding exact BLE devices. + * @param settings Settings for the scan. + * @param workSource {@link WorkSource} identifying the application(s) for which to blame for + * the scan. + * @param callback Callback used to deliver scan results. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + public void startScanFromSource(List filters, ScanSettings settings, + final WorkSource workSource, final ScanCallback callback) { + startScan(filters, settings, workSource, callback, null); } private void startScan(List filters, ScanSettings settings, - final ScanCallback callback, List> resultStorages) { + final WorkSource workSource, final ScanCallback callback, + List> resultStorages) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (settings == null || callback == null) { - throw new IllegalArgumentException("settings or callback is null"); + if (callback == null) { + throw new IllegalArgumentException("callback is null"); + } + if (settings == null) { + throw new IllegalArgumentException("settings is null"); } synchronized (mLeScanClients) { if (mLeScanClients.containsKey(callback)) { @@ -152,7 +189,7 @@ public final class BluetoothLeScanner { return; } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, callback, resultStorages); + settings, workSource, callback, resultStorages); wrapper.startRegisteration(); } } @@ -215,7 +252,7 @@ public final class BluetoothLeScanner { scanFilters.add(filter.getFilter()); scanStorages.add(filter.getStorageDescriptors()); } - startScan(scanFilters, settings, callback, scanStorages); + startScan(scanFilters, settings, null, callback, scanStorages); } /** @@ -235,6 +272,7 @@ public final class BluetoothLeScanner { private final ScanCallback mScanCallback; private final List mFilters; + private final WorkSource mWorkSource; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; private List> mResultStorages; @@ -246,10 +284,12 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, - ScanCallback scanCallback, List> resultStorages) { + WorkSource workSource, ScanCallback scanCallback, + List> resultStorages) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; + mWorkSource = workSource; mScanCallback = scanCallback; mClientIf = 0; mResultStorages = resultStorages; @@ -322,7 +362,9 @@ public final class BluetoothLeScanner { mClientIf = clientIf; try { mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, - mResultStorages, ActivityThread.currentOpPackageName()); + mWorkSource, mResultStorages, + ActivityThread.currentOpPackageName()); + } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; -- GitLab From 2e7f4ae7f45be64205047790a189f4e775e016fc Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 5 Aug 2016 06:40:31 -0700 Subject: [PATCH 0603/1408] Separate advertiser from GATT client (1/4) Bug: 30622771 Change-Id: I08c0498f8a1ea04423d3e864e9a60c7c78f1dbad --- .../BluetoothGattCallbackWrapper.java | 5 -- .../android/bluetooth/IBluetoothGatt.aidl | 9 +++- .../bluetooth/IBluetoothGattCallback.aidl | 2 - .../bluetooth/le/BluetoothLeAdvertiser.java | 52 +++++++++---------- .../bluetooth/le/IAdvertiserCallback.aidl | 29 +++++++++++ 5 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 framework/java/android/bluetooth/le/IAdvertiserCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 17e533a5b32..da815691ca0 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -82,11 +82,6 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { public void onReadRemoteRssi(String address, int rssi, int status) throws RemoteException { } - @Override - public void onMultiAdvertiseCallback(int status, boolean isStart, - AdvertiseSettings advertiseSettings) throws RemoteException { - } - @Override public void onConfigureMTU(String address, int mtu, int status) throws RemoteException { } diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 74980385a60..f4ebcaff4dd 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -28,6 +28,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; +import android.bluetooth.le.IAdvertiserCallback; /** * API for interacting with BLE / GATT @@ -41,11 +42,15 @@ interface IBluetoothGatt { in String callingPackage); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); - void startMultiAdvertising(in int appIf, + + void registerAdvertiser(in IAdvertiserCallback callback); + void unregisterAdvertiser(in int advertiserId); + void startMultiAdvertising(in int advertiserId, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in AdvertiseSettings settings); - void stopMultiAdvertising(in int appIf); + void stopMultiAdvertising(in int advertiserId); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index 7163c370694..efda08e2363 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -38,8 +38,6 @@ oneway interface IBluetoothGattCallback { void onDescriptorWrite(in String address, in int status, in int handle); void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onMultiAdvertiseCallback(in int status, boolean isStart, - in AdvertiseSettings advertiseSettings); void onScanManagerErrorCallback(in int errorCode); void onConfigureMTU(in String address, in int mtu, in int status); void onFoundOrLost(in boolean onFound, in ScanResult scanResult); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index d468bd416ed..048f7910cde 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.le.IAdvertiserCallback; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -162,7 +163,7 @@ public final class BluetoothLeAdvertiser { } /** - * Cleans up advertise clients. Should be called when bluetooth is down. + * Cleans up advertisers. Should be called when bluetooth is down. * * @hide */ @@ -228,7 +229,7 @@ public final class BluetoothLeAdvertiser { /** * Bluetooth GATT interface callbacks for advertising. */ - private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper { + private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub { private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; private final AdvertiseCallback mAdvertiseCallback; private final AdvertiseData mAdvertisement; @@ -236,10 +237,10 @@ public final class BluetoothLeAdvertiser { private final AdvertiseSettings mSettings; private final IBluetoothGatt mBluetoothGatt; - // mClientIf 0: not registered + // mAdvertiserId 0: not registered // -1: advertise stopped or registration timeout // >0: registered and advertising started - private int mClientIf; + private int mAdvertiserId; private boolean mIsAdvertising = false; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, @@ -251,35 +252,34 @@ public final class BluetoothLeAdvertiser { mScanResponse = scanResponse; mSettings = settings; mBluetoothGatt = bluetoothGatt; - mClientIf = 0; + mAdvertiserId = 0; } public void startRegisteration() { synchronized (this) { - if (mClientIf == -1) return; + if (mAdvertiserId == -1) return; try { - UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); + mBluetoothGatt.registerAdvertiser(this); wait(LE_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "Failed to start registeration", e); } - if (mClientIf > 0 && mIsAdvertising) { + if (mAdvertiserId > 0 && mIsAdvertising) { mLeAdvertisers.put(mAdvertiseCallback, this); - } else if (mClientIf <= 0) { + } else if (mAdvertiserId <= 0) { // Registration timeout, reset mClientIf to -1 so no subsequent operations can // proceed. - if (mClientIf == 0) mClientIf = -1; + if (mAdvertiserId == 0) mAdvertiserId = -1; // Post internal error if registration failed. postStartFailure(mAdvertiseCallback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); } else { // Unregister application if it's already registered but advertise failed. try { - mBluetoothGatt.unregisterClient(mClientIf); - mClientIf = -1; + mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); + mAdvertiserId = -1; } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } @@ -290,7 +290,7 @@ public final class BluetoothLeAdvertiser { public void stopAdvertising() { synchronized (this) { try { - mBluetoothGatt.stopMultiAdvertising(mClientIf); + mBluetoothGatt.stopMultiAdvertising(mAdvertiserId); wait(LE_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "Failed to stop advertising", e); @@ -305,20 +305,20 @@ public final class BluetoothLeAdvertiser { } /** - * Application interface registered - app is ready to go + * Advertiser interface registered - app is ready to go */ @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + public void onAdvertiserRegistered(int status, int advertiserId) { + Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { - if (mClientIf == -1) { - // Registration succeeds after timeout, unregister client. - mBluetoothGatt.unregisterClient(clientIf); + if (mAdvertiserId == -1) { + // Registration succeeds after timeout, unregister advertiser. + mBluetoothGatt.unregisterAdvertiser(advertiserId); } else { - mClientIf = clientIf; - mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, + mAdvertiserId = advertiserId; + mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement, mScanResponse, mSettings); } return; @@ -327,7 +327,7 @@ public final class BluetoothLeAdvertiser { } } // Registration failed. - mClientIf = -1; + mAdvertiserId = -1; notifyAll(); } } @@ -346,10 +346,10 @@ public final class BluetoothLeAdvertiser { postStartFailure(mAdvertiseCallback, status); } } else { - // unregister client for stop. + // unregister advertiser for stop. try { - mBluetoothGatt.unregisterClient(mClientIf); - mClientIf = -1; + mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); + mAdvertiserId = -1; mIsAdvertising = false; mLeAdvertisers.remove(mAdvertiseCallback); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl b/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl new file mode 100644 index 00000000000..c58b1dfec96 --- /dev/null +++ b/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl @@ -0,0 +1,29 @@ +/* + * 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.le; + +import android.bluetooth.le.AdvertiseSettings; + +/** + * Callback definitions for interacting with Advertiser + * @hide + */ +oneway interface IAdvertiserCallback { + void onAdvertiserRegistered(in int status, in int advertiserId); + + void onMultiAdvertiseCallback(in int status, boolean isStart, + in AdvertiseSettings advertiseSettings); +} -- GitLab From 6fca269639472d8daaedc426ed2f788940f09445 Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Thu, 1 Sep 2016 16:32:13 -0700 Subject: [PATCH 0604/1408] Update Bluetooth toggle UI to UX spec - framework Change-Id: Iaaf9d3801a721f15ad7019bca59f235e4de3e2a7 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 9d880202f7a..a789157a8ec 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -742,6 +742,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Legacy apps in permission review mode trigger a user prompt if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { Intent intent = new Intent(intentAction); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); try { -- GitLab From c9afd654443b4cccccb00476d31672cc6ddac765 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 11:01:29 -0700 Subject: [PATCH 0605/1408] [DO NOT MERGE] Fix setPairingConfirmation permissions issue (2/2) setPairingConfirmation was set to only require BLUETOOTH_ADMIN permission which shouldn't be able to set the confirmation itself. This is restricted to BLUETOOTH_PRIVILEGED permission. Bug: 29043989 Change-Id: Iddc935f0b02f5ff56e930914b4b664377e786184 --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d789a944f5f..cd74faa4e92 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -993,7 +993,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error -- GitLab From 635bb16a91e7a59d3bdea0ecc2849bee6fcca9b9 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 11:01:29 -0700 Subject: [PATCH 0606/1408] [DO NOT MERGE] Fix setPairingConfirmation permissions issue (2/2) setPairingConfirmation was set to only require BLUETOOTH_ADMIN permission which shouldn't be able to set the confirmation itself. This is restricted to BLUETOOTH_PRIVILEGED permission. Bug: 29043989 Change-Id: Iddc935f0b02f5ff56e930914b4b664377e786184 --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5e50b696b22..0daa193e9c5 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1074,7 +1074,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error -- GitLab From 39b883b6f60c24a7ea0275f56b65ac849bedb9a9 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 9 Aug 2016 13:23:39 -0700 Subject: [PATCH 0607/1408] Fix setPairingConfirmation permissions issue (2/2) setPairingConfirmation was set to only require BLUETOOTH_ADMIN permission which shouldn't be able to set the confirmation itself. This is restricted to BLUETOOTH_PRIVILEGED permission. Bug: 29043989 Change-Id: I887de32d156e672ec44aa0b286cd7ea7f9f8ad55 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d27dfa04faf..f7e25045155 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1153,12 +1153,12 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); -- GitLab From 20d3b1f4958b57908c76bee41cac5eb7e18ef6f5 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 13:23:02 -0700 Subject: [PATCH 0608/1408] [DO NOT MERGE] Prevent FDs from being leaked when accepted sockets are closed Bug: 28672558 Change-Id: I4bc14bd7f098e34012c2ae1eeba2d439145901f0 --- framework/java/android/bluetooth/BluetoothSocket.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 36997e54484..5d66cdd8a27 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -191,6 +191,7 @@ public final class BluetoothSocket implements Closeable { as.close(); throw new IOException("bt socket acept failed"); } + as.mPfd = new ParcelFileDescriptor(fds[0]); as.mSocket = new LocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From a6088d9e03f849c2ff4ace740ccecf1aef7dd7cc Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Tue, 6 Sep 2016 18:03:10 -0700 Subject: [PATCH 0609/1408] Enable Bluetooth by default Bug: 29446913 Change-Id: I6d42810cb3f99617b886f6c643f8c66c42c1d017 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8c5887f7a51..89fdfaf3055 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -293,7 +293,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private final boolean isBluetoothPersistedStateOn() { return Settings.Global.getInt(mContentResolver, - Settings.Global.BLUETOOTH_ON, 0) != BLUETOOTH_OFF; + Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) != BLUETOOTH_OFF; } /** @@ -301,7 +301,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private final boolean isBluetoothPersistedStateOnBluetooth() { return Settings.Global.getInt(mContentResolver, - Settings.Global.BLUETOOTH_ON, 0) == BLUETOOTH_ON_BLUETOOTH; + Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; } /** -- GitLab From 206f65e76a571aa42881a1a8c6c876bbadb9e57f Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 13:23:02 -0700 Subject: [PATCH 0610/1408] [DO NOT MERGE] Prevent FDs from being leaked when accepted sockets are closed Bug: 28672558 Change-Id: I4bc14bd7f098e34012c2ae1eeba2d439145901f0 --- framework/java/android/bluetooth/BluetoothSocket.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d10eaea2fba..5ccabf05672 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -196,6 +196,7 @@ public final class BluetoothSocket implements Closeable { as.close(); throw new IOException("bt socket acept failed"); } + as.mPfd = new ParcelFileDescriptor(fds[0]); as.mSocket = new LocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From b33f167a24b68041924866b5e4a7688a61da80e8 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 13:23:02 -0700 Subject: [PATCH 0611/1408] [DO NOT MERGE] Prevent FDs from being leaked when accepted sockets are closed Bug: 28672558 Change-Id: I4bc14bd7f098e34012c2ae1eeba2d439145901f0 --- framework/java/android/bluetooth/BluetoothSocket.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 36997e54484..5d66cdd8a27 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -191,6 +191,7 @@ public final class BluetoothSocket implements Closeable { as.close(); throw new IOException("bt socket acept failed"); } + as.mPfd = new ParcelFileDescriptor(fds[0]); as.mSocket = new LocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From c0995d637c3d4cdcab7a2c07ea9f08d2a8f46e33 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Thu, 8 Sep 2016 11:01:29 -0700 Subject: [PATCH 0612/1408] [DO NOT MERGE] Fix setPairingConfirmation permissions issue (2/2) setPairingConfirmation was set to only require BLUETOOTH_ADMIN permission which shouldn't be able to set the confirmation itself. This is restricted to BLUETOOTH_PRIVILEGED permission. Bug: 29043989 Change-Id: Iddc935f0b02f5ff56e930914b4b664377e786184 --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index bb0d0a39f56..4dd8913e119 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1106,7 +1106,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error -- GitLab From 76e183f594a6178f45dcfb296aabf12623e22665 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 3 Aug 2016 12:57:34 -0700 Subject: [PATCH 0613/1408] Remove APIs that are not directly usable This API is now exposed via MediaBrowser. Bug: b/28791287 Change-Id: I0a8a185934fd7aaa9f2b5eac7398955fa380060f --- .../bluetooth/BluetoothAvrcpController.java | 98 ------------------- .../bluetooth/IBluetoothAvrcpController.aidl | 3 - 2 files changed, 101 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 444e4293fe2..a395aa470f5 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -65,21 +65,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; - /** - * Intent used to broadcast the change in metadata state of playing track on the AVRCP - * AG. - * - *

        This intent will have the two extras: - *

          - *
        • {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.
        • - *
        • {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback - * state.
        • - *
        - */ - public static final String ACTION_TRACK_EVENT = - "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT"; - - /** * Intent used to broadcast the change in player application setting state on AVRCP AG. * @@ -92,35 +77,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String ACTION_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; - public static final String EXTRA_METADATA = - "android.bluetooth.avrcp-controller.profile.extra.METADATA"; - - public static final String EXTRA_PLAYBACK = - "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK"; - public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - /* - * KeyCoded for Pass Through Commands - */ - public static final int PASS_THRU_CMD_ID_PLAY = 0x44; - public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; - public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; - public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; - public static final int PASS_THRU_CMD_ID_STOP = 0x45; - public static final int PASS_THRU_CMD_ID_FF = 0x49; - public static final int PASS_THRU_CMD_ID_REWIND = 0x48; - public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; - public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; - /* Key State Variables */ - public static final int KEY_STATE_PRESSED = 0; - public static final int KEY_STATE_RELEASED = 1; - /* Group Navigation Key Codes */ - public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00; - public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01; - - private Context mContext; private ServiceListener mServiceListener; private IBluetoothAvrcpController mService; @@ -267,20 +226,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); - if (mService != null && isEnabled()) { - try { - mService.sendPassThroughCmd(device, keyCode, keyState); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); - return; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } - /** * Gets the player application settings. * @@ -300,49 +245,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return settings; } - /** - * Gets the metadata for the current track. - * - * This should be usually called when application UI needs to be updated, eg. when the track - * changes or immediately after connecting and getting the current state. - * @return the {@link MediaMetadata} or {@link null} if there is an error. - */ - public MediaMetadata getMetadata(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getMetadata"); - MediaMetadata metadata = null; - if (mService != null && isEnabled()) { - try { - metadata = mService.getMetadata(device); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getMetadata() " + e); - return null; - } - } - return metadata; - } - - /** - * Gets the playback state for current track. - * - * When the application is first connecting it can use current track state to get playback info. - * For all further updates it should listen to notifications. - * @return the {@link PlaybackState} or {@link null} if there is an error. - */ - public PlaybackState getPlaybackState(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getPlaybackState"); - PlaybackState playbackState = null; - if (mService != null && isEnabled()) { - try { - playbackState = mService.getPlaybackState(device); - } catch (RemoteException e) { - Log.e(TAG, - "Error talking to BT service in getPlaybackState() " + e); - return null; - } - } - return playbackState; - } - /** * Sets the player app setting for current player. * returns true in case setting is supported by remote, false otherwise diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl index f1288d02229..cfa11cac4a8 100644 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -30,10 +30,7 @@ interface IBluetoothAvrcpController { List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); - void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - MediaMetadata getMetadata(in BluetoothDevice device); - PlaybackState getPlaybackState(in BluetoothDevice device); boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); } -- GitLab From 4cd9a26c6c408c3532cb49fc38fe182bc7a0b7fa Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 21 Sep 2016 17:28:11 -0700 Subject: [PATCH 0614/1408] Removed MESSAGE_BIND_PROFILE_SERVICE messages on unbind Add a missing call to remove all MESSAGE_BIND_PROFILE_SERVICE message when unbinding and finishing. Bug: 31442739 Test: manual, and "frameworks/base/core/tests/bluetoothtests" unit tests Change-Id: I7e9a8f79a38d4eb8ab8b53be04650226fc72732e (cherry picked from commit 2bbe13ebccb163c03fd6e74c4450dfd3a43b3984) --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 89fdfaf3055..0f8db2e6565 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -707,6 +707,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mUnbinding) return; mUnbinding = true; mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE); if (mBluetooth != null) { //Unregister callback object try { -- GitLab From 5e3d34a070cbaa7ea142fc51960fc5bb3c0888d5 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 21 Sep 2016 17:28:11 -0700 Subject: [PATCH 0615/1408] Removed MESSAGE_BIND_PROFILE_SERVICE messages on unbind Add a missing call to remove all MESSAGE_BIND_PROFILE_SERVICE message when unbinding and finishing. Bug: 31442739 Test: manual, and "frameworks/base/core/tests/bluetoothtests" unit tests Change-Id: I7e9a8f79a38d4eb8ab8b53be04650226fc72732e --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 172025b3833..6575a2a86cb 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -689,6 +689,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mUnbinding) return; mUnbinding = true; mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE); if (mBluetooth != null) { //Unregister callback object try { -- GitLab From 2e8a0aff66e8501eb5a425b8bfea92ef0dbdc80c Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 23 Sep 2016 12:39:43 -0700 Subject: [PATCH 0616/1408] Prevent NPE if someone creates a bad BluetoothHealthAppConfig object Bug: 28271086 Change-Id: Ic8ebe3152e2b06c070316acc7e6a1f89763cd2a3 --- .../android/bluetooth/BluetoothHealthAppConfiguration.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 15a9101452e..1717a1e36e1 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -68,7 +68,9 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothHealthAppConfiguration) { BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o; - // config.getName() can never be NULL + + if (mName == null) return false; + return mName.equals(config.getName()) && mDataType == config.getDataType() && mRole == config.getRole() && -- GitLab From 59d15654a5ab54a9f1a228152e3d6d829b108a2b Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Tue, 27 Sep 2016 14:34:33 -0700 Subject: [PATCH 0617/1408] Remove unused mContext from BluetoothGatt[Server]. All that this member variable is doing right now is leaking a reference to a context without any benefit. Bug: 31752040 Bug: 31710795 Change-Id: If2241422533318b866340e8dcc9f5fbd9518349c --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 4 +--- framework/java/android/bluetooth/BluetoothGattServer.java | 4 +--- framework/java/android/bluetooth/BluetoothManager.java | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6d6dfebced2..cd5eff29237 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1596,7 +1596,7 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport); + BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 800dd434a96..552c8d3b014 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -41,7 +41,6 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; - private final Context mContext; private IBluetoothGatt mService; private BluetoothGattCallback mCallback; private int mClientIf; @@ -496,9 +495,8 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, + /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport) { - mContext = context; mService = iGatt; mDevice = device; mTransport = transport; diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index f4513405b19..9f8d1a77a80 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -44,7 +44,6 @@ public final class BluetoothGattServer implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; - private final Context mContext; private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; @@ -307,8 +306,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { - mContext = context; + /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) { mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); mCallback = null; diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 00058a97909..29283e793ce 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -236,7 +236,7 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport); + BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt,transport); Boolean regStatus = mGattServer.registerCallback(callback); return regStatus? mGattServer : null; } catch (RemoteException e) { -- GitLab From d5e99a9687fa7b383ac6d916670e4823dbd3f349 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 20 Jul 2016 11:55:48 -0700 Subject: [PATCH 0618/1408] Fix connecting to profiles when bonding from local device (1/3) When bond is created from Bluetooth Settings, profiles gets properly auto connected when services are discovered. If pairing is done from any other app, i.e. NFC app, the bond is not being recognized as initiated from local device, and profiles are not connected. This patch makes sure that if bonding is initiated, no matter from which app, it will cause proper profiles to connect. Bug: 30211618 Change-Id: I71131f33eb5b9db2f5b4a8737b191d541bf1fd3d --- framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++++++ framework/java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index fa70c3f7486..ce54637d883 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -892,6 +892,14 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** @hide */ + public boolean isBondingInitiatedLocally() { + try { + return sService.isBondingInitiatedLocally(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + /** * Set the Out Of Band data for a remote device to be used later * in the pairing mechanism. Users can obtain this data through other diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index a4205396367..8c985364f6a 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -62,6 +62,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); + boolean isBondingInitiatedLocally(in BluetoothDevice device); int getConnectionState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); -- GitLab From 9bb3a68712a411104a6484b58a21521166f5f761 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Sun, 9 Oct 2016 12:54:42 -0700 Subject: [PATCH 0619/1408] Add a way to query for supported Bluetooth profiles. Currently there is no way to get the profiles supported by the Bluetooth adapter. Asking for a profile proxy of an unsupported profile does not fail and can lead to code indefinitely waiting for the proxy response. This new code will allow for checking the supported profiles before asking for the proxies. Bug: 26451648 Change-Id: I4b48e7151f5ca53851aa3b967c143fae140ecd34 --- .../android/bluetooth/BluetoothAdapter.java | 30 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 7 +++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 38 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 246a752b3a5..b93a5578f2b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1497,6 +1497,36 @@ public final class BluetoothAdapter { return null; } + /** + * Gets the currently supported profiles by the adapter. + * + *

        This can be used to check whether a profile is supported before attempting + * to connect to its respective proxy. + * + * @return a list of integers indicating the ids of supported profiles as defined in + * {@link BluetoothProfile}. + * @hide + */ + public List getSupportedProfiles() { + final ArrayList supportedProfiles = new ArrayList(); + + try { + synchronized (mManagerCallback) { + if (mService != null) { + final long supportedProfilesBitMask = mService.getSupportedProfiles(); + + for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) { + if ((supportedProfilesBitMask & (1 << i)) != 0) { + supportedProfiles.add(i); + } + } + } + } + } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);} + + return supportedProfiles; + } + /** * Get the current connection state of the local Bluetooth adapter. * This can be used to check whether the local Bluetooth adapter is connected diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index eee66d193fe..20d95ccc383 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -136,6 +136,13 @@ public interface BluetoothProfile { */ public static final int PBAP_CLIENT = 17; + /** + * Max profile ID. This value should be updated whenever a new profile is added to match + * the largest value assigned to a profile. + * @hide + */ + public static final int MAX_PROFILE_ID = 17; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index a4205396367..f28ab275e4c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -62,6 +62,7 @@ interface IBluetooth boolean cancelBondProcess(in BluetoothDevice device); boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); + long getSupportedProfiles(); int getConnectionState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); -- GitLab From 71019735d35c40c63696745944f6dcacc59faed3 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Wed, 3 Aug 2016 12:57:34 -0700 Subject: [PATCH 0620/1408] Remove APIs that are not directly usable. The right way to expose this API is done via MediaBrowser. Bug: b/28791287 Change-Id: I0a8a185934fd7aaa9f2b5eac7398955fa380060f (cherry picked from commit b5fbb75aa26a6322b26b1289cc5d14a8c31acb34) --- .../bluetooth/BluetoothAvrcpController.java | 98 ------------------- .../bluetooth/IBluetoothAvrcpController.aidl | 3 - 2 files changed, 101 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 444e4293fe2..a395aa470f5 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -65,21 +65,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; - /** - * Intent used to broadcast the change in metadata state of playing track on the AVRCP - * AG. - * - *

        This intent will have the two extras: - *

          - *
        • {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.
        • - *
        • {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback - * state.
        • - *
        - */ - public static final String ACTION_TRACK_EVENT = - "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT"; - - /** * Intent used to broadcast the change in player application setting state on AVRCP AG. * @@ -92,35 +77,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String ACTION_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; - public static final String EXTRA_METADATA = - "android.bluetooth.avrcp-controller.profile.extra.METADATA"; - - public static final String EXTRA_PLAYBACK = - "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK"; - public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - /* - * KeyCoded for Pass Through Commands - */ - public static final int PASS_THRU_CMD_ID_PLAY = 0x44; - public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; - public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; - public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; - public static final int PASS_THRU_CMD_ID_STOP = 0x45; - public static final int PASS_THRU_CMD_ID_FF = 0x49; - public static final int PASS_THRU_CMD_ID_REWIND = 0x48; - public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; - public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; - /* Key State Variables */ - public static final int KEY_STATE_PRESSED = 0; - public static final int KEY_STATE_RELEASED = 1; - /* Group Navigation Key Codes */ - public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00; - public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01; - - private Context mContext; private ServiceListener mServiceListener; private IBluetoothAvrcpController mService; @@ -267,20 +226,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); - if (mService != null && isEnabled()) { - try { - mService.sendPassThroughCmd(device, keyCode, keyState); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); - return; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } - /** * Gets the player application settings. * @@ -300,49 +245,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return settings; } - /** - * Gets the metadata for the current track. - * - * This should be usually called when application UI needs to be updated, eg. when the track - * changes or immediately after connecting and getting the current state. - * @return the {@link MediaMetadata} or {@link null} if there is an error. - */ - public MediaMetadata getMetadata(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getMetadata"); - MediaMetadata metadata = null; - if (mService != null && isEnabled()) { - try { - metadata = mService.getMetadata(device); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getMetadata() " + e); - return null; - } - } - return metadata; - } - - /** - * Gets the playback state for current track. - * - * When the application is first connecting it can use current track state to get playback info. - * For all further updates it should listen to notifications. - * @return the {@link PlaybackState} or {@link null} if there is an error. - */ - public PlaybackState getPlaybackState(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getPlaybackState"); - PlaybackState playbackState = null; - if (mService != null && isEnabled()) { - try { - playbackState = mService.getPlaybackState(device); - } catch (RemoteException e) { - Log.e(TAG, - "Error talking to BT service in getPlaybackState() " + e); - return null; - } - } - return playbackState; - } - /** * Sets the player app setting for current player. * returns true in case setting is supported by remote, false otherwise diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl index f1288d02229..cfa11cac4a8 100644 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -30,10 +30,7 @@ interface IBluetoothAvrcpController { List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); - void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - MediaMetadata getMetadata(in BluetoothDevice device); - PlaybackState getPlaybackState(in BluetoothDevice device); boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); } -- GitLab From c816f2eeba24e185baf977bb5b82227d23257aca Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 18 Oct 2016 10:04:24 -0700 Subject: [PATCH 0621/1408] Bluetooth: More logging of bluetooth service state Bug: 32140251 Bug: 32140271 Bug: 32060415 Change-Id: I50faa184551748023ea5a573646a75293f553d16 --- .../java/android/bluetooth/BluetoothAdapter.java | 6 +++--- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 246a752b3a5..6be5a9725dc 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2006,7 +2006,7 @@ public final class BluetoothAdapter { final private IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); mServiceLock.writeLock().lock(); mService = bluetoothService; @@ -2028,7 +2028,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); try { mServiceLock.writeLock().lock(); @@ -2056,7 +2056,7 @@ public final class BluetoothAdapter { } public void onBrEdrDown() { - if (VDBG) Log.i(TAG, "on QBrEdrDown: "); + if (DBG) Log.i(TAG, "onBrEdrDown:"); } }; diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0f8db2e6565..6757fcce43c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -203,7 +203,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - Slog.d(TAG, "state" + st); + Slog.d(TAG, "Airplane Mode change - current state: " + st); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off @@ -266,6 +266,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { + if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); mEnableExternal = true; } @@ -292,8 +293,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Returns true if the Bluetooth saved state is "on" */ private final boolean isBluetoothPersistedStateOn() { - return Settings.Global.getInt(mContentResolver, - Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) != BLUETOOTH_OFF; + int state = Settings.Global.getInt(mContentResolver, + Settings.Global.BLUETOOTH_ON, -1); + if (DBG) Slog.d(TAG, "Bluetooth persisted state: " + state); + return state != BLUETOOTH_OFF; } /** @@ -309,6 +312,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * */ private void persistBluetoothSetting(int value) { + if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); @@ -1366,7 +1370,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { { int prevState = msg.arg1; int newState = msg.arg2; - if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState); + if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState =" + newState); mState = newState; bluetoothStateChangeHandler(prevState, newState); // handle error state transition case from TURNING_ON to OFF @@ -1678,6 +1682,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void bluetoothStateChangeHandler(int prevState, int newState) { boolean isStandardBroadcast = true; + if (DBG) Slog.d(TAG, "bluetoothStateChangeHandler: " + prevState + " -> " + newState); if (prevState != newState) { //Notify all proxy objects first of adapter state change if (newState == BluetoothAdapter.STATE_BLE_ON || -- GitLab From 0ac5de327ceb8ebc39cba43edabb13660650b422 Mon Sep 17 00:00:00 2001 From: Jacky Cheung Date: Fri, 29 Jul 2016 11:49:27 -0700 Subject: [PATCH 0622/1408] Gradually increase the level of authentication on failed GATT operations. When lower layer returns insufficient authentication or insufficient encryption on various GATT operations, gradually retry with increased authentication level first to AUTHENTICATION_NO_MITM, then AUTHENTICATION_MITM. Change-Id: I68335cf7451aeaf49c227d604f69cb329d117d08 --- .../java/android/bluetooth/BluetoothGatt.java | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 552c8d3b014..0eca4d670d3 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -44,14 +44,18 @@ public final class BluetoothGatt implements BluetoothProfile { private IBluetoothGatt mService; private BluetoothGattCallback mCallback; private int mClientIf; - private boolean mAuthRetry = false; private BluetoothDevice mDevice; private boolean mAutoConnect; + private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; private int mTransport; + private static final int AUTH_RETRY_STATE_IDLE = 0; + private static final int AUTH_RETRY_STATE_NO_MITM = 1; + private static final int AUTH_RETRY_STATE_MITM = 2; + private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; private static final int CONN_STATE_CONNECTED = 2; @@ -262,17 +266,19 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; - mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM); + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readCharacteristic(mClientIf, address, handle, authReq); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) { @@ -311,19 +317,20 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeCharacteristic(mClientIf, address, handle, - characteristic.getWriteType(), AUTHENTICATION_MITM, - characteristic.getValue()); + characteristic.getWriteType(), authReq, characteristic.getValue()); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); @@ -378,17 +385,19 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; - mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM); + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readDescriptor(mClientIf, address, handle, authReq); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = true; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); @@ -417,19 +426,21 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, - AUTHENTICATION_MITM, descriptor.getValue()); + authReq, descriptor.getValue()); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); @@ -503,6 +514,7 @@ public final class BluetoothGatt implements BluetoothProfile { mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; } /** @@ -516,6 +528,7 @@ public final class BluetoothGatt implements BluetoothProfile { unregisterApp(); mConnState = CONN_STATE_CLOSED; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; } /** -- GitLab From 3b008f090189f7243dba215c12c12b601514b0d4 Mon Sep 17 00:00:00 2001 From: Jacky Cheung Date: Thu, 20 Oct 2016 13:55:21 -0700 Subject: [PATCH 0623/1408] Gradually increase the level of authentication on failed GATT operations. When lower layer returns insufficient authentication or insufficient encryption on various GATT operations, gradually retry with increased authentication level first to AUTHENTICATION_NO_MITM, then AUTHENTICATION_MITM. Test: ported from internal branch. Change-Id: I6bcc4198ca493b7900ddd166a81d30cde896fa86 --- .../java/android/bluetooth/BluetoothGatt.java | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 9cfe4175b60..0fe2abf7140 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -45,14 +45,18 @@ public final class BluetoothGatt implements BluetoothProfile { private IBluetoothGatt mService; private BluetoothGattCallback mCallback; private int mClientIf; - private boolean mAuthRetry = false; private BluetoothDevice mDevice; private boolean mAutoConnect; + private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; private int mTransport; + private static final int AUTH_RETRY_STATE_IDLE = 0; + private static final int AUTH_RETRY_STATE_NO_MITM = 1; + private static final int AUTH_RETRY_STATE_MITM = 2; + private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; private static final int CONN_STATE_CONNECTED = 2; @@ -260,17 +264,19 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; - mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM); + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readCharacteristic(mClientIf, address, handle, authReq); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) { @@ -309,19 +315,20 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeCharacteristic(mClientIf, address, handle, - characteristic.getWriteType(), AUTHENTICATION_MITM, - characteristic.getValue()); + characteristic.getWriteType(), authReq, characteristic.getValue()); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); @@ -376,17 +383,19 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; - mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM); + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readDescriptor(mClientIf, address, handle, authReq); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = true; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); @@ -415,18 +424,20 @@ public final class BluetoothGatt implements BluetoothProfile { if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && mAuthRetry == false) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - mAuthRetry = true; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, - AUTHENTICATION_MITM, descriptor.getValue()); + authReq, descriptor.getValue()); + mAuthRetryState++; return; } catch (RemoteException e) { Log.e(TAG,"",e); } } - mAuthRetry = false; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; try { mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); @@ -501,6 +512,7 @@ public final class BluetoothGatt implements BluetoothProfile { mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; } /** @@ -514,6 +526,7 @@ public final class BluetoothGatt implements BluetoothProfile { unregisterApp(); mConnState = CONN_STATE_CLOSED; + mAuthRetryState = AUTH_RETRY_STATE_IDLE; } /** -- GitLab From 4648fc3fefafb823580368e826345d701a439679 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 24 Oct 2016 13:56:54 -0700 Subject: [PATCH 0624/1408] Add helper method to convert Bluetooth UUID to bytes Bug: 30622771 Test: sl4a ConcurrentBleAdvertisingTest Change-Id: I7f646d1d357c51b82efc504a0e65d868ad363ddb --- .../java/android/bluetooth/BluetoothUuid.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 2ded4c8fea3..243579a31ce 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -274,6 +274,48 @@ public final class BluetoothUuid { return new ParcelUuid(new UUID(msb, lsb)); } + /** + * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID, + * Note returned value is little endian (Bluetooth). + * + * @param uuid uuid to parse. + * @return shortest representation of {@code uuid} as bytes. + * @throws IllegalArgumentException If the {@code uuid} is null. + */ + public static byte[] uuidToBytes(ParcelUuid uuid) { + if (uuid == null) { + throw new IllegalArgumentException("uuid cannot be null"); + } + + if (is16BitUuid(uuid)) { + byte[] uuidBytes = new byte[UUID_BYTES_16_BIT]; + int uuidVal = getServiceIdentifierFromParcelUuid(uuid); + uuidBytes[0] = (byte)(uuidVal & 0xFF); + uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8); + return uuidBytes; + } + + if (is32BitUuid(uuid)) { + byte[] uuidBytes = new byte[UUID_BYTES_32_BIT]; + int uuidVal = getServiceIdentifierFromParcelUuid(uuid); + uuidBytes[0] = (byte)(uuidVal & 0xFF); + uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8); + uuidBytes[2] = (byte)((uuidVal & 0xFF0000) >> 16); + uuidBytes[3] = (byte)((uuidVal & 0xFF000000) >> 24); + return uuidBytes; + } + + // Construct a 128 bit UUID. + long msb = uuid.getUuid().getMostSignificantBits(); + long lsb = uuid.getUuid().getLeastSignificantBits(); + + byte[] uuidBytes = new byte[UUID_BYTES_128_BIT]; + ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); + buf.putLong(8, msb); + buf.putLong(0, lsb); + return uuidBytes; + } + /** * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. * -- GitLab From 167b3c5e922186f5d4a6a63c737401c5f2051193 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 25 Oct 2016 10:47:51 -0700 Subject: [PATCH 0625/1408] Bluetooth: prevent enabling BLE in airplane mode Enabling BLE in airplane mode puts BluetoothManagerService in an unexpected state which causes Bluetooth to be on when airplane mode is disabled. Also fixes a bug where a crash of a BLE client would trigger a restart into ON mode. Test: SL4A BleBackgroundScanTest:test_airplane_mode_disables_ble Bug: 32140251 Bug: 32140271 Bug: 32369494 Change-Id: Ie65157e65c3a1ca914f567a7a0c631175d1e5835 (cherry picked from commit bd93b7b3dc6141cef6236cf0ca7dcc5acf5bfeed) --- .../java/android/bluetooth/BluetoothAdapter.java | 16 +++++----------- .../bluetooth/BluetoothManagerService.java | 10 ++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6be5a9725dc..50c8e275ff8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -765,19 +765,13 @@ public final class BluetoothAdapter { public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) return false; - if (isLeEnabled() == true) { - if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); - try { - mManagerService.updateBleAppCount(mToken, true); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - } - try { - if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); + if (isLeEnabled()) { + if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + return true; + } + if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); return mManagerService.enable(); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6757fcce43c..52be811fad1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -217,6 +217,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); + mEnable = false; mEnableExternal = false; } } catch (RemoteException e) { @@ -449,14 +450,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); if (mBleAppCount > 0) --mBleAppCount; if (mBleAppCount == 0) { if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { + if (mBluetooth != null && + mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mEnable = false; mBluetooth.onBrEdrDown(); } } catch (RemoteException e) { @@ -473,6 +476,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public boolean isBleScanAlwaysAvailable() { + if (isAirplaneModeOn() && !mEnable) { + return false; + } try { return (Settings.Global.getInt(mContentResolver, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; -- GitLab From acb832d29d97138cfb925f00a2efd527ff912cac Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 29 Jun 2016 17:31:44 -0700 Subject: [PATCH 0626/1408] Add Bluetooth toggle prompts - framework If permission review is enabled toggling bluetoth on or off results in a user prompt to collect consent. This applies only to legacy apps, i.e. ones that don't support runtime permissions as they target SDK 22. Also added a configuration resource which controls whether permission review mode is enabled. By default it is not and an OEM can change this via an overlay. For now we also keep the old mechanism to toggle review mode via a build property which is still used and will be removed when clients have transitioned. bug:28715749 Change-Id: I94c5828ad6c8aa6b363622a26ff9da4fc2e2fac7 (cherry picked from commit 1fff3cdf4b1e4fc7920a5b76d4ae92eb6824325d) --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++- .../android/bluetooth/IBluetoothManager.aidl | 5 +- .../bluetooth/BluetoothManagerService.java | 83 +++++++++++++++---- 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cd1a3dd02b6..073196cf7c0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -251,6 +252,29 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + *

        This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + *

        Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

        Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.

        @@ -775,7 +799,7 @@ public final class BluetoothAdapter { try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -902,7 +926,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -934,7 +958,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -952,7 +976,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 0b81ee8c547..f39ca8e0e9c 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable(String packageName, boolean persist); IBluetoothGatt getBluetoothGatt(); boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); @@ -49,3 +49,4 @@ interface IBluetoothManager int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } + diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6575a2a86cb..7875cb86ee7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,10 +35,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -151,6 +153,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map mProfileServices = new HashMap (); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -243,6 +247,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -626,15 +635,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + public boolean enable(String packageName) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; + } } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); @@ -650,14 +670,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -678,6 +708,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + -- GitLab From 67242681c87d916a851ee06dd22a42d0746f5493 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 26 Oct 2016 13:05:30 -0700 Subject: [PATCH 0627/1408] Separate LE scanner from GATT client (1/4) Right now, LE scanning functionality is combined with the GATT client. This is the source of various bugs, like scans suddenly stoppinging when a GATT client is killed. It also increases memory consumption, because we associate many structures with a GATT client, which are not necessary when just scanning. Test: sl4a BleScanApiTest ConcurrentBleScanTest Change-Id: I0c25bd4a58bb430eb0ee4100d5f2bbab194f9621 --- .../java/android/bluetooth/BluetoothGatt.java | 2 +- .../BluetoothGattCallbackWrapper.java | 96 ------------------- .../android/bluetooth/IBluetoothGatt.aidl | 12 ++- .../bluetooth/IBluetoothGattCallback.aidl | 6 -- .../IBluetoothGattServerCallback.aidl | 1 - .../bluetooth/le/BluetoothLeAdvertiser.java | 1 - .../bluetooth/le/BluetoothLeScanner.java | 59 ++++++------ .../bluetooth/le/IScannerCallback.aidl | 31 ++++++ 8 files changed, 68 insertions(+), 140 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java create mode 100644 framework/java/android/bluetooth/le/IScannerCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 0fe2abf7140..f76d62d3c41 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -136,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ private final IBluetoothGattCallback mBluetoothGattCallback = - new BluetoothGattCallbackWrapper() { + new IBluetoothGattCallback.Stub() { /** * Application interface registered - app is ready to go * @hide diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java deleted file mode 100644 index da815691ca0..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.ScanResult; -import android.bluetooth.BluetoothGattService; -import android.os.ParcelUuid; -import android.os.RemoteException; - -import java.util.List; - -/** - * Wrapper class for default implementation of IBluetoothGattCallback. - * - * @hide - */ -public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { - - @Override - public void onClientRegistered(int status, int clientIf) throws RemoteException { - } - - @Override - public void onClientConnectionState(int status, int clientIf, boolean connected, String address) - throws RemoteException { - } - - @Override - public void onScanResult(ScanResult scanResult) throws RemoteException { - } - - @Override - public void onBatchScanResults(List batchResults) throws RemoteException { - } - - @Override - public void onSearchComplete(String address, List services, - int status) throws RemoteException { - } - - @Override - public void onCharacteristicRead(String address, int status, int handle, byte[] value) - throws RemoteException { - } - - @Override - public void onCharacteristicWrite(String address, int status, int handle) throws RemoteException { - } - - @Override - public void onExecuteWrite(String address, int status) throws RemoteException { - } - - @Override - public void onDescriptorRead(String address, int status, int handle, byte[] value) throws RemoteException { - } - - @Override - public void onDescriptorWrite(String address, int status, int handle) throws RemoteException { - } - - @Override - public void onNotify(String address, int handle, byte[] value) throws RemoteException { - } - - @Override - public void onReadRemoteRssi(String address, int rssi, int status) throws RemoteException { - } - - @Override - public void onConfigureMTU(String address, int mtu, int status) throws RemoteException { - } - - @Override - public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException { - } - - @Override - public void onScanManagerErrorCallback(int errorCode) throws RemoteException { - } -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index f4ebcaff4dd..aa2291e072d 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -29,6 +29,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; +import android.bluetooth.le.IScannerCallback; /** * API for interacting with BLE / GATT @@ -37,11 +38,12 @@ import android.bluetooth.le.IAdvertiserCallback; interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); - void startScan(in int appIf, in boolean isServer, in ScanSettings settings, - in List filters, in WorkSource workSource, in List scanStorages, - in String callingPackage); - void stopScan(in int appIf, in boolean isServer); - void flushPendingBatchResults(in int appIf, in boolean isServer); + void registerScanner(in IScannerCallback callback); + void unregisterScanner(in int scannerId); + void startScan(in int scannerId, in ScanSettings settings, in List filters, + in WorkSource workSource, in List scanStorages, in String callingPackage); + void stopScan(in int scannerId); + void flushPendingBatchResults(in int scannerId); void registerAdvertiser(in IAdvertiserCallback callback); void unregisterAdvertiser(in int advertiserId); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl index efda08e2363..72cb6182773 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -17,8 +17,6 @@ package android.bluetooth; import android.os.ParcelUuid; import android.bluetooth.BluetoothGattService; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.ScanResult; /** * Callback definitions for interacting with BLE / GATT @@ -28,8 +26,6 @@ oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); - void onScanResult(in ScanResult scanResult); - void onBatchScanResults(in List batchResults); void onSearchComplete(in String address, in List services, in int status); void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); void onCharacteristicWrite(in String address, in int status, in int handle); @@ -38,7 +34,5 @@ oneway interface IBluetoothGattCallback { void onDescriptorWrite(in String address, in int status, in int handle); void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onScanManagerErrorCallback(in int errorCode); void onConfigureMTU(in String address, in int mtu, in int status); - void onFoundOrLost(in boolean onFound, in ScanResult scanResult); } diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index 0bcb07b9564..1a924fb4e31 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -23,7 +23,6 @@ import android.bluetooth.BluetoothGattService; */ oneway interface IBluetoothGattServerCallback { void onServerRegistered(in int status, in int serverIf); - void onScanResult(in String address, in int rssi, in byte[] advData); void onServerConnectionState(in int status, in int serverIf, in boolean connected, in String address); void onServiceAdded(in int status, in BluetoothGattService service); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 048f7910cde..26f2dea9022 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -18,7 +18,6 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 5715ff87242..b63c614711e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -22,9 +22,9 @@ import android.annotation.SystemApi; import android.app.ActivityThread; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.le.IScannerCallback; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -267,7 +267,7 @@ public final class BluetoothLeScanner { /** * Bluetooth GATT interface callbacks */ - private class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { + private class BleScanCallbackWrapper extends IScannerCallback.Stub { private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; private final ScanCallback mScanCallback; @@ -280,7 +280,7 @@ public final class BluetoothLeScanner { // mLeHandle 0: not registered // -1: scan stopped or registration failed // > 0: registered and scan started - private int mClientIf; + private int mScannerId; public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, @@ -291,28 +291,27 @@ public final class BluetoothLeScanner { mSettings = settings; mWorkSource = workSource; mScanCallback = scanCallback; - mClientIf = 0; + mScannerId = 0; mResultStorages = resultStorages; } public void startRegisteration() { synchronized (this) { // Scan stopped. - if (mClientIf == -1) return; + if (mScannerId == -1) return; try { - UUID uuid = UUID.randomUUID(); - mBluetoothGatt.registerClient(new ParcelUuid(uuid), this); + mBluetoothGatt.registerScanner(this); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "application registeration exception", e); postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); } - if (mClientIf > 0) { + if (mScannerId > 0) { mLeScanClients.put(mScanCallback, this); } else { - // Registration timed out or got exception, reset clientIf to -1 so no + // Registration timed out or got exception, reset scannerId to -1 so no // subsequent operations can proceed. - if (mClientIf == 0) mClientIf = -1; + if (mScannerId == 0) mScannerId = -1; postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); } @@ -321,28 +320,28 @@ public final class BluetoothLeScanner { public void stopLeScan() { synchronized (this) { - if (mClientIf <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mClientIf); + if (mScannerId <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mScannerId); return; } try { - mBluetoothGatt.stopScan(mClientIf, false); - mBluetoothGatt.unregisterClient(mClientIf); + mBluetoothGatt.stopScan(mScannerId); + mBluetoothGatt.unregisterScanner(mScannerId); } catch (RemoteException e) { Log.e(TAG, "Failed to stop scan and unregister", e); } - mClientIf = -1; + mScannerId = -1; } } void flushPendingBatchResults() { synchronized (this) { - if (mClientIf <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mClientIf); + if (mScannerId <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mScannerId); return; } try { - mBluetoothGatt.flushPendingBatchResults(mClientIf, false); + mBluetoothGatt.flushPendingBatchResults(mScannerId); } catch (RemoteException e) { Log.e(TAG, "Failed to get pending scan results", e); } @@ -353,28 +352,28 @@ public final class BluetoothLeScanner { * Application interface registered - app is ready to go */ @Override - public void onClientRegistered(int status, int clientIf) { - Log.d(TAG, "onClientRegistered() - status=" + status + - " clientIf=" + clientIf + " mClientIf=" + mClientIf); + public void onScannerRegistered(int status, int scannerId) { + Log.d(TAG, "onScannerRegistered() - status=" + status + + " scannerId=" + scannerId + " mScannerId=" + mScannerId); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { - if (mClientIf == -1) { + if (mScannerId == -1) { // Registration succeeds after timeout, unregister client. - mBluetoothGatt.unregisterClient(clientIf); + mBluetoothGatt.unregisterClient(scannerId); } else { - mClientIf = clientIf; - mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, + mScannerId = scannerId; + mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, mWorkSource, mResultStorages, ActivityThread.currentOpPackageName()); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); - mClientIf = -1; + mScannerId = -1; } } else { // registration failed - mClientIf = -1; + mScannerId = -1; } notifyAll(); } @@ -391,7 +390,7 @@ public final class BluetoothLeScanner { // Check null in case the scan has been stopped synchronized (this) { - if (mClientIf <= 0) return; + if (mScannerId <= 0) return; } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @@ -423,7 +422,7 @@ public final class BluetoothLeScanner { // Check null in case the scan has been stopped synchronized (this) { - if (mClientIf <= 0) + if (mScannerId <= 0) return; } Handler handler = new Handler(Looper.getMainLooper()); @@ -447,7 +446,7 @@ public final class BluetoothLeScanner { Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); } synchronized (this) { - if (mClientIf <= 0) + if (mScannerId <= 0) return; } postCallbackError(mScanCallback, errorCode); diff --git a/framework/java/android/bluetooth/le/IScannerCallback.aidl b/framework/java/android/bluetooth/le/IScannerCallback.aidl new file mode 100644 index 00000000000..8cbbaef41ae --- /dev/null +++ b/framework/java/android/bluetooth/le/IScannerCallback.aidl @@ -0,0 +1,31 @@ +/* + * 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.le; + +import android.bluetooth.le.ScanResult; + +/** + * Callback definitions for interacting with Advertiser + * @hide + */ +oneway interface IScannerCallback { + void onScannerRegistered(in int status, in int scannerId); + + void onScanResult(in ScanResult scanResult); + void onBatchScanResults(in List batchResults); + void onFoundOrLost(in boolean onFound, in ScanResult scanResult); + void onScanManagerErrorCallback(in int errorCode); +} -- GitLab From 0885f257213db97228fe6e66985ec177d3dfd722 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Mon, 27 Jun 2016 20:13:54 -0700 Subject: [PATCH 0628/1408] Use UUIDs for call management in Headset Client (HF) Using UUIDs that are managed by the service gives more control and error handling in the service since it has control over assigning them. Bug: b/29788044 Change-Id: I8483f8e61a33302ba95d544828947d7fb4a21be9 (cherry picked from commit 457639426db7441cc7a5ad9990fd56ec79b2b003) (cherry picked from commit b61166fbeaef2d5a788d93d75e485ad80e3c57f6) --- .../bluetooth/BluetoothHeadsetClient.java | 73 ++++--------------- .../bluetooth/BluetoothHeadsetClientCall.java | 12 +++ .../bluetooth/IBluetoothHeadsetClient.aidl | 6 +- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 874026fb157..93790feecd1 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -27,6 +27,7 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * Public API to control Hands Free Profile (HFP role only). @@ -799,7 +800,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Works only when Extended Call Control is supported by Audio Gateway. * * @param device remote device - * @param index index of the call to be terminated + * @param call Handle of call obtained in {@link dial()} or obtained via + * {@link ACTION_CALL_CHANGED}. {@code call} may be null in which + * case we will hangup all active calls. * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_CALL_CHANGED} @@ -809,12 +812,12 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_AG_FEATURE_ECC}. * This method invocation will fail silently when feature is not supported.

        */ - public boolean terminateCall(BluetoothDevice device, int index) { + public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { - return mService.terminateCall(device, index); + return mService.terminateCall(device, call); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -882,42 +885,19 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } - /** - * Redials last number from Audio Gateway. - * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; - */ - public boolean redial(BluetoothDevice device) { - if (DBG) log("redial()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { - return mService.redial(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - /** * Places a call with specified number. * * @param device remote device * @param number valid phone number - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; + * @return {@link BluetoothHeadsetClientCall} call if command has been + * issued successfully; + * {@link null} otherwise; + * upon completion HFP sends {@link #ACTION_CALL_CHANGED} + * intent in case of success; {@link #ACTION_RESULT} is sent + * otherwise; */ - public boolean dial(BluetoothDevice device, String number) { + public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); if (mService != null && isEnabled() && isValidDevice(device)) { @@ -928,32 +908,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } - - /** - * Places a call to the number under specified memory location. - * - * @param device remote device - * @param location valid memory location - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; - */ - public boolean dialMemory(BluetoothDevice device, int location) { - if (DBG) log("dialMemory()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - try { - return mService.dialMemory(device, location); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return null; } /** diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index c73bc3cb53e..420c079f5fe 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import java.util.UUID; @@ -70,6 +71,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private boolean mMultiParty; private final boolean mOutgoing; private final UUID mUUID; + private final long mCreationElapsedMilli; /** * Creates BluetoothHeadsetClientCall instance. @@ -88,6 +90,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mNumber = number != null ? number : ""; mMultiParty = multiParty; mOutgoing = outgoing; + mCreationElapsedMilli = SystemClock.elapsedRealtime(); } /** @@ -170,6 +173,15 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mNumber; } + /** + * Gets call's creation time in millis since epoch. + * + * @return long representing the creation time. + */ + public long getCreationElapsedMilli() { + return mCreationElapsedMilli; + } + /** * Checks if call is an active call in a conference mode (aka multi party). * diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index 79ae4e48fa7..a351bd2d707 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -47,14 +47,12 @@ interface IBluetoothHeadsetClient { boolean acceptCall(in BluetoothDevice device, int flag); boolean holdCall(in BluetoothDevice device); boolean rejectCall(in BluetoothDevice device); - boolean terminateCall(in BluetoothDevice device, int index); + boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call); boolean enterPrivateMode(in BluetoothDevice device, int index); boolean explicitCallTransfer(in BluetoothDevice device); - boolean redial(in BluetoothDevice device); - boolean dial(in BluetoothDevice device, String number); - boolean dialMemory(in BluetoothDevice device, int location); + BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number); boolean sendDTMF(in BluetoothDevice device, byte code); boolean getLastVoiceTagNumber(in BluetoothDevice device); -- GitLab From 257f8fe85e626e8741ae626228214c2c34390aea Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 25 Oct 2016 10:47:51 -0700 Subject: [PATCH 0629/1408] Bluetooth: prevent enabling BLE in airplane mode Enabling BLE in airplane mode puts BluetoothManagerService in an unexpected state which causes Bluetooth to be on when airplane mode is disabled. Also fixes a bug where a crash of a BLE client would trigger a restart into ON mode. Test: SL4A BleBackgroundScanTest:test_airplane_mode_disables_ble Bug: 32140251 Bug: 32140271 Bug: 32369494 Change-Id: Ie65157e65c3a1ca914f567a7a0c631175d1e5835 --- .../java/android/bluetooth/BluetoothAdapter.java | 16 +++++----------- .../bluetooth/BluetoothManagerService.java | 10 ++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 073196cf7c0..b57a4e07bf8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -786,19 +786,13 @@ public final class BluetoothAdapter { public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) return false; - if (isLeEnabled() == true) { - if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); - try { - mManagerService.updateBleAppCount(mToken, true); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - } - try { - if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); + if (isLeEnabled()) { + if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + return true; + } + if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 7875cb86ee7..2a5b1946b90 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -221,6 +221,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { mBluetooth.onBrEdrDown(); + mEnable = false; mEnableExternal = false; } } catch (RemoteException e) { @@ -436,14 +437,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); if (mBleAppCount > 0) --mBleAppCount; if (mBleAppCount == 0) { if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { + if (mBluetooth != null && + mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mEnable = false; mBluetooth.onBrEdrDown(); } } catch (RemoteException e) { @@ -460,6 +463,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public boolean isBleScanAlwaysAvailable() { + if (isAirplaneModeOn() && !mEnable) { + return false; + } try { return (Settings.Global.getInt(mContentResolver, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; -- GitLab From ff5093b73ec3daf31209d1d031e0e160e012274d Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Sun, 9 Oct 2016 12:54:42 -0700 Subject: [PATCH 0630/1408] Add a way to query for supported Bluetooth profiles. Currently there is no way to get the profiles supported by the Bluetooth adapter. Asking for a profile proxy of an unsupported profile does not fail and can lead to code indefinitely waiting for the proxy response. This new code will allow for checking the supported profiles before asking for the proxies. Bug: 26451648 Change-Id: I4b48e7151f5ca53851aa3b967c143fae140ecd34 (cherry picked from commit b1301fa2849bafd6daa422281dc5200863bc761e) --- .../android/bluetooth/BluetoothAdapter.java | 30 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 7 +++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 38 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b57a4e07bf8..59edadce962 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1512,6 +1512,36 @@ public final class BluetoothAdapter { return null; } + /** + * Gets the currently supported profiles by the adapter. + * + *

        This can be used to check whether a profile is supported before attempting + * to connect to its respective proxy. + * + * @return a list of integers indicating the ids of supported profiles as defined in + * {@link BluetoothProfile}. + * @hide + */ + public List getSupportedProfiles() { + final ArrayList supportedProfiles = new ArrayList(); + + try { + synchronized (mManagerCallback) { + if (mService != null) { + final long supportedProfilesBitMask = mService.getSupportedProfiles(); + + for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) { + if ((supportedProfilesBitMask & (1 << i)) != 0) { + supportedProfiles.add(i); + } + } + } + } + } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);} + + return supportedProfiles; + } + /** * Get the current connection state of the local Bluetooth adapter. * This can be used to check whether the local Bluetooth adapter is connected diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index eee66d193fe..20d95ccc383 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -136,6 +136,13 @@ public interface BluetoothProfile { */ public static final int PBAP_CLIENT = 17; + /** + * Max profile ID. This value should be updated whenever a new profile is added to match + * the largest value assigned to a profile. + * @hide + */ + public static final int MAX_PROFILE_ID = 17; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 8c985364f6a..96a1ae8b945 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -63,6 +63,7 @@ interface IBluetooth boolean removeBond(in BluetoothDevice device); int getBondState(in BluetoothDevice device); boolean isBondingInitiatedLocally(in BluetoothDevice device); + long getSupportedProfiles(); int getConnectionState(in BluetoothDevice device); String getRemoteName(in BluetoothDevice device); -- GitLab From 54d4b66174dd0ce4d5c7e8d3898a2b4d3893dc71 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 1 Sep 2016 14:19:28 -0700 Subject: [PATCH 0631/1408] 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) --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothMapClient.java | 415 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/IBluetoothMapClient.aidl | 40 ++ 4 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothMapClient.java create mode 100644 framework/java/android/bluetooth/IBluetoothMapClient.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 59edadce962..11c27355c78 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1973,6 +1973,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PBAP_CLIENT) { BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); return true; + } else if (profile == BluetoothProfile.MAP_CLIENT) { + BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); + return true; } else { return false; } @@ -2045,6 +2048,10 @@ public final class BluetoothAdapter { BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy; pbapClient.close(); break; + case BluetoothProfile.MAP_CLIENT: + BluetoothMapClient mapClient = (BluetoothMapClient)proxy; + mapClient.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java new file mode 100644 index 00000000000..da66b93f2dd --- /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; + } + + +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 20d95ccc383..f3636070fa5 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Android Open Source Project + * Copyright (C) 2010-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. @@ -136,6 +136,12 @@ public interface BluetoothProfile { */ public static final int PBAP_CLIENT = 17; + /** + * MAP Messaging Client Equipment (MCE) + * @hide + */ + public static final int MAP_CLIENT = 18; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. diff --git a/framework/java/android/bluetooth/IBluetoothMapClient.aidl b/framework/java/android/bluetooth/IBluetoothMapClient.aidl new file mode 100644 index 00000000000..df45af91c92 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothMapClient.aidl @@ -0,0 +1,40 @@ +/* + * 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.bluetooth.BluetoothDevice; +import android.net.Uri; + +/** + * System private API for Bluetooth MAP MCE service + * + * {@hide} + */ +interface IBluetoothMapClient { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device,in int priority); + int getPriority(in BluetoothDevice device); + boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message, + in PendingIntent sentIntent, in PendingIntent deliveryIntent); + boolean getUnreadMessages(in BluetoothDevice device); +} -- GitLab From d95b0847a70845532b12b5fab0290dcc22be29b3 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 6 Oct 2016 11:44:53 -0700 Subject: [PATCH 0632/1408] 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(-) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index da66b93f2dd..425248224e1 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 -- GitLab From 73458a81e89e0f1955f326d8d29e710a9b4eb746 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 4 Nov 2016 11:23:46 -0600 Subject: [PATCH 0633/1408] Detect non-oneway calls leaving system_server. To protect system stability, any Binder calls leaving the system_server must carefully be performed using FLAG_ONEWAY (or the 'oneway' verb in AIDL) which prevents the call from blocking indefinitely on the remote process. In this CL, the system_server uses the new Binder.setWarnOnBlocking() method to enable detection by default for all remote Binder interfaces. It can also use Binder.allowBlocking() to allow blocking calls on certain remote interfaces that have been determined to be safe. This CL adds the 'oneway' verb to several interfaces and methods where it should have been added, and marks a handful of system ContentProviders as being safe to call into. Also, we assume that any services obtained from ServiceManager are part of the core OS, and are okay to make blocking calls to. Test: builds, boots, runs with minimal logs triggered Bug: 32715088 Change-Id: Ide476e120cb40436a94b7faf7615c943d691f4c0 --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- framework/java/android/bluetooth/BluetoothA2dpSink.java | 3 ++- .../java/android/bluetooth/BluetoothAvrcpController.java | 3 ++- framework/java/android/bluetooth/BluetoothHeadset.java | 3 ++- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 3 ++- framework/java/android/bluetooth/BluetoothHealth.java | 3 ++- framework/java/android/bluetooth/BluetoothInputDevice.java | 3 ++- framework/java/android/bluetooth/BluetoothMap.java | 2 +- framework/java/android/bluetooth/BluetoothPan.java | 3 ++- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 ++- framework/java/android/bluetooth/BluetoothSap.java | 3 ++- framework/java/android/bluetooth/IBluetoothHeadset.aidl | 2 +- .../bluetooth/IBluetoothProfileServiceConnection.aidl | 2 +- .../android/bluetooth/IBluetoothStateChangeCallback.aidl | 2 +- .../android/server/bluetooth/BluetoothManagerService.java | 5 +++-- 15 files changed, 27 insertions(+), 16 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 353c6400ffd..3de159a6cff 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.media.AudioManager; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -572,7 +573,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object connected"); try { mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(service); + mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } finally { mServiceLock.writeLock().unlock(); } diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 74302f27ec1..9dfc4b442fa 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -481,7 +482,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(service); + mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index a395aa470f5..0261b1b35e2 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaMetadata; import android.media.session.PlaybackState; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -284,7 +285,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(service); + mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index f46a3b3c733..28421ebc4cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -1037,7 +1038,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadset.Stub.asInterface(service); + mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_CONNECTED)); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 93790feecd1..c7c64c4391c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -1122,7 +1123,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(service); + mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 4949c24372a..8d77888193b 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -522,7 +523,7 @@ public final class BluetoothHealth implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHealth.Stub.asInterface(service); + mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 252e3d28a25..e3288f3c7c8 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -479,7 +480,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothInputDevice.Stub.asInterface(service); + mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 7f57acf3dc6..2e73051ee61 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -371,7 +371,7 @@ public final class BluetoothMap implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(service); + mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 744f9421b23..2a026a91e8e 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -368,7 +369,7 @@ public final class BluetoothPan implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(service); + mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PAN, diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index eab4c6f5130..9f00e1aaa3a 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; +import android.os.Binder; import android.os.IBinder; import android.util.Log; @@ -288,7 +289,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("Proxy object connected"); } - mService = IBluetoothPbapClient.Stub.asInterface(service); + mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, BluetoothPbapClient.this); } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index e70c936e253..89c1bf8f9aa 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.RemoteException; +import android.os.Binder; import android.os.IBinder; import android.os.ServiceManager; import android.util.Log; @@ -393,7 +394,7 @@ public final class BluetoothSap implements BluetoothProfile { private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(service); + mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 6ad442b6138..dde0ac67bbb 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -54,7 +54,7 @@ interface IBluetoothHeadset { boolean getAudioRouteAllowed(); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); + oneway void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type); boolean enableWBS(); diff --git a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl index 96c59e23915..541583ff553 100755 --- a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl @@ -24,7 +24,7 @@ import android.os.IBinder; * * {@hide} */ -interface IBluetoothProfileServiceConnection { +oneway interface IBluetoothProfileServiceConnection { void onServiceConnected(in ComponentName comp, in IBinder service); void onServiceDisconnected(in ComponentName comp); } diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl index feccdce57b9..0da4e884328 100644 --- a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -21,7 +21,7 @@ package android.bluetooth; * * {@hide} */ -interface IBluetoothStateChangeCallback +oneway interface IBluetoothStateChangeCallback { void onBluetoothStateChange(boolean on); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 64560484858..9b3fac3d66d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1364,7 +1364,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { - mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); + mBluetoothGatt = IBluetoothGatt.Stub + .asInterface(Binder.allowBlocking(service)); onBluetoothGattServiceUp(); break; } // else must be SERVICE_IBLUETOOTH @@ -1374,7 +1375,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBinding = false; mBluetoothBinder = service; - mBluetooth = IBluetooth.Stub.asInterface(service); + mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service)); if (!isNameAndAddressSet()) { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); -- GitLab From ac1c8d563d9441ca5315fef33e24c367a2aea2a0 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 9 Nov 2016 16:51:09 -0800 Subject: [PATCH 0634/1408] BLE OOB Pairing - parse address type (5/5) When address type is not parsed, creating bond to devices not using random address is impossible. Bug: 32780409 Test: try pairing with nRF52DK using random address Change-Id: Ie6cc1f8c008d43b2acd021b47f9bbfb1f63472e8 --- framework/java/android/bluetooth/OobData.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index f53ca94a1d5..9e87230c686 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -30,10 +30,24 @@ import android.util.Log; * @hide */ public class OobData implements Parcelable { + private byte[] leBluetoothDeviceAddress; private byte[] securityManagerTk; private byte[] leSecureConnectionsConfirmation; private byte[] leSecureConnectionsRandom; + public byte[] getLeBluetoothDeviceAddress() { + return leBluetoothDeviceAddress; + } + + /** + * Sets the LE Bluetooth Device Address value to be used during LE pairing. + * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for + * a detailed description. + */ + public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { + this.leBluetoothDeviceAddress = leBluetoothDeviceAddress; + } + public byte[] getSecurityManagerTk() { return securityManagerTk; } @@ -66,6 +80,7 @@ public class OobData implements Parcelable { public OobData() { } private OobData(Parcel in) { + leBluetoothDeviceAddress = in.createByteArray(); securityManagerTk = in.createByteArray(); leSecureConnectionsConfirmation = in.createByteArray(); leSecureConnectionsRandom = in.createByteArray(); @@ -77,6 +92,7 @@ public class OobData implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(leBluetoothDeviceAddress); out.writeByteArray(securityManagerTk); out.writeByteArray(leSecureConnectionsConfirmation); out.writeByteArray(leSecureConnectionsRandom); -- GitLab From cba64756a88e4352493ccbc403a34deca6cbd97d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 28 Jul 2016 05:21:36 -0700 Subject: [PATCH 0635/1408] Add LE Secure Connection data parsing (1/4) Bug: 30460956 Change-Id: I8d6e721b3b04f5ca9e3e02f7f2b90487482e1b37 (cherry picked from commit 1d30636fd774ec548a12df72070a3daef7c96716) --- framework/java/android/bluetooth/OobData.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 8e659e04d70..f53ca94a1d5 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -31,6 +31,8 @@ import android.util.Log; */ public class OobData implements Parcelable { private byte[] securityManagerTk; + private byte[] leSecureConnectionsConfirmation; + private byte[] leSecureConnectionsRandom; public byte[] getSecurityManagerTk() { return securityManagerTk; @@ -45,10 +47,28 @@ public class OobData implements Parcelable { this.securityManagerTk = securityManagerTk; } + public byte[] getLeSecureConnectionsConfirmation() { + return leSecureConnectionsConfirmation; + } + + public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) { + this.leSecureConnectionsConfirmation = leSecureConnectionsConfirmation; + } + + public byte[] getLeSecureConnectionsRandom() { + return leSecureConnectionsRandom; + } + + public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) { + this.leSecureConnectionsRandom = leSecureConnectionsRandom; + } + public OobData() { } private OobData(Parcel in) { securityManagerTk = in.createByteArray(); + leSecureConnectionsConfirmation = in.createByteArray(); + leSecureConnectionsRandom = in.createByteArray(); } public int describeContents() { @@ -58,6 +78,8 @@ public class OobData implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeByteArray(securityManagerTk); + out.writeByteArray(leSecureConnectionsConfirmation); + out.writeByteArray(leSecureConnectionsRandom); } public static final Parcelable.Creator CREATOR -- GitLab From 1d8f1954108cca2e204f0f1fc1a236f41de261a0 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 9 Nov 2016 16:51:09 -0800 Subject: [PATCH 0636/1408] BLE OOB Pairing - parse address type (5/5) When address type is not parsed, creating bond to devices not using random address is impossible. Bug: 32780409 Test: try pairing with nRF52DK using random address Change-Id: Ie6cc1f8c008d43b2acd021b47f9bbfb1f63472e8 (cherry picked from commit ac1c8d563d9441ca5315fef33e24c367a2aea2a0) --- framework/java/android/bluetooth/OobData.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index f53ca94a1d5..9e87230c686 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -30,10 +30,24 @@ import android.util.Log; * @hide */ public class OobData implements Parcelable { + private byte[] leBluetoothDeviceAddress; private byte[] securityManagerTk; private byte[] leSecureConnectionsConfirmation; private byte[] leSecureConnectionsRandom; + public byte[] getLeBluetoothDeviceAddress() { + return leBluetoothDeviceAddress; + } + + /** + * Sets the LE Bluetooth Device Address value to be used during LE pairing. + * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for + * a detailed description. + */ + public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { + this.leBluetoothDeviceAddress = leBluetoothDeviceAddress; + } + public byte[] getSecurityManagerTk() { return securityManagerTk; } @@ -66,6 +80,7 @@ public class OobData implements Parcelable { public OobData() { } private OobData(Parcel in) { + leBluetoothDeviceAddress = in.createByteArray(); securityManagerTk = in.createByteArray(); leSecureConnectionsConfirmation = in.createByteArray(); leSecureConnectionsRandom = in.createByteArray(); @@ -77,6 +92,7 @@ public class OobData implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + out.writeByteArray(leBluetoothDeviceAddress); out.writeByteArray(securityManagerTk); out.writeByteArray(leSecureConnectionsConfirmation); out.writeByteArray(leSecureConnectionsRandom); -- GitLab From 66ed856126f6c78f3e3e61ccdf2ff0881d67966c Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Wed, 9 Nov 2016 12:01:24 -0800 Subject: [PATCH 0637/1408] Bluetooth: BLE app tracking fixes Simplify tracking registered BLE Apps and clear the state when we try to recover bluetooth from starting to ON. Test: enable and disable bluetooth an LE-only app running Bug: 32609235 Change-Id: I3233bf8402a3c8fc4886ef21b1e9411bc78d4e2c --- .../bluetooth/BluetoothManagerService.java | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2a5b1946b90..5b365983f07 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -58,6 +58,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; import java.util.Map; @@ -117,7 +118,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTHGATT = 2; private final Context mContext; - private static int mBleAppCount = 0; // Locks are not provided for mName and mAddress. // They are accessed in handler or broadcast receiver, same thread context. @@ -211,10 +211,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off - synchronized (this) { - mBleAppCount = 0; - mBleApps.clear(); - } + clearBleApps(); if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part try { @@ -438,28 +435,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); - if (mBleAppCount > 0) --mBleAppCount; - - if (mBleAppCount == 0) { - if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null && - mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { - mEnable = false; - mBluetooth.onBrEdrDown(); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); + if (isBleAppPresent()) { + // Nothing to do, another app is here. + return; + } + if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null && + mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mEnable = false; + mBluetooth.onBrEdrDown(); } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } /** Internal death rec list */ - Map mBleApps = new HashMap(); + Map mBleApps = new ConcurrentHashMap(); @Override public boolean isBleScanAlwaysAvailable() { @@ -479,17 +476,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { ContentObserver contentObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { - if (!isBleScanAlwaysAvailable()) { - disableBleScanMode(); - clearBleApps(); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) mBluetooth.onBrEdrDown(); - } catch (RemoteException e) { - Slog.e(TAG, "error when disabling bluetooth", e); - } finally { - mBluetoothLock.readLock().unlock(); - } + if (isBleScanAlwaysAvailable()) { + // Nothing to do + return; + } + // BLE scan is not available. + disableBleScanMode(); + clearBleApps(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) mBluetooth.onBrEdrDown(); + } catch (RemoteException e) { + Slog.e(TAG, "error when disabling bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); } } }; @@ -525,9 +525,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { throw new IllegalArgumentException("Wake lock is already dead."); } mBleApps.put(token, deathRec); - synchronized (this) { - ++mBleAppCount; - } if (DBG) Slog.d(TAG, "Registered for death Notification"); } @@ -537,31 +534,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Unregister death recipient as the app goes away. token.unlinkToDeath(r, 0); mBleApps.remove(token); - synchronized (this) { - if (mBleAppCount > 0) --mBleAppCount; - } if (DBG) Slog.d(TAG, "Unregistered for death Notification"); } } - if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount); - if (mBleAppCount == 0 && mEnable) { + int appCount = mBleApps.size(); + if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); + if (appCount == 0 && mEnable) { disableBleScanMode(); } - return mBleAppCount; + return appCount; } // Clear all apps using BLE scan only mode. private void clearBleApps() { - synchronized (this) { - mBleApps.clear(); - mBleAppCount = 0; - } + mBleApps.clear(); } /** @hide*/ public boolean isBleAppPresent() { - if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount); - return (mBleAppCount > 0); + if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); + return mBleApps.size() > 0; } /** @@ -1417,12 +1409,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(); + recoverBluetoothServiceFromError(false); } if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(); + recoverBluetoothServiceFromError(true); } // If we tried to enable BT while BT was in the process of shutting down, // wait for the BT process to fully tear down and then force a restart @@ -1837,7 +1829,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { quietMode ? 1 : 0, 0)); } - private void recoverBluetoothServiceFromError() { + private void recoverBluetoothServiceFromError(boolean clearBle) { Slog.e(TAG,"recoverBluetoothServiceFromError"); try { mBluetoothLock.readLock().lock(); @@ -1875,6 +1867,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; + if (clearBle) { + clearBleApps(); + } + mEnable = false; if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { -- GitLab From 976e264b589c692ec33ddd82c7b585b2d830bf69 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 4 Nov 2016 15:25:57 -0700 Subject: [PATCH 0638/1408] Bluetooth: advertising improvements This patch removes isPeripheralModeSupported(), hidden public method which is always returning true. It also modify the BluetoothLeAdvertiser to be able to use advertising instance with instance id equal 0. Bug: 30622771 Bug: 24099160 Change-Id: Id31582621dbe56d5c3a8d4ee5cd296af66a5f026 --- .../android/bluetooth/BluetoothAdapter.java | 22 -------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - .../bluetooth/le/BluetoothLeAdvertiser.java | 30 ++++++++----------- 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 11c27355c78..4271e3f99df 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -601,10 +601,6 @@ public final class BluetoothAdapter { */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { if (!getLeAccess()) return null; - if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) { - Log.e(TAG, "Bluetooth LE advertising not supported"); - return null; - } synchronized(mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); @@ -1353,24 +1349,6 @@ public final class BluetoothAdapter { } } - /** - * Returns whether peripheral mode is supported. - * - * @hide - */ - public boolean isPeripheralModeSupported() { - if (getState() != STATE_ON) return false; - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.isPeripheralModeSupported(); - } catch (RemoteException e) { - Log.e(TAG, "failed to get peripheral mode capability: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - /** * Return true if offloaded filters are supported * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 96a1ae8b945..7c5458b7704 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -100,7 +100,6 @@ interface IBluetooth boolean factoryReset(); boolean isMultiAdvertisementSupported(); - boolean isPeripheralModeSupported(); boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 26f2dea9022..5d276623ce1 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -111,12 +111,6 @@ public final class BluetoothLeAdvertiser { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } - if (!mBluetoothAdapter.isMultipleAdvertisementSupported() && - !mBluetoothAdapter.isPeripheralModeSupported()) { - postStartFailure(callback, - AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED); - return; - } boolean isConnectable = settings.isConnectable(); if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) { @@ -236,9 +230,9 @@ public final class BluetoothLeAdvertiser { private final AdvertiseSettings mSettings; private final IBluetoothGatt mBluetoothGatt; - // mAdvertiserId 0: not registered - // -1: advertise stopped or registration timeout - // >0: registered and advertising started + // mAdvertiserId -1: not registered + // -2: advertise stopped or registration timeout + // >=0: registered and advertising started private int mAdvertiserId; private boolean mIsAdvertising = false; @@ -251,12 +245,12 @@ public final class BluetoothLeAdvertiser { mScanResponse = scanResponse; mSettings = settings; mBluetoothGatt = bluetoothGatt; - mAdvertiserId = 0; + mAdvertiserId = -1; } public void startRegisteration() { synchronized (this) { - if (mAdvertiserId == -1) return; + if (mAdvertiserId == -2) return; try { mBluetoothGatt.registerAdvertiser(this); @@ -264,13 +258,13 @@ public final class BluetoothLeAdvertiser { } catch (InterruptedException | RemoteException e) { Log.e(TAG, "Failed to start registeration", e); } - if (mAdvertiserId > 0 && mIsAdvertising) { + if (mAdvertiserId >= 0 && mIsAdvertising) { mLeAdvertisers.put(mAdvertiseCallback, this); - } else if (mAdvertiserId <= 0) { + } else if (mAdvertiserId < 0) { // Registration timeout, reset mClientIf to -1 so no subsequent operations can // proceed. - if (mAdvertiserId == 0) mAdvertiserId = -1; + if (mAdvertiserId == 0) mAdvertiserId = -2; // Post internal error if registration failed. postStartFailure(mAdvertiseCallback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); @@ -278,7 +272,7 @@ public final class BluetoothLeAdvertiser { // Unregister application if it's already registered but advertise failed. try { mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); - mAdvertiserId = -1; + mAdvertiserId = -2; } catch (RemoteException e) { Log.e(TAG, "remote exception when unregistering", e); } @@ -312,7 +306,7 @@ public final class BluetoothLeAdvertiser { synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { - if (mAdvertiserId == -1) { + if (mAdvertiserId == -2) { // Registration succeeds after timeout, unregister advertiser. mBluetoothGatt.unregisterAdvertiser(advertiserId); } else { @@ -326,7 +320,7 @@ public final class BluetoothLeAdvertiser { } } // Registration failed. - mAdvertiserId = -1; + mAdvertiserId = -2; notifyAll(); } } @@ -348,7 +342,7 @@ public final class BluetoothLeAdvertiser { // unregister advertiser for stop. try { mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); - mAdvertiserId = -1; + mAdvertiserId = -2; mIsAdvertising = false; mLeAdvertisers.remove(mAdvertiseCallback); } catch (RemoteException e) { -- GitLab From cb9f281e420c261efb59ede7289c4334f3a440d4 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Wed, 9 Nov 2016 12:01:24 -0800 Subject: [PATCH 0639/1408] Bluetooth: BLE app tracking fixes Simplify tracking registered BLE Apps and clear the state when we try to recover bluetooth from starting to ON. Test: enable and disable bluetooth an LE-only app running Bug: 32609235 Change-Id: I3233bf8402a3c8fc4886ef21b1e9411bc78d4e2c --- .../bluetooth/BluetoothManagerService.java | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 52be811fad1..7bf8c743c8e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -56,6 +56,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; import java.util.Map; @@ -115,7 +116,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int SERVICE_IBLUETOOTHGATT = 2; private final Context mContext; - private static int mBleAppCount = 0; // Locks are not provided for mName and mAddress. // They are accessed in handler or broadcast receiver, same thread context. @@ -207,10 +207,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off - synchronized (this) { - mBleAppCount = 0; - mBleApps.clear(); - } + clearBleApps(); if (st == BluetoothAdapter.STATE_BLE_ON) { //if state is BLE_ON make sure you trigger disableBLE part try { @@ -451,28 +448,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { public void binderDied() { if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); - if (mBleAppCount > 0) --mBleAppCount; - - if (mBleAppCount == 0) { - if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null && - mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { - mEnable = false; - mBluetooth.onBrEdrDown(); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); + if (isBleAppPresent()) { + // Nothing to do, another app is here. + return; + } + if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null && + mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + mEnable = false; + mBluetooth.onBrEdrDown(); } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } } } /** Internal death rec list */ - Map mBleApps = new HashMap(); + Map mBleApps = new ConcurrentHashMap(); @Override public boolean isBleScanAlwaysAvailable() { @@ -492,17 +489,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { ContentObserver contentObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { - if (!isBleScanAlwaysAvailable()) { - disableBleScanMode(); - clearBleApps(); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) mBluetooth.onBrEdrDown(); - } catch (RemoteException e) { - Slog.e(TAG, "error when disabling bluetooth", e); - } finally { - mBluetoothLock.readLock().unlock(); - } + if (isBleScanAlwaysAvailable()) { + // Nothing to do + return; + } + // BLE scan is not available. + disableBleScanMode(); + clearBleApps(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) mBluetooth.onBrEdrDown(); + } catch (RemoteException e) { + Slog.e(TAG, "error when disabling bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); } } }; @@ -538,9 +538,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { throw new IllegalArgumentException("Wake lock is already dead."); } mBleApps.put(token, deathRec); - synchronized (this) { - ++mBleAppCount; - } if (DBG) Slog.d(TAG, "Registered for death Notification"); } @@ -550,31 +547,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Unregister death recipient as the app goes away. token.unlinkToDeath(r, 0); mBleApps.remove(token); - synchronized (this) { - if (mBleAppCount > 0) --mBleAppCount; - } if (DBG) Slog.d(TAG, "Unregistered for death Notification"); } } - if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount); - if (mBleAppCount == 0 && mEnable) { + int appCount = mBleApps.size(); + if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); + if (appCount == 0 && mEnable) { disableBleScanMode(); } - return mBleAppCount; + return appCount; } // Clear all apps using BLE scan only mode. private void clearBleApps() { - synchronized (this) { - mBleApps.clear(); - mBleAppCount = 0; - } + mBleApps.clear(); } /** @hide*/ public boolean isBleAppPresent() { - if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount); - return (mBleAppCount > 0); + if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); + return mBleApps.size() > 0; } /** @@ -1384,12 +1376,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(); + recoverBluetoothServiceFromError(false); } if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(); + recoverBluetoothServiceFromError(true); } // If we tried to enable BT while BT was in the process of shutting down, // wait for the BT process to fully tear down and then force a restart @@ -1805,7 +1797,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { quietMode ? 1 : 0, 0)); } - private void recoverBluetoothServiceFromError() { + private void recoverBluetoothServiceFromError(boolean clearBle) { Slog.e(TAG,"recoverBluetoothServiceFromError"); try { mBluetoothLock.readLock().lock(); @@ -1843,6 +1835,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; + if (clearBle) { + clearBleApps(); + } + mEnable = false; if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { -- GitLab From db12717d7d952920794d82a9a43ece7809f92795 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 22 Nov 2016 12:44:22 -0800 Subject: [PATCH 0640/1408] Bluetooth: report proper advertiser status on error When advertiser is not started because too many advertisers are registered, return proper error code. Bug: 30622771 Test: sl4a ConcurrentBleAdvertisingTest Change-Id: I57384ff30132e9a7ee17dcf191ff89baa7abf1ef --- .../android/bluetooth/le/BluetoothLeAdvertiser.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 5d276623ce1..94d03e533df 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -235,6 +235,7 @@ public final class BluetoothLeAdvertiser { // >=0: registered and advertising started private int mAdvertiserId; private boolean mIsAdvertising = false; + private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR; public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, AdvertiseData advertiseData, AdvertiseData scanResponse, @@ -262,12 +263,11 @@ public final class BluetoothLeAdvertiser { mLeAdvertisers.put(mAdvertiseCallback, this); } else if (mAdvertiserId < 0) { - // Registration timeout, reset mClientIf to -1 so no subsequent operations can + // Registration timeout, reset mClientIf to -2 so no subsequent operations can // proceed. - if (mAdvertiserId == 0) mAdvertiserId = -2; + if (mAdvertiserId == -1) mAdvertiserId = -2; // Post internal error if registration failed. - postStartFailure(mAdvertiseCallback, - AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + postStartFailure(mAdvertiseCallback, registrationError); } else { // Unregister application if it's already registered but advertise failed. try { @@ -318,6 +318,8 @@ public final class BluetoothLeAdvertiser { } catch (RemoteException e) { Log.e(TAG, "failed to start advertising", e); } + } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { + registrationError = status; } // Registration failed. mAdvertiserId = -2; -- GitLab From aae784e455a6cd2654b6ec0977a4ab85fed46810 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Thu, 24 Nov 2016 16:48:37 +0100 Subject: [PATCH 0641/1408] Introduce a new user restriction for disallowing Bluetooth. Only the device owner will be able to set the restriction and the restriction will prevent usage of Bluetooth on the entire device - i.e. in all the users. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.UserRestrictionsTest Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895300 Change-Id: Iae77647b1e48be6b8efbeccdff6a3d468a0e174d --- .../bluetooth/BluetoothManagerService.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5b365983f07..5b38ebfbcde 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,6 +41,7 @@ import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -51,6 +52,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; @@ -176,6 +179,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final UserRestrictionsListener mUserRestrictionsListener = + new UserRestrictionsListener() { + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + final boolean bluetoothDisallowed = + newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + try { + disable(null, true); + } catch (RemoteException e) { + Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", + e); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -612,6 +633,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enableNoAutoConnect() { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); + } + return false; + } + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -637,6 +665,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); + } + return false; + } + if (!callerSystem) { if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "enable(): not allowed for non-active and non system user"); @@ -841,6 +876,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ public void handleOnBootPhase() { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); + UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + if (isBluetoothDisallowed()) { + return; + } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); @@ -1883,6 +1924,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private boolean isBluetoothDisallowed() { + long callingIdentity = Binder.clearCallingIdentity(); + try { + return mContext.getSystemService(UserManager.class) + .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 065a9e81b1d66743f71abd521d4e0ffbeeed0300 Mon Sep 17 00:00:00 2001 From: ltrochtova Date: Thu, 1 Dec 2016 20:50:53 +0000 Subject: [PATCH 0642/1408] Revert "Introduce a new user restriction for disallowing Bluetooth." This reverts commit aae784e455a6cd2654b6ec0977a4ab85fed46810. Change-Id: I5859b262a741d1fe8e8b61e142eecf7d742d8d41 --- .../bluetooth/BluetoothManagerService.java | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5b38ebfbcde..5b365983f07 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,7 +41,6 @@ import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -52,8 +51,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; @@ -179,24 +176,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final UserRestrictionsListener mUserRestrictionsListener = - new UserRestrictionsListener() { - @Override - public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, - Bundle prevRestrictions) { - final boolean bluetoothDisallowed = - newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); - if ((mEnable || mEnableExternal) && bluetoothDisallowed) { - try { - disable(null, true); - } catch (RemoteException e) { - Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", - e); - } - } - } - }; - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -633,13 +612,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enableNoAutoConnect() { - if (isBluetoothDisallowed()) { - if (DBG) { - Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); - } - return false; - } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -665,13 +637,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (isBluetoothDisallowed()) { - if (DBG) { - Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); - } - return false; - } - if (!callerSystem) { if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "enable(): not allowed for non-active and non system user"); @@ -876,12 +841,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ public void handleOnBootPhase() { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); - UserManagerInternal userManagerInternal = - LocalServices.getService(UserManagerInternal.class); - userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - if (isBluetoothDisallowed()) { - return; - } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); @@ -1924,16 +1883,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - private boolean isBluetoothDisallowed() { - long callingIdentity = Binder.clearCallingIdentity(); - try { - return mContext.getSystemService(UserManager.class) - .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 5682fb80c9c84e215def90b2204ff25d2a6ba87b Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 29 Jun 2016 17:31:44 -0700 Subject: [PATCH 0643/1408] Add Bluetooth toggle prompts - framework If permission review is enabled toggling bluetoth on or off results in a user prompt to collect consent. This applies only to legacy apps, i.e. ones that don't support runtime permissions as they target SDK 22. Also added a configuration resource which controls whether permission review mode is enabled. By default it is not and an OEM can change this via an overlay. For now we also keep the old mechanism to toggle review mode via a build property which is still used and will be removed when clients have transitioned. bug:28715749 Change-Id: I94c5828ad6c8aa6b363622a26ff9da4fc2e2fac7 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++- .../android/bluetooth/IBluetoothManager.aidl | 4 +- .../bluetooth/BluetoothManagerService.java | 83 +++++++++++++++---- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4f19dcac160..542b06b1360 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -254,6 +255,29 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + *

        This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + *

        Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

        Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.

        @@ -772,7 +796,7 @@ public final class BluetoothAdapter { return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -899,7 +923,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -931,7 +955,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -949,7 +973,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2b853a373b5..90f008520a8 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable(String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 52be811fad1..a3af5c64301 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,10 +35,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -151,6 +153,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map mProfileServices = new HashMap (); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -244,6 +248,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -654,15 +663,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + public boolean enable(String packageName) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; + } } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); @@ -678,14 +698,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -706,6 +736,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + -- GitLab From 9ef21a8f8a07674d15a2d7a750759330b98b8d2f Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Fri, 2 Dec 2016 12:19:39 +0100 Subject: [PATCH 0644/1408] Introduce a new user restriction for disallowing Bluetooth. Only the device owner will be able to set the restriction and the restriction will prevent usage of Bluetooth on the entire device - i.e. in all the users. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.UserRestrictionsTest Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895300 Merged-In: I2875cf178cb16eca1965d0ba965d1cd3d8db2ad5 Change-Id: I2875cf178cb16eca1965d0ba965d1cd3d8db2ad5 --- .../bluetooth/BluetoothManagerService.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5b365983f07..5b38ebfbcde 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -41,6 +41,7 @@ import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -51,6 +52,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; @@ -176,6 +179,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final UserRestrictionsListener mUserRestrictionsListener = + new UserRestrictionsListener() { + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + final boolean bluetoothDisallowed = + newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + try { + disable(null, true); + } catch (RemoteException e) { + Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", + e); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -612,6 +633,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enableNoAutoConnect() { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); + } + return false; + } + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -637,6 +665,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); + } + return false; + } + if (!callerSystem) { if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "enable(): not allowed for non-active and non system user"); @@ -841,6 +876,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ public void handleOnBootPhase() { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); + UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + if (isBluetoothDisallowed()) { + return; + } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); @@ -1883,6 +1924,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private boolean isBluetoothDisallowed() { + long callingIdentity = Binder.clearCallingIdentity(); + try { + return mContext.getSystemService(UserManager.class) + .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From cea5b6c7bda3e3695c67e585e1e507351fabaa03 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Fri, 2 Dec 2016 12:19:39 +0100 Subject: [PATCH 0645/1408] Introduce a new user restriction for disallowing Bluetooth. Only the device owner will be able to set the restriction and the restriction will prevent usage of Bluetooth on the entire device - i.e. in all the users. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.UserRestrictionsTest Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895300 Merged-In: I2875cf178cb16eca1965d0ba965d1cd3d8db2ad5 Change-Id: I2875cf178cb16eca1965d0ba965d1cd3d8db2ad5 --- .../bluetooth/BluetoothManagerService.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aa6afdfba07..75b2dd494b6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -39,6 +39,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -49,6 +50,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; @@ -172,6 +175,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final UserRestrictionsListener mUserRestrictionsListener = + new UserRestrictionsListener() { + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + final boolean bluetoothDisallowed = + newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + disable(true); + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -621,6 +637,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enableNoAutoConnect() { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); + } + return false; + } + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -643,6 +666,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean enable() { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); + } + return false; + } + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { Slog.w(TAG,"enable(): not allowed for non-active and non system user"); @@ -804,6 +834,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ public void handleOnBootPhase() { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); + UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + if (isBluetoothDisallowed()) { + return; + } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); @@ -1846,6 +1882,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private boolean isBluetoothDisallowed() { + long callingIdentity = Binder.clearCallingIdentity(); + try { + return mContext.getSystemService(UserManager.class) + .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 7d22b5dd649d59644a1fab5cbe86a2f32f9500b3 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Fri, 2 Dec 2016 12:19:39 +0100 Subject: [PATCH 0646/1408] Introduce a new user restriction for disallowing Bluetooth. Only the device owner will be able to set the restriction and the restriction will prevent usage of Bluetooth on the entire device - i.e. in all the users. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.UserRestrictionsTest Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895300 Change-Id: I2875cf178cb16eca1965d0ba965d1cd3d8db2ad5 --- .../bluetooth/BluetoothManagerService.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f7068cf3fb4..b0c560353d8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -42,6 +42,7 @@ import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -52,6 +53,8 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; @@ -177,6 +180,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final UserRestrictionsListener mUserRestrictionsListener = + new UserRestrictionsListener() { + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + final boolean bluetoothDisallowed = + newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + try { + disable(null, true); + } catch (RemoteException e) { + Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", + e); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -634,6 +655,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean enableNoAutoConnect() { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); + } + return false; + } + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -659,6 +687,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); + } + return false; + } + if (!callerSystem) { if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "enable(): not allowed for non-active and non system user"); @@ -872,6 +907,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ public void handleOnBootPhase() { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); + UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + if (isBluetoothDisallowed()) { + return; + } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); @@ -1916,6 +1957,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private boolean isBluetoothDisallowed() { + long callingIdentity = Binder.clearCallingIdentity(); + try { + return mContext.getSystemService(UserManager.class) + .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 45b8ffba5f29180a91f0d13429135d8a608419e6 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 29 Jun 2016 17:31:44 -0700 Subject: [PATCH 0647/1408] DO NOT MERGE Add Bluetooth toggle prompts - framework If permission review is enabled toggling bluetoth on or off results in a user prompt to collect consent. This applies only to legacy apps, i.e. ones that don't support runtime permissions as they target SDK 22. Also added a configuration resource which controls whether permission review mode is enabled. By default it is not and an OEM can change this via an overlay. For now we also keep the old mechanism to toggle review mode via a build property which is still used and will be removed when clients have transitioned. bug:28715749 Change-Id: I77bca2305f9d0f20034b2c8fc5b58e0565d5e617 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++- .../android/bluetooth/IBluetoothManager.aidl | 4 +- .../bluetooth/BluetoothManagerService.java | 90 +++++++++++++++---- 3 files changed, 105 insertions(+), 21 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d419d0384f5..61544e4f927 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -254,6 +255,29 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + *

        This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + *

        Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

        Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.

        @@ -768,7 +792,7 @@ public final class BluetoothAdapter { return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -895,7 +919,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -927,7 +951,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -945,7 +969,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2b853a373b5..2ab9ae80b44 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable( String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 75b2dd494b6..5cf9eead5de 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,11 +35,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; import android.os.Bundle; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -154,6 +156,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map mProfileServices = new HashMap (); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -183,7 +187,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { - disable(true); + try { + disable("android.os.UserManagerInternal", true); + } catch (RemoteException e) { + // Shouldn't happen: startConsentUiIfNeeded not called + // when from system. + } } } }; @@ -257,6 +266,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -665,7 +679,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable() { + public boolean enable(String packageName) throws RemoteException { if (isBluetoothDisallowed()) { if (DBG) { Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); @@ -673,14 +687,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; + } } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); @@ -696,14 +721,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -724,6 +759,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + -- GitLab From 875b7834c4ce6fa7e4debaecd55e7ec6c8ba139f Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Tue, 13 Dec 2016 20:27:15 +0000 Subject: [PATCH 0648/1408] Really resolve merge conflicts of 5f4cb93 to nyc-mr2-dev-plus-aosp Test: Treehugger Change-Id: Idd62dc36b4257f31148b29dec19a2785043e3d11 --- .../bluetooth/BluetoothManagerService.java | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 71697286512..7cc8997968c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; @@ -155,6 +156,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map mProfileServices = new HashMap (); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -263,6 +266,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -717,14 +725,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -745,6 +763,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + -- GitLab From d200bbfbf8914fc20853a4d3e172b9d4a1deebe4 Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Tue, 13 Dec 2016 20:36:04 +0000 Subject: [PATCH 0649/1408] Finish resolving conflict from 715cbb4 Test: Treehugger Change-Id: If7fa9f7e300f3d810e41447369ccbbfbc3963b94 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++++++++++++--- .../android/bluetooth/IBluetoothManager.aidl | 4 +-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0ad46e6f239..b0e27a4ab54 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -254,6 +255,29 @@ public final class BluetoothAdapter { public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; + /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + *

        This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + *

        Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * resultCode + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + *

        Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.

        @@ -768,7 +792,7 @@ public final class BluetoothAdapter { return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -895,7 +919,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -927,7 +951,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -945,7 +969,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2b853a373b5..90f008520a8 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable(String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); -- GitLab From f46373a3b2c3218fa95b0cb4cb8fd9c2c605df16 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 13 Dec 2016 10:51:02 -0800 Subject: [PATCH 0650/1408] Bluetooth: log message improvements Some log improvements: - Reduce logspam - Use names for states in logs instead of numbers - Be more consistent with messages Also remove some commented out dead code. Test: run on phone, observe more useful logs Change-Id: I32163278e148be144c03d4e8aaf0eb761226c94c --- .../android/bluetooth/BluetoothAdapter.java | 97 +++---- .../bluetooth/BluetoothManagerService.java | 239 +++++++++--------- 2 files changed, 150 insertions(+), 186 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 61544e4f927..4a97b078f7b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -200,6 +200,23 @@ public final class BluetoothAdapter { */ public static final int STATE_BLE_TURNING_OFF = 16; + /** + * Human-readable string helper for AdapterState + * @hide + */ + public static String nameForState(@AdapterState int state) { + switch(state) { + case STATE_OFF: return "OFF"; + case STATE_TURNING_ON: return "TURNING_ON"; + case STATE_ON: return "ON"; + case STATE_TURNING_OFF: return "TURNING_OFF"; + case STATE_BLE_TURNING_ON: return "BLE_TURNING_ON"; + case STATE_BLE_ON: return "BLE_ON"; + case STATE_BLE_TURNING_OFF: return "BLE_TURNING_OFF"; + default: return "?!?!? (" + state + ")"; + } + } + /** * Activity Action: Show a system activity that requests discoverable mode. * This activity will also request the user to turn on Bluetooth if it @@ -658,15 +675,8 @@ public final class BluetoothAdapter { @SystemApi public boolean isLeEnabled() { final int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON"); - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - } else { - if (DBG) Log.d (TAG, "STATE_OFF"); - return false; - } - return true; + if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); + return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); } /** @@ -831,10 +841,10 @@ public final class BluetoothAdapter { if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + if (VDBG) Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); state = BluetoothAdapter.STATE_OFF; } - if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(state)); return state; } @@ -871,12 +881,12 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } - if (VDBG) Log.d(TAG,"getLeState() returning " + state); + if (VDBG) Log.d(TAG,"getLeState() returning " + BluetoothAdapter.nameForState(state)); return state; } boolean getLeAccess() { - if(getLeState() == STATE_ON) + if (getLeState() == STATE_ON) return true; else if (getLeState() == STATE_BLE_ON) @@ -914,8 +924,8 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { - if (isEnabled() == true) { - if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); + if (isEnabled()) { + if (DBG) Log.d(TAG, "enable(): BT already enabled!"); return true; } try { @@ -1518,8 +1528,9 @@ public final class BluetoothAdapter { } } } - } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);} - + } catch (RemoteException e) { + Log.e(TAG, "getSupportedProfiles:", e); + } return supportedProfiles; } @@ -1870,34 +1881,6 @@ public final class BluetoothAdapter { * @hide */ public Pair readOutOfBandData() { - if (getState() != STATE_ON) return null; - //TODO(BT - /* - try { - byte[] hash; - byte[] randomizer; - - byte[] ret = null; - mServiceLock.readLock().lock(); - if (mService != null) mService.readOutOfBandData(); - - if (ret == null || ret.length != 32) return null; - - hash = Arrays.copyOfRange(ret, 0, 16); - randomizer = Arrays.copyOfRange(ret, 16, 32); - - if (DBG) { - Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) + - ":" + Arrays.toString(randomizer)); - } - return new Pair(hash, randomizer); - - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - */ return null; } @@ -2051,7 +2034,7 @@ public final class BluetoothAdapter { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + Log.d(TAG, "onBluetoothServiceUp: cb is null!"); } } catch (Exception e) { Log.e(TAG,"",e); @@ -2079,7 +2062,7 @@ public final class BluetoothAdapter { if (cb != null) { cb.onBluetoothServiceDown(); } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); } } catch (Exception e) { Log.e(TAG,"",e); @@ -2089,7 +2072,7 @@ public final class BluetoothAdapter { } public void onBrEdrDown() { - if (VDBG) Log.i(TAG, "on QBrEdrDown: "); + if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService); } }; @@ -2100,7 +2083,7 @@ public final class BluetoothAdapter { */ public boolean enableNoAutoConnect() { if (isEnabled() == true){ - if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!"); + if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); return true; } try { @@ -2140,22 +2123,6 @@ public final class BluetoothAdapter { */ public boolean changeApplicationBluetoothState(boolean on, BluetoothStateChangeCallback callback) { - if (callback == null) return false; - - //TODO(BT) - /* - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.changeApplicationBluetoothState(on, new - StateChangeCallbackWrapper(callback), new Binder()); - } - } catch (RemoteException e) { - Log.e(TAG, "changeBluetoothState", e); - } finally { - mServiceLock.readLock().unlock(); - } - */ return false; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5cf9eead5de..417531217b7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -104,6 +104,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; + private static final int MAX_SAVE_RETRIES = 3; private static final int MAX_ERROR_RESTART_RETRIES = 6; @@ -228,7 +229,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - Slog.d(TAG, "state" + st); + Slog.d(TAG, "State " + BluetoothAdapter.nameForState(st)); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off @@ -422,6 +423,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (callback == null) { + Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); + return; + } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; mHandler.sendMessage(msg); @@ -430,6 +435,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (callback == null) { + Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); + return; + } Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; mHandler.sendMessage(msg); @@ -456,7 +465,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public int getState() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "getState(): not allowed for non-active and non system user"); + Slog.w(TAG, "getState(): report OFF for non-active and non system user"); return BluetoothAdapter.STATE_OFF; } @@ -708,7 +717,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding + " mState = " + mState); + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); } synchronized(mReceiver) { @@ -787,7 +797,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + - " mBinding = " + mBinding); + " mBinding = " + mBinding + " mUnbinding = " + mUnbinding); } try { @@ -803,16 +813,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException re) { Slog.e(TAG, "Unable to unregister BluetoothCallback",re); } - - if (DBG) Slog.d(TAG, "Sending unbind request."); mBluetoothBinder = null; mBluetooth = null; - //Unbind mContext.unbindService(mConnection); mUnbinding = false; mBinding = false; } else { - mUnbinding=false; + mUnbinding = false; } mBluetoothGatt = null; } finally { @@ -1088,7 +1095,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is up */ private void sendBluetoothServiceUpCallback() { - if (DBG) Slog.d(TAG,"Calling onBluetoothServiceUp callbacks"); try { int n = mCallbacks.beginBroadcast(); Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers."); @@ -1107,7 +1113,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is down */ private void sendBluetoothServiceDownCallback() { - if (DBG) Slog.d(TAG,"Calling onBluetoothServiceDown callbacks"); try { int n = mCallbacks.beginBroadcast(); Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers."); @@ -1179,34 +1184,33 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private class BluetoothServiceConnection implements ServiceConnection { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + className.getClassName()); + public void onServiceConnected(ComponentName componentName, IBinder service) { + String name = componentName.getClassName(); + if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); - // TBD if (className.getClassName().equals(IBluetooth.class.getName())) { - if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + if (name.equals("com.android.bluetooth.btservice.AdapterService")) { msg.arg1 = SERVICE_IBLUETOOTH; - // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) { - } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + } else if (name.equals("com.android.bluetooth.gatt.GattService")) { msg.arg1 = SERVICE_IBLUETOOTHGATT; } else { - Slog.e(TAG, "Unknown service connected: " + className.getClassName()); + Slog.e(TAG, "Unknown service connected: " + name); return; } msg.obj = service; mHandler.sendMessage(msg); } - public void onServiceDisconnected(ComponentName className) { - // Called if we unexpected disconnected. - if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + - className.getClassName()); + public void onServiceDisconnected(ComponentName componentName) { + // Called if we unexpectedly disconnect. + String name = componentName.getClassName(); + if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); - if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + if (name.equals("com.android.bluetooth.btservice.AdapterService")) { msg.arg1 = SERVICE_IBLUETOOTH; - } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + } else if (name.equals("com.android.bluetooth.gatt.GattService")) { msg.arg1 = SERVICE_IBLUETOOTHGATT; } else { - Slog.e(TAG, "Unknown service disconnected: " + className.getClassName()); + Slog.e(TAG, "Unknown service disconnected: " + name); return; } mHandler.sendMessage(msg); @@ -1224,7 +1228,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void handleMessage(Message msg) { - if (DBG) Slog.d (TAG, "Message: " + msg.what); switch (msg.what) { case MESSAGE_GET_NAME_AND_ADDRESS: if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); @@ -1262,7 +1265,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1312,6 +1315,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_DISABLE: + if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { waitForOnOff(true, false); @@ -1327,31 +1331,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_REGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - boolean added = mCallbacks.register(callback); - Slog.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added ); - } + mCallbacks.register(callback); break; + } case MESSAGE_UNREGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - boolean removed = mCallbacks.unregister(callback); - Slog.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed); + mCallbacks.unregister(callback); break; } case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; - if (callback != null) { - mStateChangeCallbacks.register(callback); - } + mStateChangeCallbacks.register(callback); break; } case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; - if (callback != null) { - mStateChangeCallbacks.unregister(callback); - } + mStateChangeCallbacks.unregister(callback); break; } case MESSAGE_ADD_PROXY_DELAYED: @@ -1424,13 +1422,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { if (mQuietEnable == false) { - if(!mBluetooth.enable()) { + if (!mBluetooth.enable()) { Slog.e(TAG,"IBluetooth.enable() returned false"); } - } - else - { - if(!mBluetooth.enableNoAutoConnect()) { + } else { + if (!mBluetooth.enableNoAutoConnect()) { Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); } } @@ -1448,19 +1444,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; } - case MESSAGE_TIMEOUT_BIND: { - Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); - mBluetoothLock.writeLock().lock(); - mBinding = false; - mBluetoothLock.writeLock().unlock(); - - break; - } case MESSAGE_BLUETOOTH_STATE_CHANGE: { int prevState = msg.arg1; int newState = msg.arg2; - if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState); + if (DBG) { + Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " + + BluetoothAdapter.nameForState(newState)); + } mState = newState; bluetoothStateChangeHandler(prevState, newState); // handle error state transition case from TURNING_ON to OFF @@ -1500,7 +1491,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { - Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); + Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")"); try { mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTH) { @@ -1511,7 +1502,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothGatt = null; break; } else { - Slog.e(TAG, "Bad msg.arg1: " + msg.arg1); + Slog.e(TAG, "Unknown argument for service disconnect!"); break; } } finally { @@ -1548,8 +1539,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_RESTART_BLUETOOTH_SERVICE: { - Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:" - +" Restart IBluetooth service"); + Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE"); /* Enable without persisting the setting as it doesnt change when IBluetooth service restarts */ @@ -1557,7 +1547,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { handleEnable(mQuietEnable); break; } - + case MESSAGE_TIMEOUT_BIND: { + Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); + mBluetoothLock.writeLock().lock(); + mBinding = false; + mBluetoothLock.writeLock().unlock(); + break; + } case MESSAGE_TIMEOUT_UNBIND: { Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); @@ -1643,11 +1639,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; - // if user is switched when service is being binding - // delay sending MESSAGE_USER_SWITCHED + // if user is switched when service is binding retry after a delay mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS); if (DBG) { - Slog.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2); + Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2); } } break; @@ -1748,7 +1743,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { parentUser == foregroundUser || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; - if (DBG) { + if (DBG && !valid) { Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser + " parentUser=" + parentUser @@ -1761,7 +1756,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendBleStateChanged(int prevState, int newState) { - if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState); + if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + + " > " + BluetoothAdapter.nameForState(newState)); // Send broadcast message to everyone else Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); @@ -1772,75 +1768,76 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void bluetoothStateChangeHandler(int prevState, int newState) { boolean isStandardBroadcast = true; - if (prevState != newState) { - //Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_BLE_ON || - newState == BluetoothAdapter.STATE_OFF) { - boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF - && newState == BluetoothAdapter.STATE_BLE_ON); - - if (newState == BluetoothAdapter.STATE_OFF) { - // If Bluetooth is off, send service down event to proxy objects, and unbind - if (DBG) Slog.d(TAG, "Bluetooth is complete turn off"); - sendBluetoothServiceDownCallback(); - unbindAndFinish(); - sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; - - } else if (!intermediate_off) { - // connect to GattService - if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); - if (mBluetoothGatt != null) { - if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); - onBluetoothGattServiceUp(); - } else { - if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); - } - } - sendBleStateChanged(prevState, newState); - //Don't broadcase this as std intent - isStandardBroadcast = false; - - } else if (intermediate_off){ - if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); - // For LE only mode, broadcast as is - sendBleStateChanged(prevState, newState); - sendBluetoothStateCallback(false); // BT is OFF for general users - // Broadcast as STATE_OFF - newState = BluetoothAdapter.STATE_OFF; - sendBrEdrDownCallback(); - } - } else if (newState == BluetoothAdapter.STATE_ON) { - boolean isUp = (newState==BluetoothAdapter.STATE_ON); - sendBluetoothStateCallback(isUp); + if (prevState == newState) { // No change. Nothing to do. + return; + } + // Notify all proxy objects first of adapter state change + if (newState == BluetoothAdapter.STATE_BLE_ON || + newState == BluetoothAdapter.STATE_OFF) { + boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF + && newState == BluetoothAdapter.STATE_BLE_ON); + + if (newState == BluetoothAdapter.STATE_OFF) { + // If Bluetooth is off, send service down event to proxy objects, and unbind + if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down"); + sendBluetoothServiceDownCallback(); + unbindAndFinish(); sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || - newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + } else if (!intermediate_off) { + // connect to GattService + if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); + if (mBluetoothGatt != null) { + if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); + onBluetoothGattServiceUp(); + } else { + if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH_LE)) { + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); + } + } sendBleStateChanged(prevState, newState); + //Don't broadcase this as std intent isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_TURNING_ON || - newState == BluetoothAdapter.STATE_TURNING_OFF) { + } else if (intermediate_off) { + if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); + // For LE only mode, broadcast as is sendBleStateChanged(prevState, newState); + sendBluetoothStateCallback(false); // BT is OFF for general users + // Broadcast as STATE_OFF + newState = BluetoothAdapter.STATE_OFF; + sendBrEdrDownCallback(); } + } else if (newState == BluetoothAdapter.STATE_ON) { + boolean isUp = (newState == BluetoothAdapter.STATE_ON); + sendBluetoothStateCallback(isUp); + sendBleStateChanged(prevState, newState); - if (isStandardBroadcast) { - if (prevState == BluetoothAdapter.STATE_BLE_ON) { - // Show prevState of BLE_ON as OFF to standard users - prevState = BluetoothAdapter.STATE_OFF; - } - Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || + newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + sendBleStateChanged(prevState, newState); + isStandardBroadcast = false; + + } else if (newState == BluetoothAdapter.STATE_TURNING_ON || + newState == BluetoothAdapter.STATE_TURNING_OFF) { + sendBleStateChanged(prevState, newState); + } + + if (isStandardBroadcast) { + if (prevState == BluetoothAdapter.STATE_BLE_ON) { + // Show prevState of BLE_ON as OFF to standard users + prevState = BluetoothAdapter.STATE_OFF; } + Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); } } -- GitLab From fbccc787db465e1fac9f23a068811862a44c1171 Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Mon, 5 Dec 2016 16:46:52 +0000 Subject: [PATCH 0651/1408] In permission review mode, always request user's consent to toggle BT. Bug: 33155221 Change-Id: Ic1ed987bbaf282892a2aef354a8ca0add2c829ba --- .../bluetooth/BluetoothManagerService.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index a3af5c64301..bfbd8cb38d8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -676,9 +676,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (!isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + if (!isEnabled() && mPermissionReviewRequired) { + startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE); return false; } } @@ -711,9 +710,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + if (isEnabled() && mPermissionReviewRequired) { + startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE); return false; } } @@ -736,8 +734,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - private boolean startConsentUiIfNeeded(String packageName, - int callingUid, String intentAction) throws RemoteException { + private void startConsentUi(String packageName, int callingUid, String intentAction) + throws RemoteException { try { // Validate the package only if we are going to use it ApplicationInfo applicationInfo = mContext.getPackageManager() @@ -749,16 +747,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + " not in uid " + callingUid); } - // Legacy apps in permission review mode trigger a user prompt - if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { - Intent intent = new Intent(intentAction); - mContext.startActivity(intent); - return true; - } + // Permission review mode, trigger a user prompt + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); } catch (PackageManager.NameNotFoundException e) { throw new RemoteException(e.getMessage()); } - return false; } public void unbindAndFinish() { -- GitLab From 84109761aacb89103670e871d28e1bb8478843a7 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 13 Dec 2016 10:51:02 -0800 Subject: [PATCH 0652/1408] Bluetooth: log message improvements Some log improvements: - Reduce logspam - Use names for states in logs instead of numbers - Be more consistent with messages Also remove some commented out dead code. Test: run on phone, observe more useful logs Change-Id: I32163278e148be144c03d4e8aaf0eb761226c94c --- .../android/bluetooth/BluetoothAdapter.java | 97 +++---- .../bluetooth/BluetoothManagerService.java | 240 +++++++++--------- 2 files changed, 150 insertions(+), 187 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 542b06b1360..5ced03b2c2d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -200,6 +200,23 @@ public final class BluetoothAdapter { */ public static final int STATE_BLE_TURNING_OFF = 16; + /** + * Human-readable string helper for AdapterState + * @hide + */ + public static String nameForState(@AdapterState int state) { + switch(state) { + case STATE_OFF: return "OFF"; + case STATE_TURNING_ON: return "TURNING_ON"; + case STATE_ON: return "ON"; + case STATE_TURNING_OFF: return "TURNING_OFF"; + case STATE_BLE_TURNING_ON: return "BLE_TURNING_ON"; + case STATE_BLE_ON: return "BLE_ON"; + case STATE_BLE_TURNING_OFF: return "BLE_TURNING_OFF"; + default: return "?!?!? (" + state + ")"; + } + } + /** * Activity Action: Show a system activity that requests discoverable mode. * This activity will also request the user to turn on Bluetooth if it @@ -662,15 +679,8 @@ public final class BluetoothAdapter { @SystemApi public boolean isLeEnabled() { final int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON"); - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - } else { - if (DBG) Log.d (TAG, "STATE_OFF"); - return false; - } - return true; + if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); + return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); } /** @@ -835,10 +845,10 @@ public final class BluetoothAdapter { if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) Log.d(TAG, "Consider internal state as OFF"); + if (VDBG) Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); state = BluetoothAdapter.STATE_OFF; } - if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state); + if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(state)); return state; } @@ -875,12 +885,12 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } - if (VDBG) Log.d(TAG,"getLeState() returning " + state); + if (VDBG) Log.d(TAG,"getLeState() returning " + BluetoothAdapter.nameForState(state)); return state; } boolean getLeAccess() { - if(getLeState() == STATE_ON) + if (getLeState() == STATE_ON) return true; else if (getLeState() == STATE_BLE_ON) @@ -918,8 +928,8 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { - if (isEnabled() == true) { - if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); + if (isEnabled()) { + if (DBG) Log.d(TAG, "enable(): BT already enabled!"); return true; } try { @@ -1540,8 +1550,9 @@ public final class BluetoothAdapter { } } } - } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);} - + } catch (RemoteException e) { + Log.e(TAG, "getSupportedProfiles:", e); + } return supportedProfiles; } @@ -1892,34 +1903,6 @@ public final class BluetoothAdapter { * @hide */ public Pair readOutOfBandData() { - if (getState() != STATE_ON) return null; - //TODO(BT - /* - try { - byte[] hash; - byte[] randomizer; - - byte[] ret = null; - mServiceLock.readLock().lock(); - if (mService != null) mService.readOutOfBandData(); - - if (ret == null || ret.length != 32) return null; - - hash = Arrays.copyOfRange(ret, 0, 16); - randomizer = Arrays.copyOfRange(ret, 16, 32); - - if (DBG) { - Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) + - ":" + Arrays.toString(randomizer)); - } - return new Pair(hash, randomizer); - - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - */ return null; } @@ -2066,7 +2049,7 @@ public final class BluetoothAdapter { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!!!"); + Log.d(TAG, "onBluetoothServiceUp: cb is null!"); } } catch (Exception e) { Log.e(TAG,"",e); @@ -2094,7 +2077,7 @@ public final class BluetoothAdapter { if (cb != null) { cb.onBluetoothServiceDown(); } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!!!"); + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); } } catch (Exception e) { Log.e(TAG,"",e); @@ -2104,7 +2087,7 @@ public final class BluetoothAdapter { } public void onBrEdrDown() { - if (DBG) Log.i(TAG, "onBrEdrDown:"); + if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService); } }; @@ -2115,7 +2098,7 @@ public final class BluetoothAdapter { */ public boolean enableNoAutoConnect() { if (isEnabled() == true){ - if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!"); + if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); return true; } try { @@ -2155,22 +2138,6 @@ public final class BluetoothAdapter { */ public boolean changeApplicationBluetoothState(boolean on, BluetoothStateChangeCallback callback) { - if (callback == null) return false; - - //TODO(BT) - /* - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.changeApplicationBluetoothState(on, new - StateChangeCallbackWrapper(callback), new Binder()); - } - } catch (RemoteException e) { - Log.e(TAG, "changeBluetoothState", e); - } finally { - mServiceLock.readLock().unlock(); - } - */ return false; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index dbb8e0f44ba..b2e38d86757 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -101,6 +101,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; + private static final int MAX_SAVE_RETRIES = 3; private static final int MAX_ERROR_RESTART_RETRIES = 6; @@ -207,7 +208,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - Slog.d(TAG, "Airplane Mode change - current state: " + st); + Slog.d(TAG, "State " + BluetoothAdapter.nameForState(st)); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off @@ -405,6 +406,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (callback == null) { + Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); + return; + } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; mHandler.sendMessage(msg); @@ -413,6 +418,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (callback == null) { + Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); + return; + } Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; mHandler.sendMessage(msg); @@ -439,7 +448,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public int getState() { if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "getState(): not allowed for non-active and non system user"); + Slog.w(TAG, "getState(): report OFF for non-active and non system user"); return BluetoothAdapter.STATE_OFF; } @@ -677,7 +686,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding + " mState = " + mState); + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); } synchronized(mReceiver) { @@ -756,7 +766,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + - " mBinding = " + mBinding); + " mBinding = " + mBinding + " mUnbinding = " + mUnbinding); } try { @@ -772,16 +782,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } catch (RemoteException re) { Slog.e(TAG, "Unable to unregister BluetoothCallback",re); } - - if (DBG) Slog.d(TAG, "Sending unbind request."); mBluetoothBinder = null; mBluetooth = null; - //Unbind mContext.unbindService(mConnection); mUnbinding = false; mBinding = false; } else { - mUnbinding=false; + mUnbinding = false; } mBluetoothGatt = null; } finally { @@ -1051,7 +1058,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is up */ private void sendBluetoothServiceUpCallback() { - if (DBG) Slog.d(TAG,"Calling onBluetoothServiceUp callbacks"); try { int n = mCallbacks.beginBroadcast(); Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers."); @@ -1070,7 +1076,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is down */ private void sendBluetoothServiceDownCallback() { - if (DBG) Slog.d(TAG,"Calling onBluetoothServiceDown callbacks"); try { int n = mCallbacks.beginBroadcast(); Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers."); @@ -1142,34 +1147,33 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private class BluetoothServiceConnection implements ServiceConnection { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + className.getClassName()); + public void onServiceConnected(ComponentName componentName, IBinder service) { + String name = componentName.getClassName(); + if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); - // TBD if (className.getClassName().equals(IBluetooth.class.getName())) { - if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + if (name.equals("com.android.bluetooth.btservice.AdapterService")) { msg.arg1 = SERVICE_IBLUETOOTH; - // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) { - } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + } else if (name.equals("com.android.bluetooth.gatt.GattService")) { msg.arg1 = SERVICE_IBLUETOOTHGATT; } else { - Slog.e(TAG, "Unknown service connected: " + className.getClassName()); + Slog.e(TAG, "Unknown service connected: " + name); return; } msg.obj = service; mHandler.sendMessage(msg); } - public void onServiceDisconnected(ComponentName className) { - // Called if we unexpected disconnected. - if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + - className.getClassName()); + public void onServiceDisconnected(ComponentName componentName) { + // Called if we unexpectedly disconnect. + String name = componentName.getClassName(); + if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); - if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + if (name.equals("com.android.bluetooth.btservice.AdapterService")) { msg.arg1 = SERVICE_IBLUETOOTH; - } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + } else if (name.equals("com.android.bluetooth.gatt.GattService")) { msg.arg1 = SERVICE_IBLUETOOTHGATT; } else { - Slog.e(TAG, "Unknown service disconnected: " + className.getClassName()); + Slog.e(TAG, "Unknown service disconnected: " + name); return; } mHandler.sendMessage(msg); @@ -1187,7 +1191,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void handleMessage(Message msg) { - if (DBG) Slog.d (TAG, "Message: " + msg.what); switch (msg.what) { case MESSAGE_GET_NAME_AND_ADDRESS: if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); @@ -1225,7 +1228,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1275,6 +1278,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_DISABLE: + if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { waitForOnOff(true, false); @@ -1290,31 +1294,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_REGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - boolean added = mCallbacks.register(callback); - Slog.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added ); - } + mCallbacks.register(callback); break; + } case MESSAGE_UNREGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - boolean removed = mCallbacks.unregister(callback); - Slog.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed); + mCallbacks.unregister(callback); break; } case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; - if (callback != null) { - mStateChangeCallbacks.register(callback); - } + mStateChangeCallbacks.register(callback); break; } case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; - if (callback != null) { - mStateChangeCallbacks.unregister(callback); - } + mStateChangeCallbacks.unregister(callback); break; } case MESSAGE_ADD_PROXY_DELAYED: @@ -1387,13 +1385,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { if (mQuietEnable == false) { - if(!mBluetooth.enable()) { + if (!mBluetooth.enable()) { Slog.e(TAG,"IBluetooth.enable() returned false"); } - } - else - { - if(!mBluetooth.enableNoAutoConnect()) { + } else { + if (!mBluetooth.enableNoAutoConnect()) { Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); } } @@ -1411,19 +1407,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; } - case MESSAGE_TIMEOUT_BIND: { - Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); - mBluetoothLock.writeLock().lock(); - mBinding = false; - mBluetoothLock.writeLock().unlock(); - - break; - } case MESSAGE_BLUETOOTH_STATE_CHANGE: { int prevState = msg.arg1; int newState = msg.arg2; - if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState =" + newState); + if (DBG) { + Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " + + BluetoothAdapter.nameForState(newState)); + } mState = newState; bluetoothStateChangeHandler(prevState, newState); // handle error state transition case from TURNING_ON to OFF @@ -1463,7 +1454,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { - Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); + Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")"); try { mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTH) { @@ -1474,7 +1465,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothGatt = null; break; } else { - Slog.e(TAG, "Bad msg.arg1: " + msg.arg1); + Slog.e(TAG, "Unknown argument for service disconnect!"); break; } } finally { @@ -1511,8 +1502,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_RESTART_BLUETOOTH_SERVICE: { - Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:" - +" Restart IBluetooth service"); + Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE"); /* Enable without persisting the setting as it doesnt change when IBluetooth service restarts */ @@ -1520,7 +1510,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { handleEnable(mQuietEnable); break; } - + case MESSAGE_TIMEOUT_BIND: { + Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); + mBluetoothLock.writeLock().lock(); + mBinding = false; + mBluetoothLock.writeLock().unlock(); + break; + } case MESSAGE_TIMEOUT_UNBIND: { Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); @@ -1606,11 +1602,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; - // if user is switched when service is being binding - // delay sending MESSAGE_USER_SWITCHED + // if user is switched when service is binding retry after a delay mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS); if (DBG) { - Slog.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2); + Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2); } } break; @@ -1711,7 +1706,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { parentUser == foregroundUser || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; - if (DBG) { + if (DBG && !valid) { Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser + " parentUser=" + parentUser @@ -1724,7 +1719,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendBleStateChanged(int prevState, int newState) { - if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState); + if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + + " > " + BluetoothAdapter.nameForState(newState)); // Send broadcast message to everyone else Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); @@ -1735,76 +1731,76 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void bluetoothStateChangeHandler(int prevState, int newState) { boolean isStandardBroadcast = true; - if (DBG) Slog.d(TAG, "bluetoothStateChangeHandler: " + prevState + " -> " + newState); - if (prevState != newState) { - //Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_BLE_ON || - newState == BluetoothAdapter.STATE_OFF) { - boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF - && newState == BluetoothAdapter.STATE_BLE_ON); - - if (newState == BluetoothAdapter.STATE_OFF) { - // If Bluetooth is off, send service down event to proxy objects, and unbind - if (DBG) Slog.d(TAG, "Bluetooth is complete turn off"); - sendBluetoothServiceDownCallback(); - unbindAndFinish(); - sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; - - } else if (!intermediate_off) { - // connect to GattService - if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); - if (mBluetoothGatt != null) { - if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); - onBluetoothGattServiceUp(); - } else { - if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); - } - } - sendBleStateChanged(prevState, newState); - //Don't broadcase this as std intent - isStandardBroadcast = false; - - } else if (intermediate_off){ - if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); - // For LE only mode, broadcast as is - sendBleStateChanged(prevState, newState); - sendBluetoothStateCallback(false); // BT is OFF for general users - // Broadcast as STATE_OFF - newState = BluetoothAdapter.STATE_OFF; - sendBrEdrDownCallback(); - } - } else if (newState == BluetoothAdapter.STATE_ON) { - boolean isUp = (newState==BluetoothAdapter.STATE_ON); - sendBluetoothStateCallback(isUp); + if (prevState == newState) { // No change. Nothing to do. + return; + } + // Notify all proxy objects first of adapter state change + if (newState == BluetoothAdapter.STATE_BLE_ON || + newState == BluetoothAdapter.STATE_OFF) { + boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF + && newState == BluetoothAdapter.STATE_BLE_ON); + + if (newState == BluetoothAdapter.STATE_OFF) { + // If Bluetooth is off, send service down event to proxy objects, and unbind + if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down"); + sendBluetoothServiceDownCallback(); + unbindAndFinish(); sendBleStateChanged(prevState, newState); + // Don't broadcast as it has already been broadcast before + isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || - newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + } else if (!intermediate_off) { + // connect to GattService + if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); + if (mBluetoothGatt != null) { + if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); + onBluetoothGattServiceUp(); + } else { + if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH_LE)) { + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); + } + } sendBleStateChanged(prevState, newState); + //Don't broadcase this as std intent isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_TURNING_ON || - newState == BluetoothAdapter.STATE_TURNING_OFF) { + } else if (intermediate_off) { + if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); + // For LE only mode, broadcast as is sendBleStateChanged(prevState, newState); + sendBluetoothStateCallback(false); // BT is OFF for general users + // Broadcast as STATE_OFF + newState = BluetoothAdapter.STATE_OFF; + sendBrEdrDownCallback(); } + } else if (newState == BluetoothAdapter.STATE_ON) { + boolean isUp = (newState == BluetoothAdapter.STATE_ON); + sendBluetoothStateCallback(isUp); + sendBleStateChanged(prevState, newState); - if (isStandardBroadcast) { - if (prevState == BluetoothAdapter.STATE_BLE_ON) { - // Show prevState of BLE_ON as OFF to standard users - prevState = BluetoothAdapter.STATE_OFF; - } - Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || + newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + sendBleStateChanged(prevState, newState); + isStandardBroadcast = false; + + } else if (newState == BluetoothAdapter.STATE_TURNING_ON || + newState == BluetoothAdapter.STATE_TURNING_OFF) { + sendBleStateChanged(prevState, newState); + } + + if (isStandardBroadcast) { + if (prevState == BluetoothAdapter.STATE_BLE_ON) { + // Show prevState of BLE_ON as OFF to standard users + prevState = BluetoothAdapter.STATE_OFF; } + Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); + intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); } } -- GitLab From b5776f141a07450d29e4558083ce2bb1e087c381 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Thu, 15 Dec 2016 13:51:30 -0800 Subject: [PATCH 0653/1408] Bluetooth: fix issues re-enabling after crash When Bluetooth crashes, sometimes an LE app restarts it before ActivityManager gets a chance. In this case, the Bluetooth manager would assume the state should be LE only and did not continue to fully enabled. Luckily in this case the persisted system state is also ON. Use the persisted state as information about whether we should be fully enabled. Test: basic sanity check, forced crash of BT Bug: 33632976 Change-Id: I546d7abccb82a26fcca2eb70d6d7c76e9510404e --- .../bluetooth/BluetoothManagerService.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b2e38d86757..9b212083911 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -317,13 +317,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Save the Bluetooth on/off state - * */ private void persistBluetoothSetting(int value) { if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value); + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); + Binder.restoreCallingIdentity(callingIdentity); } /** @@ -588,20 +590,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Action taken when GattService is turned off + * Action taken when GattService is turned on */ private void onBluetoothGattServiceUp() { if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up"); try { mBluetoothLock.readLock().lock(); - if (isBleAppPresent() == false && mBluetooth != null - && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + if (mBluetooth == null) { + if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); + return; + } + int st = mBluetooth.getState(); + if (st != BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " + + BluetoothAdapter.nameForState(st)); + return; + } + if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { + // This triggers transition to STATE_ON mBluetooth.onLeServiceUp(); - - // waive WRITE_SECURE_SETTINGS permission check - long callingIdentity = Binder.clearCallingIdentity(); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - Binder.restoreCallingIdentity(callingIdentity); } } catch (RemoteException e) { Slog.e(TAG,"Unable to call onServiceUp", e); @@ -727,10 +735,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { if (persist) { - // waive WRITE_SECURE_SETTINGS permission check - long callingIdentity = Binder.clearCallingIdentity(); persistBluetoothSetting(BLUETOOTH_OFF); - Binder.restoreCallingIdentity(callingIdentity); } mEnableExternal = false; sendDisableMsg(); -- GitLab From 0e22d63c17545b97edb4b8181204d7676c047efd Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Thu, 15 Dec 2016 13:51:30 -0800 Subject: [PATCH 0654/1408] Bluetooth: fix issues re-enabling after crash When Bluetooth crashes, sometimes an LE app restarts it before ActivityManager gets a chance. In this case, the Bluetooth manager would assume the state should be LE only and did not continue to fully enabled. Luckily in this case the persisted system state is also ON. Use the persisted state as information about whether we should be fully enabled. Test: basic sanity check, forced crash of BT Bug: 33632976 Change-Id: I546d7abccb82a26fcca2eb70d6d7c76e9510404e --- .../bluetooth/BluetoothManagerService.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 417531217b7..1035ac8f7c7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -335,12 +335,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Save the Bluetooth on/off state - * */ private void persistBluetoothSetting(int value) { + if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value); + // waive WRITE_SECURE_SETTINGS permission check + long callingIdentity = Binder.clearCallingIdentity(); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); + Binder.restoreCallingIdentity(callingIdentity); } /** @@ -605,20 +608,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Action taken when GattService is turned off + * Action taken when GattService is turned on */ private void onBluetoothGattServiceUp() { if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up"); try { mBluetoothLock.readLock().lock(); - if (isBleAppPresent() == false && mBluetooth != null - && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + if (mBluetooth == null) { + if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); + return; + } + int st = mBluetooth.getState(); + if (st != BluetoothAdapter.STATE_BLE_ON) { + if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " + + BluetoothAdapter.nameForState(st)); + return; + } + if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { + // This triggers transition to STATE_ON mBluetooth.onLeServiceUp(); - - // waive WRITE_SECURE_SETTINGS permission check - long callingIdentity = Binder.clearCallingIdentity(); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - Binder.restoreCallingIdentity(callingIdentity); } } catch (RemoteException e) { Slog.e(TAG,"Unable to call onServiceUp", e); @@ -758,10 +767,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { if (persist) { - // waive WRITE_SECURE_SETTINGS permission check - long callingIdentity = Binder.clearCallingIdentity(); persistBluetoothSetting(BLUETOOTH_OFF); - Binder.restoreCallingIdentity(callingIdentity); } mEnableExternal = false; sendDisableMsg(); -- GitLab From 25213da1215b9ccdd6e629be4351a68c82510318 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 20 Dec 2016 11:21:12 -0800 Subject: [PATCH 0655/1408] Bluetooth: persist state when turning on from BLE Test: in BLE_ON state, turn to full on, reboot phone Bug: 33777496 Change-Id: I246d6ff0dcb81d66aa915ef443040d6fc3c98310 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 9b212083911..7f42d1c030e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1244,8 +1244,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth != null) { int state = mBluetooth.getState(); if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT is in BLE_ON State"); + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); mBluetooth.onLeServiceUp(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); break; } } -- GitLab From dd034c272c56955594f8a779a1f8b4ced8d4b41f Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 20 Dec 2016 11:21:12 -0800 Subject: [PATCH 0656/1408] Bluetooth: persist state when turning on from BLE Test: in BLE_ON state, turn to full on, reboot phone Bug: 33777496 Change-Id: I246d6ff0dcb81d66aa915ef443040d6fc3c98310 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1035ac8f7c7..86bbca7d070 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1282,8 +1282,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth != null) { int state = mBluetooth.getState(); if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT is in BLE_ON State"); + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); mBluetooth.onLeServiceUp(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); break; } } -- GitLab From c5fdfc6d368ba26bc8b1a2fb4fb67c4e78c6b263 Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Mon, 5 Dec 2016 16:46:52 +0000 Subject: [PATCH 0657/1408] In permission review mode, always request user's consent to toggle BT. Bug: 33155221 Test: Manual: flash the watch, check that the consent UI was displayed in both apps (the one with CompileSDK=21 and TargetSDK=21, and the one with CompileSDK=25 and TargetSDK=25). Change-Id: Ic1ed987bbaf282892a2aef354a8ca0add2c829ba --- .../bluetooth/BluetoothManagerService.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 86bbca7d070..36ef3e9f043 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -717,9 +717,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (!isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + if (!isEnabled() && mPermissionReviewRequired) { + startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE); return false; } } @@ -753,9 +752,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + if (isEnabled() && mPermissionReviewRequired) { + startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE); return false; } } @@ -775,8 +773,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - private boolean startConsentUiIfNeeded(String packageName, - int callingUid, String intentAction) throws RemoteException { + private void startConsentUi(String packageName, int callingUid, String intentAction) + throws RemoteException { try { // Validate the package only if we are going to use it ApplicationInfo applicationInfo = mContext.getPackageManager() @@ -788,16 +786,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + " not in uid " + callingUid); } - // Legacy apps in permission review mode trigger a user prompt - if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { - Intent intent = new Intent(intentAction); - mContext.startActivity(intent); - return true; - } + // Permission review mode, trigger a user prompt + Intent intent = new Intent(intentAction); + mContext.startActivity(intent); } catch (PackageManager.NameNotFoundException e) { throw new RemoteException(e.getMessage()); } - return false; } public void unbindAndFinish() { -- GitLab From 7d82a61d972018cfa94f983a97283718c34ce4e0 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Fri, 18 Apr 2014 11:22:45 +0530 Subject: [PATCH 0658/1408] Bluetooth: Add support for HID Device Role This patch adds the HID Device Role support in Bluetooth framework. Also AIDL and callback related files for HID Device role are added to provide interface for third party applications to communicate with HID Device Service. Change-Id: Id03a362b7bcfa2e76056fa0197eaac12ce49b5a2 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothHidDevice.java | 500 ++++++++++++++++++ .../BluetoothHidDeviceAppConfiguration.aidl | 19 + .../BluetoothHidDeviceAppConfiguration.java | 70 +++ .../BluetoothHidDeviceAppQosSettings.aidl | 19 + .../BluetoothHidDeviceAppQosSettings.java | 97 ++++ .../BluetoothHidDeviceAppSdpSettings.aidl | 19 + .../BluetoothHidDeviceAppSdpSettings.java | 80 +++ .../bluetooth/BluetoothHidDeviceCallback.java | 126 +++++ .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/IBluetoothHidDevice.aidl | 37 ++ .../IBluetoothHidDeviceCallback.aidl | 31 ++ 12 files changed, 1012 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothHidDevice.java create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java create mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceCallback.java create mode 100644 framework/java/android/bluetooth/IBluetoothHidDevice.aidl create mode 100644 framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4a97b078f7b..2b35e2bec44 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1940,6 +1940,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener); + return true; } else { return false; } @@ -2016,6 +2019,10 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); break; + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidd = (BluetoothHidDevice) proxy; + hidd.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java new file mode 100644 index 00000000000..3e6a0786607 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -0,0 +1,500 @@ +/* + * 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Arrays; +import java.util.List; + +/** + * @hide + */ +public final class BluetoothHidDevice implements BluetoothProfile { + + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.hid.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Constants representing device subclass. + * + * @see #registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback) + */ + public static final byte SUBCLASS1_NONE = (byte) 0x00; + public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; + public static final byte SUBCLASS1_MOUSE = (byte) 0x80; + public static final byte SUBCLASS1_COMBO = (byte) 0xC0; + + public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; + public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; + public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; + public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; + public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; + public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; + + /** + * Constants representing report types. + * + * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int) + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[]) + */ + public static final byte REPORT_TYPE_INPUT = (byte) 1; + public static final byte REPORT_TYPE_OUTPUT = (byte) 2; + public static final byte REPORT_TYPE_FEATURE = (byte) 3; + + /** + * Constants representing error response for Set Report. + * + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + */ + public static final byte ERROR_RSP_SUCCESS = (byte) 0; + public static final byte ERROR_RSP_NOT_READY = (byte) 1; + public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; + public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; + public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; + public static final byte ERROR_RSP_UNKNOWN = (byte) 14; + + /** + * Constants representing protocol mode used set by host. Default is always + * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. + * + * @see BluetoothHidDeviceCallback#onSetProtocol(byte) + */ + public static final byte PROTOCOL_BOOT_MODE = (byte) 0; + public static final byte PROTOCOL_REPORT_MODE = (byte) 1; + + private Context mContext; + + private ServiceListener mServiceListener; + + private IBluetoothHidDevice mService; + + private BluetoothAdapter mAdapter; + + private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub { + + private BluetoothHidDeviceCallback mCallback; + + public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) { + mCallback = callback; + } + + @Override + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, config, registered); + } + + @Override + public void onConnectionStateChanged(BluetoothDevice device, int state) { + mCallback.onConnectionStateChanged(device, state); + } + + @Override + public void onGetReport(byte type, byte id, int bufferSize) { + mCallback.onGetReport(type, id, bufferSize); + } + + @Override + public void onSetReport(byte type, byte id, byte[] data) { + mCallback.onSetReport(type, id, data); + } + + @Override + public void onSetProtocol(byte protocol) { + mCallback.onSetProtocol(protocol); + } + + @Override + public void onIntrData(byte reportId, byte[] data) { + mCallback.onIntrData(reportId, data); + } + + @Override + public void onVirtualCableUnplug() { + mCallback.onVirtualCableUnplug(); + } + } + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + public void onBluetoothStateChange(boolean up) { + Log.d(TAG, "onBluetoothStateChange: up=" + up); + synchronized (mConnection) { + if (!up) { + Log.d(TAG,"Unbinding service..."); + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e); + } + } + } else { + try { + if (mService == null) { + Log.d(TAG,"Binding HID Device service..."); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } + } + } + } + }; + + private ServiceConnection mConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected()"); + + mService = IBluetoothHidDevice.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected()"); + + mService = null; + + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + } + } + }; + + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); + + mContext = context; + mServiceListener = listener; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHidDevice.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 HID Device Service with " + intent); + return false; + } + Log.d(TAG, "Bound to HID Device Service"); + return true; + } + + void close() { + Log.v(TAG, "close()"); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + synchronized (mConnection) { + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"close: could not unbind HID Dev service: ", e); + } + } + } + + mServiceListener = null; + } + + @Override + public List getConnectedDevices() { + Log.v(TAG, "getConnectedDevices()"); + return null; + } + + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); + return null; + } + + @Override + public int getConnectionState(BluetoothDevice device) { + Log.v(TAG, "getConnectionState(): device=" + device.getAddress()); + + return STATE_DISCONNECTED; + } + + /** + * Registers application to be used for HID device. Connections to HID + * Device are only possible when application is registered. Only one + * application can be registered at time. When no longer used, application + * should be unregistered using + * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of + * HID Device SDP record. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of + * Incoming QoS Settings. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of + * Outgoing QoS Settings. + * @param callback {@link BluetoothHidDeviceCallback} object to which + * callback messages will be sent. + * @return + */ + public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, + BluetoothHidDeviceCallback callback) { + Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos + + " callback=" + callback); + + boolean result = false; + + if (sdp == null || callback == null) { + return false; + } + + if (mService != null) { + try { + BluetoothHidDeviceAppConfiguration config = + new BluetoothHidDeviceAppConfiguration(); + BluetoothHidDeviceCallbackWrapper cbw = + new BluetoothHidDeviceCallbackWrapper(callback); + result = mService.registerApp(config, sdp, inQos, outQos, cbw); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Unregisters application. Active connection will be disconnected and no + * new connections will be allowed until registered again using + * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)} + * + * @param config {@link BluetoothHidDeviceAppConfiguration} object as + * obtained from + * {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, + * BluetoothHidDeviceAppConfiguration, boolean)} + * + * @return + */ + public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + Log.v(TAG, "unregisterApp()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unregisterApp(config); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host using interrupt channel. + * + * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id + * are not defined in descriptor. + * @param data Report data, not including Report Id. + * @return + */ + public boolean sendReport(int id, byte[] data) { + Log.v(TAG, "sendReport(): id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.sendReport(id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host as reply for GET_REPORT request from + * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}. + * + * @param type Report Type, as in request. + * @param id Report Id, as in request. + * @param data Report data, not including Report Id. + * @return + */ + public boolean replyReport(byte type, byte id, byte[] data) { + Log.v(TAG, "replyReport(): type=" + type + " id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.replyReport(type, id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends error handshake message as reply for invalid SET_REPORT request + * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}. + * + * @param error Error to be sent for SET_REPORT via HANDSHAKE. + * @return + */ + public boolean reportError(byte error) { + Log.v(TAG, "reportError(): error = " + error); + + boolean result = false; + + if (mService != null) { + try { + result = mService.reportError(error); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends Virtual Cable Unplug to currently connected host. + * + * @return + */ + public boolean unplug() { + Log.v(TAG, "unplug()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unplug(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Initiates connection to host which currently has Virtual Cable + * established with device. + * + * @return + */ + public boolean connect() { + Log.v(TAG, "connect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.connect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Disconnects from currently connected host. + * + * @return + */ + public boolean disconnect() { + Log.v(TAG, "disconnect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.disconnect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } +} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl new file mode 100644 index 00000000000..283a71790d3 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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; + +parcelable BluetoothHidDeviceAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java new file mode 100644 index 00000000000..05ba64e981f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java @@ -0,0 +1,70 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppConfiguration implements Parcelable { + private final long mHash; + + BluetoothHidDeviceAppConfiguration() { + Random rnd = new Random(); + mHash = rnd.nextLong(); + } + + BluetoothHidDeviceAppConfiguration(long hash) { + mHash = hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppConfiguration) { + BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; + return mHash == config.mHash; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { + long hash = in.readLong(); + return new BluetoothHidDeviceAppConfiguration(hash); + } + + @Override + public BluetoothHidDeviceAppConfiguration[] newArray(int size) { + return new BluetoothHidDeviceAppConfiguration[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mHash); + } +} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl new file mode 100644 index 00000000000..14f91140af2 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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; + +parcelable BluetoothHidDeviceAppQosSettings; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java new file mode 100644 index 00000000000..0d6530c11a9 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -0,0 +1,97 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppQosSettings implements Parcelable { + + final public int serviceType; + final public int tokenRate; + final public int tokenBucketSize; + final public int peakBandwidth; + final public int latency; + final public int delayVariation; + + final static public int SERVICE_NO_TRAFFIC = 0x00; + final static public int SERVICE_BEST_EFFORT = 0x01; + final static public int SERVICE_GUARANTEED = 0x02; + + final static public int MAX = (int) 0xffffffff; + + public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, + int peakBandwidth, + int latency, int delayVariation) { + this.serviceType = serviceType; + this.tokenRate = tokenRate; + this.tokenBucketSize = tokenBucketSize; + this.peakBandwidth = peakBandwidth; + this.latency = latency; + this.delayVariation = delayVariation; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppQosSettings) { + BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(), + in.readInt(), + in.readInt(), in.readInt()); + } + + @Override + public BluetoothHidDeviceAppQosSettings[] newArray(int size) { + return new BluetoothHidDeviceAppQosSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(serviceType); + out.writeInt(tokenRate); + out.writeInt(tokenBucketSize); + out.writeInt(peakBandwidth); + out.writeInt(latency); + out.writeInt(delayVariation); + } + + public int[] toArray() { + return new int[] { + serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation + }; + } +} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl new file mode 100644 index 00000000000..87dd10ee15f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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; + +parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java new file mode 100644 index 00000000000..f9a22458195 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -0,0 +1,80 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + + final public String name; + final public String description; + final public String provider; + final public byte subclass; + final public byte[] descriptors; + + public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, + byte subclass, byte[] descriptors) { + this.name = name; + this.description = description; + this.provider = provider; + this.subclass = subclass; + this.descriptors = descriptors.clone(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppSdpSettings) { + BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), + in.readString(), in.readByte(), in.createByteArray()); + } + + @Override + public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { + return new BluetoothHidDeviceAppSdpSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(name); + out.writeString(description); + out.writeString(provider); + out.writeByte(subclass); + out.writeByteArray(descriptors); + } +} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java new file mode 100644 index 00000000000..0f0e050a1f4 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -0,0 +1,126 @@ +/* + * 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.util.Log; + +/** @hide */ +public abstract class BluetoothHidDeviceCallback { + + private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName(); + + /** + * Callback called when application registration state changes. Usually it's + * called due to either + * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback)} + * or + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * , but can be also unsolicited in case e.g. Bluetooth was turned off in + * which case application is unregistered automatically. + * + * @param pluggedDevice {@link BluetoothDevice} object which represents host + * that currently has Virtual Cable established with device. Only + * valid when application is registered, can be null + * . + * @param config {@link BluetoothHidDeviceAppConfiguration} object which + * represents token required to unregister application using + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * . + * @param registered true if application is registered, + * false otherwise. + */ + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ? + null : pluggedDevice.toString()) + " registered=" + registered); + } + + /** + * Callback called when connection state with remote host was changed. + * Application can assume than Virtual Cable is established when called with + * {@link BluetoothProfile#STATE_CONNECTED} state. + * + * @param device {@link BluetoothDevice} object representing host device + * which connection state was changed. + * @param state Connection state as defined in {@link BluetoothProfile}. + */ + public void onConnectionStateChanged(BluetoothDevice device, int state) { + Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state); + } + + /** + * Callback called when GET_REPORT is received from remote host. Should be + * replied by application using + * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}. + * + * @param type Requested Report Type. + * @param id Requested Report Id, can be 0 if no Report Id are defined in + * descriptor. + * @param bufferSize Requested buffer size, application shall respond with + * at least given number of bytes. + */ + public void onGetReport(byte type, byte id, int bufferSize) { + Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize); + } + + /** + * Callback called when SET_REPORT is received from remote host. In case + * received data are invalid, application shall respond with + * {@link BluetoothHidDevice#reportError()}. + * + * @param type Report Type. + * @param id Report Id. + * @param data Report data. + */ + public void onSetReport(byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: type=" + type + " id=" + id); + } + + /** + * Callback called when SET_PROTOCOL is received from remote host. + * Application shall use this information to send only reports valid for + * given protocol mode. By default, + * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * + * @param protocol Protocol Mode. + */ + public void onSetProtocol(byte protocol) { + Log.d(TAG, "onSetProtocol: protocol=" + protocol); + } + + /** + * Callback called when report data is received over interrupt channel. + * Report Type is assumed to be + * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * + * @param reportId Report Id. + * @param data Report data. + */ + public void onIntrData(byte reportId, byte[] data) { + Log.d(TAG, "onIntrData: reportId=" + reportId); + } + + /** + * Callback called when Virtual Cable is removed. This can be either due to + * {@link BluetoothHidDevice#unplug()} or request from remote side. After + * this callback is received connection will be disconnected automatically. + */ + public void onVirtualCableUnplug() { + Log.d(TAG, "onVirtualCableUnplug"); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f3636070fa5..6a009a9ba28 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -142,12 +142,18 @@ public interface BluetoothProfile { */ public static final int MAP_CLIENT = 18; + /** + * HID device + * @hide + */ + static public final int HID_DEVICE = 19; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * @hide */ - public static final int MAX_PROFILE_ID = 17; + public static final int MAX_PROFILE_ID = 19; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/IBluetoothHidDevice.aidl b/framework/java/android/bluetooth/IBluetoothHidDevice.aidl new file mode 100644 index 00000000000..15f73138e2c --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHidDevice.aidl @@ -0,0 +1,37 @@ +/* + * 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.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; +import android.bluetooth.IBluetoothHidDeviceCallback; +import android.bluetooth.BluetoothHidDeviceAppSdpSettings; +import android.bluetooth.BluetoothHidDeviceAppQosSettings; + +/** @hide */ +interface IBluetoothHidDevice { + boolean registerApp(in BluetoothHidDeviceAppConfiguration config, + in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, + in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); + boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); + boolean sendReport(in int id, in byte[] data); + boolean replyReport(in byte type, in byte id, in byte[] data); + boolean reportError(byte error); + boolean unplug(); + boolean connect(); + boolean disconnect(); +} diff --git a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl new file mode 100644 index 00000000000..12528769658 --- /dev/null +++ b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl @@ -0,0 +1,31 @@ +/* + * 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.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; + +/** @hide */ +interface IBluetoothHidDeviceCallback { + void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); + void onConnectionStateChanged(in BluetoothDevice device, in int state); + void onGetReport(in byte type, in byte id, in int bufferSize); + void onSetReport(in byte type, in byte id, in byte[] data); + void onSetProtocol(in byte protocol); + void onIntrData(in byte reportId, in byte[] data); + void onVirtualCableUnplug(); +} -- GitLab From 5fabc4426e870bc119027d773efafdf524e5f988 Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Fri, 23 Dec 2016 11:52:21 +0000 Subject: [PATCH 0659/1408] Rename the Bluetooth profile classes for HID Device role. We already have BluetoothInputDevice class, so adding something called BluetoothHidDevice seems confusing. On the other hand, the new class is designed to connect to HID Host devices, so naming it BluetoothInputHost makes sense and goes in line with the existing BluetoothInputDevice. The same goes for the new constant HID_DEVICE that is just as confusing to have together with the INPUT_DEVICE one. This CL also renames the "connection state changed" broadcast (for the same reasons), declares it as an SDK constant, and also adds some javadoc to it. Note that BluetoothHidDeviceApp* classes remained unchanged, as those correspond to the app that implements the Device (and connects to the Host). Test: make Change-Id: I5075ca5b97db3c1dd403c2e9660eecc7380cffe2 --- .../android/bluetooth/BluetoothAdapter.java | 10 ++--- ...HidDevice.java => BluetoothInputHost.java} | 43 ++++++++++++++----- .../android/bluetooth/BluetoothProfile.java | 4 +- ...idDevice.aidl => IBluetoothInputHost.aidl} | 2 +- 4 files changed, 40 insertions(+), 19 deletions(-) rename framework/java/android/bluetooth/{BluetoothHidDevice.java => BluetoothInputHost.java} (91%) rename framework/java/android/bluetooth/{IBluetoothHidDevice.aidl => IBluetoothInputHost.aidl} (97%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2b35e2bec44..b483054c99c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1940,8 +1940,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; - } else if (profile == BluetoothProfile.HID_DEVICE) { - BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener); + } else if (profile == BluetoothProfile.INPUT_HOST) { + BluetoothInputHost iHost = new BluetoothInputHost(context, listener); return true; } else { return false; @@ -2019,9 +2019,9 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); break; - case BluetoothProfile.HID_DEVICE: - BluetoothHidDevice hidd = (BluetoothHidDevice) proxy; - hidd.close(); + case BluetoothProfile.INPUT_HOST: + BluetoothInputHost iHost = (BluetoothInputHost) proxy; + iHost.close(); break; } } diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothInputHost.java similarity index 91% rename from framework/java/android/bluetooth/BluetoothHidDevice.java rename to framework/java/android/bluetooth/BluetoothInputHost.java index 3e6a0786607..129fe7ecdc3 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,12 +32,31 @@ import java.util.List; /** * @hide */ -public final class BluetoothHidDevice implements BluetoothProfile { +public final class BluetoothInputHost implements BluetoothProfile { - private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + private static final String TAG = BluetoothInputHost.class.getSimpleName(); + /** + * Intent used to broadcast the change in connection state of the Input + * Host profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.hid.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; /** * Constants representing device subclass. @@ -92,7 +113,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { private ServiceListener mServiceListener; - private IBluetoothHidDevice mService; + private IBluetoothInputHost mService; private BluetoothAdapter mAdapter; @@ -178,11 +199,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); + mService = IBluetoothInputHost.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, - BluetoothHidDevice.this); + mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, + BluetoothInputHost.this); } } @@ -192,13 +213,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); } } }; - BluetoothHidDevice(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothHidDevice"); + BluetoothInputHost(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothInputHost"); mContext = context; mServiceListener = listener; @@ -217,7 +238,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothHidDevice.class.getName()); + Intent intent = new Intent(IBluetoothInputHost.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 6a009a9ba28..2f64c719ec1 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -143,10 +143,10 @@ public interface BluetoothProfile { public static final int MAP_CLIENT = 18; /** - * HID device + * Input Host * @hide */ - static public final int HID_DEVICE = 19; + static public final int INPUT_HOST = 19; /** * Max profile ID. This value should be updated whenever a new profile is added to match diff --git a/framework/java/android/bluetooth/IBluetoothHidDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputHost.aidl similarity index 97% rename from framework/java/android/bluetooth/IBluetoothHidDevice.aidl rename to framework/java/android/bluetooth/IBluetoothInputHost.aidl index 15f73138e2c..b2c421c47c5 100644 --- a/framework/java/android/bluetooth/IBluetoothHidDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputHost.aidl @@ -23,7 +23,7 @@ import android.bluetooth.BluetoothHidDeviceAppSdpSettings; import android.bluetooth.BluetoothHidDeviceAppQosSettings; /** @hide */ -interface IBluetoothHidDevice { +interface IBluetoothInputHost { boolean registerApp(in BluetoothHidDeviceAppConfiguration config, in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); -- GitLab From 6eb20469015829978b820ba63bc0d064e36d3697 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Wed, 28 Dec 2016 14:13:21 -0800 Subject: [PATCH 0660/1408] Bluetooth: track enabling in dumpsys Move basic state dumpsys to here from AdapterService. Track which apps are enabling and disabling Bluetooth, including BLE apps, and show the apps in the dumpsys logs. Test: start phone and enable/disable, take bug report Bug: 33692282 Change-Id: I6ea62ebdcfd7873d0be1bb5c5c520bbce3737a40 --- .../android/bluetooth/BluetoothAdapter.java | 99 ++++------ .../android/bluetooth/IBluetoothManager.aidl | 6 +- .../bluetooth/BluetoothManagerService.java | 174 +++++++++++++----- 3 files changed, 161 insertions(+), 118 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4a97b078f7b..927d382f648 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -680,30 +680,7 @@ public final class BluetoothAdapter { } /** - * Performs action based on user action to turn BT ON - * or OFF if BT is in BLE_ON state - */ - private void notifyUserAction(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - Log.e(TAG, "mService is null"); - return; - } - if (enable) { - mService.onLeServiceUp(); //NA:TODO implementation pending - } else { - mService.onBrEdrDown(); //NA:TODO implementation pending - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - - /** - * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). + * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). * *

        If the internal Adapter state is STATE_BLE_ON, this would trigger the transition * to STATE_OFF and completely shut-down Bluetooth @@ -733,61 +710,50 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); - try { - mManagerService.updateBleAppCount(mToken, false); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - int bleAppCnt = 0; + if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { + String packageName = ActivityThread.currentPackageName(); + if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); try { - bleAppCnt = mManagerService.updateBleAppCount(mToken, false); + mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } - if (bleAppCnt == 0) { - // Disable only if there are no other clients - notifyUserAction(false); - } return true; } - if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); + if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); return false; } /** - * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would - * EnableBLE, EnableBLE brings-up Bluetooth so that application can access - * only LE related feature (Bluetooth GATT layers interfaces using the respective class) - * EnableBLE in turn registers the existance of a special App which wants to - * turn on Bluetooth Low enrgy part without making it visible at the settings UI - * as Bluetooth ON. - *

        Invoking EnableBLE when Bluetooth is already in ON state, would just registers - * the existance of special Application and doesn't do anything to current BT state. - * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth - * would stay in BLE_ON state so that LE features are still acessible to the special - * Applications. + * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. * - *

        This is an asynchronous call: it will return immediately, and + * enableBLE registers the existence of an app using only LE functions. + * + * enableBLE may enable Bluetooth to an LE only mode so that an app can use + * LE related features (BluetoothGatt or BluetoothGattServer classes) + * + * If the user disables Bluetooth while an app is registered to use LE only features, + * Bluetooth will remain on in LE only mode for the app. + * + * When Bluetooth is in LE only mode, it is not shown as ON to the UI. + * + *

        This is an asynchronous call: it returns immediately, and * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} - * to be notified of subsequent adapter state changes. If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time - * later transition to either {@link #STATE_OFF} or {@link - * #STATE_BLE_ON}. If this call returns false then there was an - * immediate problem that will prevent the adapter from being turned on - - * such as Airplane mode, or the adapter is already turned on. - * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various + * to be notified of adapter state changes. + * + * If this call returns * true, then the adapter state is either in a mode where + * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, + * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. + * + * If this call returns false then there was an immediate problem that prevents the + * adapter from being turned on - such as Airplane mode. + * + * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * - * @return true to indicate Bluetooth LE start-up has begun, or false on + * @return true to indicate Bluetooth LE will be available, or false on * immediate error * @hide */ @@ -796,13 +762,14 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; try { - mManagerService.updateBleAppCount(mToken, true); + String packageName = ActivityThread.currentPackageName(); + mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(ActivityThread.currentPackageName()); + return mManagerService.enable(packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2087,7 +2054,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(); + return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2ab9ae80b44..5afd7741822 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -35,8 +35,8 @@ interface IBluetoothManager void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); boolean enable(String packageName); - boolean enableNoAutoConnect(); - boolean disable( String packageName, boolean persist); + boolean enableNoAutoConnect(String packageName); + boolean disable(String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); @@ -47,6 +47,6 @@ interface IBluetoothManager String getName(); boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable); + int updateBleAppCount(IBinder b, boolean enable, String packageName); boolean isBleAppPresent(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 36ef3e9f043..adc59de87f2 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -57,14 +57,16 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -137,16 +139,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + // used inside handler thread private boolean mQuietEnable = false; - // configuarion from external IBinder call which is used to + private boolean mEnable; + + /** + * Used for tracking apps that enabled / disabled Bluetooth. + */ + private class ActiveLog { + private String mPackageName; + private boolean mEnable; + private long mTimestamp; + + public ActiveLog(String packageName, boolean enable, long timestamp) { + mPackageName = packageName; + mEnable = enable; + mTimestamp = timestamp; + } + + public long getTime() { + return mTimestamp; + } + + public String toString() { + return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; + } + + } + + private LinkedList mActiveLogs; + + // configuration from external IBinder call which is used to // synchronize with broadcast receiver. private boolean mQuietEnableExternal; - // configuarion from external IBinder call which is used to - // synchronize with broadcast receiver. private boolean mEnableExternal; - // used inside handler thread - private boolean mEnable; + + // Map of apps registered to keep BLE scanning on. + private Map mBleApps = new ConcurrentHashMap(); + private int mState; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; @@ -172,7 +204,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { + private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); @@ -251,12 +283,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting Slog.d(TAG, "Calling disable"); - sendDisableMsg(); + sendDisableMsg("airplane mode"); } } else if (mEnableExternal) { // enable without persisting the setting Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "airplane mode"); } } } @@ -272,6 +304,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { || context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); + mActiveLogs = new LinkedList(); mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -298,15 +331,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } - int sysUiUid = -1; + int systemUiUid = -1; try { - sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", + systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } - mSystemUiUid = sysUiUid; + mSystemUiUid = systemUiUid; } /** @@ -484,8 +517,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } class ClientDeathRecipient implements IBinder.DeathRecipient { + private String mPackageName; + + public ClientDeathRecipient(String packageName) { + mPackageName = packageName; + } + public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); if (isBleAppPresent()) { // Nothing to do, another app is here. return; @@ -504,10 +543,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } } - } - /** Internal death rec list */ - Map mBleApps = new ConcurrentHashMap(); + public String getPackageName() { + return mPackageName; + } + } @Override public boolean isBleScanAlwaysAvailable() { @@ -565,28 +605,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public int updateBleAppCount(IBinder token, boolean enable) { - if (enable) { - ClientDeathRecipient r = mBleApps.get(token); - if (r == null) { - ClientDeathRecipient deathRec = new ClientDeathRecipient(); - try { - token.linkToDeath(deathRec, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("Wake lock is already dead."); - } - mBleApps.put(token, deathRec); - if (DBG) Slog.d(TAG, "Registered for death Notification"); - } - - } else { - ClientDeathRecipient r = mBleApps.get(token); - if (r != null) { - // Unregister death recipient as the app goes away. - token.unlinkToDeath(r, 0); - mBleApps.remove(token); - if (DBG) Slog.d(TAG, "Unregistered for death Notification"); + public int updateBleAppCount(IBinder token, boolean enable, String packageName) { + ClientDeathRecipient r = mBleApps.get(token); + if (r == null && enable) { + ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); + try { + token.linkToDeath(deathRec, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } + mBleApps.put(token, deathRec); + if (DBG) Slog.d(TAG, "Registered for death of " + packageName); + } else if (!enable && r != null) { + // Unregister death recipient as the app goes away. + token.unlinkToDeath(r, 0); + mBleApps.remove(token); + if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); } int appCount = mBleApps.size(); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); @@ -601,7 +635,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBleApps.clear(); } - /** @hide*/ + /** @hide */ public boolean isBleAppPresent() { if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); return mBleApps.size() > 0; @@ -667,7 +701,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public boolean enableNoAutoConnect() + public boolean enableNoAutoConnect(String packageName) { if (isBluetoothDisallowed()) { if (DBG) { @@ -692,7 +726,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; - sendEnableMsg(true); + sendEnableMsg(true, packageName); } return true; } @@ -724,7 +758,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (DBG) { - Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + + Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } @@ -733,7 +767,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false); + sendEnableMsg(false, packageName); } if (DBG) Slog.d(TAG, "enable returning"); return true; @@ -768,7 +802,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; - sendDisableMsg(); + sendDisableMsg(packageName); } return true; } @@ -909,7 +943,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "system boot"); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); @@ -1877,13 +1911,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - private void sendDisableMsg() { + private void sendDisableMsg(String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); + addActiveLog(packageName, false); } - private void sendEnableMsg(boolean quietMode) { + private void sendEnableMsg(boolean quietMode, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + addActiveLog(packageName, true); + } + + private void addActiveLog(String packageName, boolean enable) { + synchronized (mActiveLogs) { + if (mActiveLogs.size() > 10) { + mActiveLogs.remove(); + } + mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); + } } private void recoverBluetoothServiceFromError(boolean clearBle) { @@ -1954,19 +1999,50 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); String errorMsg = null; + + boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); + + if (!protoOut) { + writer.println("Bluetooth Status"); + writer.println(" enabled: " + isEnabled()); + writer.println(" state: " + BluetoothAdapter.nameForState(mState)); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName); + if (mEnable) { + long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); + String onDurationString = String.format("%02d:%02d:%02d.%03d", + (int)(onDuration / (1000 * 60 * 60)), + (int)((onDuration / (1000 * 60)) % 60), + (int)((onDuration / 1000) % 60), + (int)(onDuration % 1000)); + writer.println(" time since enabled: " + onDurationString + "\n"); + } + + writer.println("Enable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(log); + } + + writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); + for (ClientDeathRecipient app : mBleApps.values()) { + writer.println(app.getPackageName()); + } + + writer.flush(); + } + if (mBluetoothBinder == null) { errorMsg = "Bluetooth Service not connected"; } else { try { mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { - errorMsg = "RemoteException while calling Bluetooth Service"; + errorMsg = "RemoteException while dumping Bluetooth Service"; } } if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format - if ((args.length > 0) && args[0].startsWith("--proto")) - return; + if (protoOut) return; writer.println(errorMsg); } } -- GitLab From b6e4b12d1e4655e4a1f2b720fb48e33917e44511 Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Fri, 30 Dec 2016 14:35:09 +0000 Subject: [PATCH 0661/1408] Fix profiles broken by ag/1751147 Change-Id: Ifa92819df8e63355a3979ea0f1a20a0363b6cd45 --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 927d382f648..f9be3a1d7f0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1907,6 +1907,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; + } else if (profile == BluetoothProfile.INPUT_HOST) { + BluetoothInputHost iHost = new BluetoothInputHost(context, listener); + return true; } else { return false; } @@ -1983,6 +1986,10 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); break; + case BluetoothProfile.INPUT_HOST: + BluetoothInputHost iHost = (BluetoothInputHost) proxy; + iHost.close(); + break; } } -- GitLab From 6f9f21b8eb9afa1d210c5faacdb803535984d925 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Wed, 28 Dec 2016 14:13:21 -0800 Subject: [PATCH 0662/1408] Bluetooth: track enabling in dumpsys Move basic state dumpsys to here from AdapterService. Track which apps are enabling and disabling Bluetooth, including BLE apps, and show the apps in the dumpsys logs. Test: start phone and enable/disable, take bug report Bug: 33692282 Change-Id: I6ea62ebdcfd7873d0be1bb5c5c520bbce3737a40 (cherry picked from commit 6eb20469015829978b820ba63bc0d064e36d3697) --- .../android/bluetooth/BluetoothAdapter.java | 99 ++++------ .../android/bluetooth/IBluetoothManager.aidl | 4 +- .../bluetooth/BluetoothManagerService.java | 174 +++++++++++++----- 3 files changed, 160 insertions(+), 117 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5ced03b2c2d..e04f1e1b75a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -684,30 +684,7 @@ public final class BluetoothAdapter { } /** - * Performs action based on user action to turn BT ON - * or OFF if BT is in BLE_ON state - */ - private void notifyUserAction(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - Log.e(TAG, "mService is null"); - return; - } - if (enable) { - mService.onLeServiceUp(); //NA:TODO implementation pending - } else { - mService.onBrEdrDown(); //NA:TODO implementation pending - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - - /** - * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). + * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). * *

        If the internal Adapter state is STATE_BLE_ON, this would trigger the transition * to STATE_OFF and completely shut-down Bluetooth @@ -737,61 +714,50 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); - try { - mManagerService.updateBleAppCount(mToken, false); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - int bleAppCnt = 0; + if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { + String packageName = ActivityThread.currentPackageName(); + if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); try { - bleAppCnt = mManagerService.updateBleAppCount(mToken, false); + mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } - if (bleAppCnt == 0) { - // Disable only if there are no other clients - notifyUserAction(false); - } return true; } - if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); + if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); return false; } /** - * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would - * EnableBLE, EnableBLE brings-up Bluetooth so that application can access - * only LE related feature (Bluetooth GATT layers interfaces using the respective class) - * EnableBLE in turn registers the existance of a special App which wants to - * turn on Bluetooth Low enrgy part without making it visible at the settings UI - * as Bluetooth ON. - *

        Invoking EnableBLE when Bluetooth is already in ON state, would just registers - * the existance of special Application and doesn't do anything to current BT state. - * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth - * would stay in BLE_ON state so that LE features are still acessible to the special - * Applications. + * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. * - *

        This is an asynchronous call: it will return immediately, and + * enableBLE registers the existence of an app using only LE functions. + * + * enableBLE may enable Bluetooth to an LE only mode so that an app can use + * LE related features (BluetoothGatt or BluetoothGattServer classes) + * + * If the user disables Bluetooth while an app is registered to use LE only features, + * Bluetooth will remain on in LE only mode for the app. + * + * When Bluetooth is in LE only mode, it is not shown as ON to the UI. + * + *

        This is an asynchronous call: it returns immediately, and * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} - * to be notified of subsequent adapter state changes. If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time - * later transition to either {@link #STATE_OFF} or {@link - * #STATE_BLE_ON}. If this call returns false then there was an - * immediate problem that will prevent the adapter from being turned on - - * such as Airplane mode, or the adapter is already turned on. - * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various + * to be notified of adapter state changes. + * + * If this call returns * true, then the adapter state is either in a mode where + * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, + * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. + * + * If this call returns false then there was an immediate problem that prevents the + * adapter from being turned on - such as Airplane mode. + * + * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * - * @return true to indicate Bluetooth LE start-up has begun, or false on + * @return true to indicate Bluetooth LE will be available, or false on * immediate error * @hide */ @@ -800,13 +766,14 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; try { - mManagerService.updateBleAppCount(mToken, true); + String packageName = ActivityThread.currentPackageName(); + mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(ActivityThread.currentPackageName()); + return mManagerService.enable(packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2102,7 +2069,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(); + return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 90f008520a8..5afd7741822 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -35,7 +35,7 @@ interface IBluetoothManager void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); boolean enable(String packageName); - boolean enableNoAutoConnect(); + boolean enableNoAutoConnect(String packageName); boolean disable(String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); @@ -47,6 +47,6 @@ interface IBluetoothManager String getName(); boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable); + int updateBleAppCount(IBinder b, boolean enable, String packageName); boolean isBleAppPresent(); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2682cd156af..677d3786fe2 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -54,14 +54,16 @@ import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -134,16 +136,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + // used inside handler thread private boolean mQuietEnable = false; - // configuarion from external IBinder call which is used to + private boolean mEnable; + + /** + * Used for tracking apps that enabled / disabled Bluetooth. + */ + private class ActiveLog { + private String mPackageName; + private boolean mEnable; + private long mTimestamp; + + public ActiveLog(String packageName, boolean enable, long timestamp) { + mPackageName = packageName; + mEnable = enable; + mTimestamp = timestamp; + } + + public long getTime() { + return mTimestamp; + } + + public String toString() { + return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; + } + + } + + private LinkedList mActiveLogs; + + // configuration from external IBinder call which is used to // synchronize with broadcast receiver. private boolean mQuietEnableExternal; - // configuarion from external IBinder call which is used to - // synchronize with broadcast receiver. private boolean mEnableExternal; - // used inside handler thread - private boolean mEnable; + + // Map of apps registered to keep BLE scanning on. + private Map mBleApps = new ConcurrentHashMap(); + private int mState; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; @@ -169,7 +201,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { + private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); @@ -230,12 +262,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting Slog.d(TAG, "Calling disable"); - sendDisableMsg(); + sendDisableMsg("airplane mode"); } } else if (mEnableExternal) { // enable without persisting the setting Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "airplane mode"); } } } @@ -251,6 +283,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { || context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); + mActiveLogs = new LinkedList(); mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -278,15 +311,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } - int sysUiUid = -1; + int systemUiUid = -1; try { - sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", + systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } - mSystemUiUid = sysUiUid; + mSystemUiUid = systemUiUid; } /** @@ -466,8 +499,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } class ClientDeathRecipient implements IBinder.DeathRecipient { + private String mPackageName; + + public ClientDeathRecipient(String packageName) { + mPackageName = packageName; + } + public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); if (isBleAppPresent()) { // Nothing to do, another app is here. return; @@ -486,10 +525,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } } - } - /** Internal death rec list */ - Map mBleApps = new ConcurrentHashMap(); + public String getPackageName() { + return mPackageName; + } + } @Override public boolean isBleScanAlwaysAvailable() { @@ -547,28 +587,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public int updateBleAppCount(IBinder token, boolean enable) { - if (enable) { - ClientDeathRecipient r = mBleApps.get(token); - if (r == null) { - ClientDeathRecipient deathRec = new ClientDeathRecipient(); - try { - token.linkToDeath(deathRec, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("Wake lock is already dead."); - } - mBleApps.put(token, deathRec); - if (DBG) Slog.d(TAG, "Registered for death Notification"); - } - - } else { - ClientDeathRecipient r = mBleApps.get(token); - if (r != null) { - // Unregister death recipient as the app goes away. - token.unlinkToDeath(r, 0); - mBleApps.remove(token); - if (DBG) Slog.d(TAG, "Unregistered for death Notification"); + public int updateBleAppCount(IBinder token, boolean enable, String packageName) { + ClientDeathRecipient r = mBleApps.get(token); + if (r == null && enable) { + ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); + try { + token.linkToDeath(deathRec, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } + mBleApps.put(token, deathRec); + if (DBG) Slog.d(TAG, "Registered for death of " + packageName); + } else if (!enable && r != null) { + // Unregister death recipient as the app goes away. + token.unlinkToDeath(r, 0); + mBleApps.remove(token); + if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); } int appCount = mBleApps.size(); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); @@ -583,7 +617,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBleApps.clear(); } - /** @hide*/ + /** @hide */ public boolean isBleAppPresent() { if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); return mBleApps.size() > 0; @@ -649,7 +683,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public boolean enableNoAutoConnect() + public boolean enableNoAutoConnect(String packageName) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -667,7 +701,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; - sendEnableMsg(true); + sendEnableMsg(true, packageName); } return true; } @@ -692,7 +726,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (DBG) { - Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + + Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } @@ -701,7 +735,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false); + sendEnableMsg(false, packageName); } if (DBG) Slog.d(TAG, "enable returning"); return true; @@ -736,7 +770,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; - sendDisableMsg(); + sendDisableMsg(packageName); } return true; } @@ -871,7 +905,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) Slog.d(TAG, "Bluetooth boot completed"); if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "system boot"); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); @@ -1839,13 +1873,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - private void sendDisableMsg() { + private void sendDisableMsg(String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); + addActiveLog(packageName, false); } - private void sendEnableMsg(boolean quietMode) { + private void sendEnableMsg(boolean quietMode, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + addActiveLog(packageName, true); + } + + private void addActiveLog(String packageName, boolean enable) { + synchronized (mActiveLogs) { + if (mActiveLogs.size() > 10) { + mActiveLogs.remove(); + } + mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); + } } private void recoverBluetoothServiceFromError(boolean clearBle) { @@ -1906,19 +1951,50 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); String errorMsg = null; + + boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); + + if (!protoOut) { + writer.println("Bluetooth Status"); + writer.println(" enabled: " + isEnabled()); + writer.println(" state: " + BluetoothAdapter.nameForState(mState)); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName); + if (mEnable) { + long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); + String onDurationString = String.format("%02d:%02d:%02d.%03d", + (int)(onDuration / (1000 * 60 * 60)), + (int)((onDuration / (1000 * 60)) % 60), + (int)((onDuration / 1000) % 60), + (int)(onDuration % 1000)); + writer.println(" time since enabled: " + onDurationString + "\n"); + } + + writer.println("Enable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(log); + } + + writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); + for (ClientDeathRecipient app : mBleApps.values()) { + writer.println(app.getPackageName()); + } + + writer.flush(); + } + if (mBluetoothBinder == null) { errorMsg = "Bluetooth Service not connected"; } else { try { mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { - errorMsg = "RemoteException while calling Bluetooth Service"; + errorMsg = "RemoteException while dumping Bluetooth Service"; } } if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format - if ((args.length > 0) && args[0].startsWith("--proto")) - return; + if (protoOut) return; writer.println(errorMsg); } } -- GitLab From b2320848b3fa0aabe332a42e6278f2c081bc813b Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Fri, 30 Dec 2016 14:43:29 +0000 Subject: [PATCH 0663/1408] HID Device role API fixes This change makes HIDD API more like the other ones, i.e. supporting multiple devices, and implements missing methods. While the underlying implementation may still only support a single device at a time, the "device" parameter can still be useful for checking if the application is trying to send the data to a correct device. Test: make Change-Id: I55fe04c0762a96fcddd6c6678e790361d648111a --- .../bluetooth/BluetoothHidDeviceCallback.java | 38 +++--- .../android/bluetooth/BluetoothInputHost.java | 111 ++++++++++++------ .../IBluetoothHidDeviceCallback.aidl | 10 +- .../bluetooth/IBluetoothInputHost.aidl | 15 ++- 4 files changed, 108 insertions(+), 66 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 0f0e050a1f4..f519776276f 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -45,9 +45,9 @@ public abstract class BluetoothHidDeviceCallback { * false otherwise. */ public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { - Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ? - null : pluggedDevice.toString()) + " registered=" + registered); + BluetoothHidDeviceAppConfiguration config, boolean registered) { + Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + + registered); } /** @@ -60,13 +60,13 @@ public abstract class BluetoothHidDeviceCallback { * @param state Connection state as defined in {@link BluetoothProfile}. */ public void onConnectionStateChanged(BluetoothDevice device, int state) { - Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state); + Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); } /** * Callback called when GET_REPORT is received from remote host. Should be * replied by application using - * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}. + * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}. * * @param type Requested Report Type. * @param id Requested Report Id, can be 0 if no Report Id are defined in @@ -74,21 +74,22 @@ public abstract class BluetoothHidDeviceCallback { * @param bufferSize Requested buffer size, application shall respond with * at least given number of bytes. */ - public void onGetReport(byte type, byte id, int bufferSize) { - Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize); + public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { + Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" + + bufferSize); } /** * Callback called when SET_REPORT is received from remote host. In case * received data are invalid, application shall respond with - * {@link BluetoothHidDevice#reportError()}. + * {@link BluetoothHidDevice#reportError(BluetoothDevice)}. * * @param type Report Type. * @param id Report Id. * @param data Report data. */ - public void onSetReport(byte type, byte id, byte[] data) { - Log.d(TAG, "onSetReport: type=" + type + " id=" + id); + public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); } /** @@ -99,8 +100,8 @@ public abstract class BluetoothHidDeviceCallback { * * @param protocol Protocol Mode. */ - public void onSetProtocol(byte protocol) { - Log.d(TAG, "onSetProtocol: protocol=" + protocol); + public void onSetProtocol(BluetoothDevice device, byte protocol) { + Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); } /** @@ -111,16 +112,17 @@ public abstract class BluetoothHidDeviceCallback { * @param reportId Report Id. * @param data Report data. */ - public void onIntrData(byte reportId, byte[] data) { - Log.d(TAG, "onIntrData: reportId=" + reportId); + public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { + Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId); } /** * Callback called when Virtual Cable is removed. This can be either due to - * {@link BluetoothHidDevice#unplug()} or request from remote side. After - * this callback is received connection will be disconnected automatically. + * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote + * side. After this callback is received connection will be disconnected + * automatically. */ - public void onVirtualCableUnplug() { - Log.d(TAG, "onVirtualCableUnplug"); + public void onVirtualCableUnplug(BluetoothDevice device) { + Log.d(TAG, "onVirtualCableUnplug: device=" + device); } } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 129fe7ecdc3..68d105f1155 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -27,6 +27,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.Arrays; +import java.util.ArrayList; import java.util.List; /** @@ -137,28 +138,28 @@ public final class BluetoothInputHost implements BluetoothProfile { } @Override - public void onGetReport(byte type, byte id, int bufferSize) { - mCallback.onGetReport(type, id, bufferSize); + public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { + mCallback.onGetReport(device, type, id, bufferSize); } @Override - public void onSetReport(byte type, byte id, byte[] data) { - mCallback.onSetReport(type, id, data); + public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { + mCallback.onSetReport(device, type, id, data); } @Override - public void onSetProtocol(byte protocol) { - mCallback.onSetProtocol(protocol); + public void onSetProtocol(BluetoothDevice device, byte protocol) { + mCallback.onSetProtocol(device, protocol); } @Override - public void onIntrData(byte reportId, byte[] data) { - mCallback.onIntrData(reportId, data); + public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { + mCallback.onIntrData(device, reportId, data); } @Override - public void onVirtualCableUnplug() { - mCallback.onVirtualCableUnplug(); + public void onVirtualCableUnplug(BluetoothDevice device) { + mCallback.onVirtualCableUnplug(device); } } @@ -276,21 +277,59 @@ public final class BluetoothInputHost implements BluetoothProfile { mServiceListener = null; } - @Override + /** + * {@inheritDoc} + */ public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - return null; + + if (mService != null) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return new ArrayList(); } - @Override + /** + * {@inheritDoc} + */ public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - return null; + + if (mService != null) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return new ArrayList(); } - @Override + /** + * {@inheritDoc} + */ public int getConnectionState(BluetoothDevice device) { - Log.v(TAG, "getConnectionState(): device=" + device.getAddress()); + Log.v(TAG, "getConnectionState(): device=" + device); + + if (mService != null) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } return STATE_DISCONNECTED; } @@ -379,14 +418,12 @@ public final class BluetoothInputHost implements BluetoothProfile { * @param data Report data, not including Report Id. * @return */ - public boolean sendReport(int id, byte[] data) { - Log.v(TAG, "sendReport(): id=" + id); - + public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; if (mService != null) { try { - result = mService.sendReport(id, data); + result = mService.sendReport(device, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -399,21 +436,21 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * Sends report to remote host as reply for GET_REPORT request from - * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}. + * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. * * @param type Report Type, as in request. * @param id Report Id, as in request. * @param data Report data, not including Report Id. * @return */ - public boolean replyReport(byte type, byte id, byte[] data) { - Log.v(TAG, "replyReport(): type=" + type + " id=" + id); + public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { + Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); boolean result = false; if (mService != null) { try { - result = mService.replyReport(type, id, data); + result = mService.replyReport(device, type, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -426,19 +463,19 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * Sends error handshake message as reply for invalid SET_REPORT request - * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}. + * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return */ - public boolean reportError(byte error) { - Log.v(TAG, "reportError(): error = " + error); + public boolean reportError(BluetoothDevice device, byte error) { + Log.v(TAG, "reportError(): device=" + device + " error=" + error); boolean result = false; if (mService != null) { try { - result = mService.reportError(error); + result = mService.reportError(device, error); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -454,14 +491,14 @@ public final class BluetoothInputHost implements BluetoothProfile { * * @return */ - public boolean unplug() { - Log.v(TAG, "unplug()"); + public boolean unplug(BluetoothDevice device) { + Log.v(TAG, "unplug(): device=" + device); boolean result = false; if (mService != null) { try { - result = mService.unplug(); + result = mService.unplug(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -478,14 +515,14 @@ public final class BluetoothInputHost implements BluetoothProfile { * * @return */ - public boolean connect() { - Log.v(TAG, "connect()"); + public boolean connect(BluetoothDevice device) { + Log.v(TAG, "connect(): device=" + device); boolean result = false; if (mService != null) { try { - result = mService.connect(); + result = mService.connect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -501,14 +538,14 @@ public final class BluetoothInputHost implements BluetoothProfile { * * @return */ - public boolean disconnect() { - Log.v(TAG, "disconnect()"); + public boolean disconnect(BluetoothDevice device) { + Log.v(TAG, "disconnect(): device=" + device); boolean result = false; if (mService != null) { try { - result = mService.disconnect(); + result = mService.disconnect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl index 12528769658..a737198ad95 100644 --- a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl @@ -23,9 +23,9 @@ import android.bluetooth.BluetoothHidDeviceAppConfiguration; interface IBluetoothHidDeviceCallback { void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); void onConnectionStateChanged(in BluetoothDevice device, in int state); - void onGetReport(in byte type, in byte id, in int bufferSize); - void onSetReport(in byte type, in byte id, in byte[] data); - void onSetProtocol(in byte protocol); - void onIntrData(in byte reportId, in byte[] data); - void onVirtualCableUnplug(); + void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize); + void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); + void onSetProtocol(in BluetoothDevice device, in byte protocol); + void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data); + void onVirtualCableUnplug(in BluetoothDevice device); } diff --git a/framework/java/android/bluetooth/IBluetoothInputHost.aidl b/framework/java/android/bluetooth/IBluetoothInputHost.aidl index b2c421c47c5..6c4993f750e 100644 --- a/framework/java/android/bluetooth/IBluetoothInputHost.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputHost.aidl @@ -28,10 +28,13 @@ interface IBluetoothInputHost { in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); - boolean sendReport(in int id, in byte[] data); - boolean replyReport(in byte type, in byte id, in byte[] data); - boolean reportError(byte error); - boolean unplug(); - boolean connect(); - boolean disconnect(); + boolean sendReport(in BluetoothDevice device, in int id, in byte[] data); + boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); + boolean reportError(in BluetoothDevice device, byte error); + boolean unplug(in BluetoothDevice device); + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); } -- GitLab From 178a3d23c6b0a2bab98e4333936939bd5c2e725a Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 21 Dec 2016 12:05:51 -0800 Subject: [PATCH 0664/1408] Add a mechanism for configuring the A2DP Source codecs * Added a new class BluetoothCodecConfig that contains codec-related configuration or capabilities: codec type, priority, sample rate, bits per sample, channel mode, and codec specific fields. * Extended the Bluetooth A2DP AIDL interface to get/set the current codec configuration * Added new call handleBluetoothA2dpDeviceConfigChange() to the Media Framework that is called when there are changes in the Bluetooth A2DP device configuration - e.g., the A2DP codec is changed. Test: A2DP streaming to headsets, TestPlans/71390 Bug: 30958229 Change-Id: I9a82716cbc2a5efbe77352a031ac80c88f6a2459 --- .../java/android/bluetooth/BluetoothA2dp.java | 69 +++++ .../bluetooth/BluetoothCodecConfig.aidl | 19 ++ .../bluetooth/BluetoothCodecConfig.java | 287 ++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 3 + 4 files changed, 378 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothCodecConfig.aidl create mode 100644 framework/java/android/bluetooth/BluetoothCodecConfig.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 353c6400ffd..1165fce3ce0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -101,6 +101,27 @@ public final class BluetoothA2dp implements BluetoothProfile { public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; + /** + * Intent used to broadcast the change in the Audio Codec state of the + * A2DP Source profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_CODEC_CONFIG} - The current codec configuration.
        • + *
        • {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently + * connected, otherwise it is not included.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CODEC_CONFIG_CHANGED = + "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; + /** * A2DP sink device is streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of @@ -542,6 +563,54 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + /** + * Gets the current codec configuration. + * + * @return the current codec configuration + * @hide + */ + public BluetoothCodecConfig getCodecConfig() { + if (DBG) Log.d(TAG, "getCodecConfig"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getCodecConfig(); + } + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getCodecConfig()", e); + return null; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Sets the codec configuration preference. + * + * @param codecConfig the codec configuration preference + * @hide + */ + public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { + if (DBG) Log.d(TAG, "setCodecConfigPreference"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + mService.setCodecConfigPreference(codecConfig); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); + return; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl b/framework/java/android/bluetooth/BluetoothCodecConfig.aidl new file mode 100644 index 00000000000..553e66e1dac --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable BluetoothCodecConfig; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java new file mode 100644 index 00000000000..5cc127766e8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -0,0 +1,287 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents the codec configuration for a Bluetooth A2DP source device. + * + * {@see BluetoothA2dp} + * + * {@hide} + */ +public final class BluetoothCodecConfig implements Parcelable { + + /** + * Extra for the codec configuration intents of the individual profiles. + * + * This extra represents the current codec configuration of the A2DP + * profile. + */ + public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG"; + + /** + * Extra for the codec configuration intents of the individual profiles. + * + * This extra represents the previous codec configuration of the A2DP + * profile. + */ + public static final String EXTRA_PREVIOUS_CODEC_CONFIG = + "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG"; + + public static final int SOURCE_CODEC_TYPE_SBC = 0; + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + + public static final int CODEC_PRIORITY_DEFAULT = 0; + public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; + + public static final int SAMPLE_RATE_NONE = 0; + public static final int SAMPLE_RATE_44100 = 0x1 << 0; + public static final int SAMPLE_RATE_48000 = 0x1 << 1; + public static final int SAMPLE_RATE_88200 = 0x1 << 2; + public static final int SAMPLE_RATE_96000 = 0x1 << 3; + public static final int SAMPLE_RATE_176400 = 0x1 << 4; + public static final int SAMPLE_RATE_192000 = 0x1 << 5; + + public static final int BITS_PER_SAMPLE_NONE = 0; + public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; + + public static final int CHANNEL_MODE_NONE = 0; + public static final int CHANNEL_MODE_MONO = 0x1 << 0; + public static final int CHANNEL_MODE_STEREO = 0x1 << 1; + + private final int mCodecType; + private final int mCodecPriority; + private final int mSampleRate; + private final int mBitsPerSample; + private final int mChannelMode; + private final long mCodecSpecific1; + private final long mCodecSpecific2; + private final long mCodecSpecific3; + private final long mCodecSpecific4; + + public BluetoothCodecConfig(int codecType, int codecPriority, + int sampleRate, int bitsPerSample, + int channelMode,long codecSpecific1, + long codecSpecific2, long codecSpecific3, + long codecSpecific4) { + mCodecType = codecType; + mCodecPriority = codecPriority; + mSampleRate = sampleRate; + mBitsPerSample = bitsPerSample; + mChannelMode = channelMode; + mCodecSpecific1 = codecSpecific1; + mCodecSpecific2 = codecSpecific2; + mCodecSpecific3 = codecSpecific3; + mCodecSpecific4 = codecSpecific4; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothCodecConfig) { + BluetoothCodecConfig other = (BluetoothCodecConfig)o; + return (other.mCodecType == mCodecType && + other.mCodecPriority == mCodecPriority && + other.mSampleRate == mSampleRate && + other.mBitsPerSample == mBitsPerSample && + other.mChannelMode == mChannelMode && + other.mCodecSpecific1 == mCodecSpecific1 && + other.mCodecSpecific2 == mCodecSpecific2 && + other.mCodecSpecific3 == mCodecSpecific3 && + other.mCodecSpecific4 == mCodecSpecific4); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mCodecType, mCodecPriority, mSampleRate, + mBitsPerSample, mChannelMode, mCodecSpecific1, + mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); + } + + @Override + public String toString() { + return "{mCodecType:" + mCodecType + + ",mCodecPriority:" + mCodecPriority + + ",mSampleRate:" + String.format("0x%x", mSampleRate) + + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + + ",mChannelMode:" + String.format("0x%x", mChannelMode) + + ",mCodecSpecific1:" + mCodecSpecific1 + + ",mCodecSpecific2:" + mCodecSpecific2 + + ",mCodecSpecific3:" + mCodecSpecific3 + + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothCodecConfig createFromParcel(Parcel in) { + final int codecType = in.readInt(); + final int codecPriority = in.readInt(); + final int sampleRate = in.readInt(); + final int bitsPerSample = in.readInt(); + final int channelMode = in.readInt(); + final long codecSpecific1 = in.readLong(); + final long codecSpecific2 = in.readLong(); + final long codecSpecific3 = in.readLong(); + final long codecSpecific4 = in.readLong(); + return new BluetoothCodecConfig(codecType, codecPriority, + sampleRate, bitsPerSample, + channelMode, codecSpecific1, + codecSpecific2, codecSpecific3, + codecSpecific4); + } + public BluetoothCodecConfig[] newArray(int size) { + return new BluetoothCodecConfig[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mCodecType); + out.writeInt(mCodecPriority); + out.writeInt(mSampleRate); + out.writeInt(mBitsPerSample); + out.writeInt(mChannelMode); + out.writeLong(mCodecSpecific1); + out.writeLong(mCodecSpecific2); + out.writeLong(mCodecSpecific3); + out.writeLong(mCodecSpecific4); + } + + /** + * Returns the codec type. + * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}. + * + * @return the codec type + */ + public int getCodecType() { + return mCodecType; + } + + /** + * Returns the codec selection priority. + * The codec selection priority is relative to other codecs: larger value + * means higher priority. If 0, reset to default. + * + * @return the codec priority + */ + public int getCodecPriority() { + return mCodecPriority; + } + + /** + * Returns the codec sample rate. The value can be a bitmask with all + * supported sample rates: + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or + * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000} + * + * @return the codec sample rate + */ + public int getSampleRate() { + return mSampleRate; + } + + /** + * Returns the codec bits per sample. The value can be a bitmask with all + * bits per sample supported: + * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or + * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or + * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or + * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32} + * + * @return the codec bits per sample + */ + public int getBitsPerSample() { + return mBitsPerSample; + } + + /** + * Returns the codec channel mode. The value can be a bitmask with all + * supported channel modes: + * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or + * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or + * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO} + * + * @return the codec channel mode + */ + public int getChannelMode() { + return mChannelMode; + } + + /** + * Returns a codec specific value1. + * + * @return a codec specific value1. + */ + public long getCodecSpecific1() { + return mCodecSpecific1; + } + + /** + * Returns a codec specific value2. + * + * @return a codec specific value2 + */ + public long getCodecSpecific2() { + return mCodecSpecific2; + } + + /** + * Returns a codec specific value3. + * + * @return a codec specific value3 + */ + public long getCodecSpecific3() { + return mCodecSpecific3; + } + + /** + * Returns a codec specific value4. + * + * @return a codec specific value4 + */ + public long getCodecSpecific4() { + return mCodecSpecific4; + } + + /** + * Checks whether the audio feeding parameters are same. + * + * @param other the codec config to compare against + * @return true if the audio feeding parameters are same, otherwise false + */ + public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { + return (other != null && other.mSampleRate == mSampleRate && + other.mBitsPerSample == mBitsPerSample && + other.mChannelMode == mChannelMode); + } +} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 26ff9e274c3..5b524eb18a5 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -16,6 +16,7 @@ package android.bluetooth; +import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; /** @@ -36,4 +37,6 @@ interface IBluetoothA2dp { oneway void adjustAvrcpAbsoluteVolume(int direction); oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); + BluetoothCodecConfig getCodecConfig(); + oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); } -- GitLab From ad5a1691081dc51857ab92d84bbcca287ad71da3 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 3 Jan 2017 11:37:38 -0800 Subject: [PATCH 0665/1408] Bluetooth: don't output dumpsys info twice Dumpsys info was printed in the "Application Services" section as well as the bluetooth_manager section. Add an argument to print so it prints from here, and doesn't in the other section. Test: run a bugreport, check in "APP SERVICES" section Bug: 29356402 Change-Id: I8adedaeac54c9a538c581459654dbdf96361e046 --- .../android/server/bluetooth/BluetoothManagerService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index adc59de87f2..4f43eac8b3c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2029,6 +2029,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } writer.flush(); + if (args.length == 0) { + // Add arg to produce output + args = new String[1]; + args[0] = "--print"; + } } if (mBluetoothBinder == null) { -- GitLab From 7ad3acbfcb80294a3fa03afbdca89663f2e581ef Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 4 Jan 2017 16:10:09 -0800 Subject: [PATCH 0666/1408] Integration of the aptX and aptX-HD codecs for A2DP source Each of the codecs can be used if the corresponding encoding shared library is installed on the device: - aptX: libaptX.so - aptX-HD: libaptXHD.so Test: A2DP streaming to aptX and aptX-HD headsets Bug: 30958229 Change-Id: I24faddc8cd88ae3e1370922c633f30e13124a867 --- framework/java/android/bluetooth/BluetoothCodecConfig.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 5cc127766e8..67fdb0caf8f 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -47,7 +47,13 @@ public final class BluetoothCodecConfig implements Parcelable { public static final String EXTRA_PREVIOUS_CODEC_CONFIG = "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG"; + // Add an entry for each source codec here. + // NOTE: The values should be same as those listed in the following file: + // hardware/libhardware/include/hardware/bt_av.h public static final int SOURCE_CODEC_TYPE_SBC = 0; + public static final int SOURCE_CODEC_TYPE_APTX = 1; + public static final int SOURCE_CODEC_TYPE_APTX_HD = 2; + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; public static final int CODEC_PRIORITY_DEFAULT = 0; -- GitLab From cc854d67e7d699f5f4e9a94f80110c4130cdfd4e Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 4 Jan 2017 16:47:02 -0800 Subject: [PATCH 0667/1408] Integration of the LDAC codec for A2DP source The codec can be used if the encoding shared library is installed on the device: libldacBT_enc.so Test: A2DP streaming to LDAC headsets Bug: 30958229 Change-Id: I524805fd308b5181427515617eda05625a7c4ae5 --- framework/java/android/bluetooth/BluetoothCodecConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 67fdb0caf8f..52cd2de4c2b 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -53,6 +53,7 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int SOURCE_CODEC_TYPE_SBC = 0; public static final int SOURCE_CODEC_TYPE_APTX = 1; public static final int SOURCE_CODEC_TYPE_APTX_HD = 2; + public static final int SOURCE_CODEC_TYPE_LDAC = 3; public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; -- GitLab From 3dfe08b1ceefb805341b04c48ec9f8f460f47a65 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Fri, 6 Jan 2017 11:29:15 +0000 Subject: [PATCH 0668/1408] Refactor RFCOMM / BluetoothSocket usage of LocalSocket LocalSocket is used by BluetoothSocket. BluetoothSocket passes a pre-created file descriptor to LocalSocket that is then given to the LocalSocketImpl. Commit b08c7bc0bdc48ae95be2697ca27ea89a9dd92c3e broke the behavior. Commit 7a8c36aa4e6a1b5e48f0ee5a787f10bcfece7587 put in a minimal fix. This change tidies up LocalSocket and associated classes and replaces a specialist constructor with a factory method to highlight the special case. While there an unnecessary exception has been removed. Bug: 34111534 Test: Boot device Test: vogar --mode app_process tests/tests/net/src/android/net/cts/LocalSocketTest.java Change-Id: I4ba2f2d9ea361a950ff8bc8d64fc800d998c3210 --- framework/java/android/bluetooth/BluetoothSocket.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index b68693880ac..98a5341b302 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -243,7 +243,7 @@ public final class BluetoothSocket implements Closeable { } as.mPfd = new ParcelFileDescriptor(fds[0]); - as.mSocket = new LocalSocket(fds[0]); + as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; @@ -367,7 +367,7 @@ public final class BluetoothSocket implements Closeable { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mPfd == null) throw new IOException("bt socket connect failed"); FileDescriptor fd = mPfd.getFileDescriptor(); - mSocket = new LocalSocket(fd); + mSocket = LocalSocket.createConnectedLocalSocket(fd); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } @@ -416,9 +416,9 @@ public final class BluetoothSocket implements Closeable { if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - if (DBG) Log.d(TAG, "bindListen(), new LocalSocket "); - mSocket = new LocalSocket(fd); - if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket"); + mSocket = LocalSocket.createConnectedLocalSocket(fd); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()"); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } -- GitLab From 093c05a8fd1f1b8bfa55fd4add02ab7af41875c3 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Mon, 9 Jan 2017 19:48:51 +0100 Subject: [PATCH 0669/1408] Don't offer the BT sharing option to the user if BT is disallowed. Disable BluetoothOppLauncherActivity component if the UserManager#BLUETOOTH_DISALLOWED user restriction is set. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895060 Change-Id: I4316843b2858aefa750f12a8a0e940424aa21a10 --- .../bluetooth/BluetoothManagerService.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 4f43eac8b3c..f2e1fb67818 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -217,6 +217,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { + if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) + && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { + // The relevant restriction has not changed - do nothing. + return; + } final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { @@ -227,6 +232,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // when from system. } } + updateOppLauncherComponentState(bluetoothDisallowed); } }; @@ -938,7 +944,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - if (isBluetoothDisallowed()) { + final boolean isBluetoothDisallowed = isBluetoothDisallowed(); + updateOppLauncherComponentState(isBluetoothDisallowed); + if (isBluetoothDisallowed) { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { @@ -1995,6 +2003,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not + * offered to the user if Bluetooth is disallowed. Puts the component to its default state if + * Bluetooth is not disallowed. + * + * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user + * restriction was set. + */ + private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", + "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); + final int newState = bluetoothDisallowed + ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED + : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + mContext.getPackageManager() + .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 2e05346f152c08fc6089bfdb387e7a363471cb98 Mon Sep 17 00:00:00 2001 From: Shijian Li Date: Wed, 11 Jan 2017 17:32:12 +0000 Subject: [PATCH 0670/1408] Sync the startConsentUiIfNeeded logics to master. We are missing the package name when starting the activity now. Bug: 34216066 Change-Id: Ic8c8576ce2c65bc554bbf4794e169838c2ccbbe1 --- .../bluetooth/BluetoothManagerService.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index bfbd8cb38d8..d767c462c6d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -28,6 +28,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -676,8 +677,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (!isEnabled() && mPermissionReviewRequired) { - startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE); + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { return false; } } @@ -710,8 +712,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (isEnabled() && mPermissionReviewRequired) { - startConsentUi(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE); + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { return false; } } @@ -734,8 +737,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - private void startConsentUi(String packageName, int callingUid, String intentAction) - throws RemoteException { + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { try { // Validate the package only if we are going to use it ApplicationInfo applicationInfo = mContext.getPackageManager() @@ -747,9 +750,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + " not in uid " + callingUid); } - // Permission review mode, trigger a user prompt Intent intent = new Intent(intentAction); - mContext.startActivity(intent); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + // Shouldn't happen + Slog.e(TAG, "Intent to handle action " + intentAction + " missing"); + return false; + } + return true; } catch (PackageManager.NameNotFoundException e) { throw new RemoteException(e.getMessage()); } -- GitLab From 4fcee874c9a959ac473a77764a9279a2d82521ed Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Thu, 12 Jan 2017 15:56:39 +0000 Subject: [PATCH 0671/1408] Revert "Don't offer the BT sharing option to the user if BT is disallowed." Causes boot loop issue: 02-15 02:31:59.785 770 770 E System : Caused by: java.lang.IllegalArgumentException: Unknown component: com.android.bluetooth/com.android.bluetooth.opp.BluetoothOppLauncherActivity 02-15 02:31:59.785 770 770 E System : .at com.android.server.pm.PackageManagerService.setEnabledSetting(PackageManagerService.java:18500) 02-15 02:31:59.785 770 770 E System : .at com.android.server.pm.PackageManagerService.setComponentEnabledSetting(PackageManagerService.java:18461) 02-15 02:31:59.785 770 770 E System : .at android.app.ApplicationPackageManager.setComponentEnabledSetting(ApplicationPackageManager.java:2129) 02-15 02:31:59.785 770 770 E System : .at com.android.server.BluetoothManagerService.updateOppLauncherComponentState(BluetoothManagerService.java:2036) This reverts commit 093c05a8fd1f1b8bfa55fd4add02ab7af41875c3. Change-Id: Ia079bb2fcc91bb5979a3840ab50b9d9ddda1cf24 --- .../bluetooth/BluetoothManagerService.java | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f2e1fb67818..4f43eac8b3c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -217,11 +217,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { - if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) - && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { - // The relevant restriction has not changed - do nothing. - return; - } final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { @@ -232,7 +227,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // when from system. } } - updateOppLauncherComponentState(bluetoothDisallowed); } }; @@ -944,9 +938,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - final boolean isBluetoothDisallowed = isBluetoothDisallowed(); - updateOppLauncherComponentState(isBluetoothDisallowed); - if (isBluetoothDisallowed) { + if (isBluetoothDisallowed()) { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { @@ -2003,24 +1995,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** - * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not - * offered to the user if Bluetooth is disallowed. Puts the component to its default state if - * Bluetooth is not disallowed. - * - * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user - * restriction was set. - */ - private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { - final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", - "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); - final int newState = bluetoothDisallowed - ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED - : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - mContext.getPackageManager() - .setComponentEnabledSetting(oppLauncherComponent, newState, 0); - } - @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From 1e43c484ea72a5cf7cabd01545792e84da6892ca Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Tue, 10 Jan 2017 12:08:23 +0000 Subject: [PATCH 0672/1408] Prepare for removal of legacy-test from default targets In preparation for removing junit classes from the Android API the legacy-test target will be removed from the TARGET_DEFAULT_JAVA_LIBRARIES. This change adds explicit dependencies on junit and/or legacy-android-test to ensure that modules will compile properly once it is removed. Bug: 30188076 Test: make checkbuild Change-Id: I13e88297731253420e4e5f5291d503f13a39a156 --- framework/tests/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk index 4a1d18cb5c0..f53419ab53c 100644 --- a/framework/tests/Android.mk +++ b/framework/tests/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test LOCAL_PACKAGE_NAME := BluetoothTests LOCAL_CERTIFICATE := platform -- GitLab From a16eceb85c99c89a7cf3e37ff90e54014a2eaf30 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Tue, 10 Jan 2017 12:08:23 +0000 Subject: [PATCH 0673/1408] Prepare for removal of legacy-test from default targets In preparation for removing junit classes from the Android API the legacy-test target will be removed from the TARGET_DEFAULT_JAVA_LIBRARIES. This change adds explicit dependencies on junit and/or legacy-android-test to ensure that modules will compile properly once it is removed. (cherry picked from 6387604f9e672ece85e07c4bcbd7be396867f06f) Bug: 30188076 Test: make checkbuild Merged-In: I13e88297731253420e4e5f5291d503f13a39a156 Change-Id: I58446eb8c45d8ac2bcdbc9fa40d1321e811bdd4b --- framework/tests/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk index 4a1d18cb5c0..f53419ab53c 100644 --- a/framework/tests/Android.mk +++ b/framework/tests/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test LOCAL_PACKAGE_NAME := BluetoothTests LOCAL_CERTIFICATE := platform -- GitLab From 245f21bf8ade5e37407d0f16a546300a9fc546b8 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 20 Jan 2017 14:55:15 -0800 Subject: [PATCH 0674/1408] Change HFP Client API to support multi device - Adds BluetoothDevice as a parameter where required - Gets rid of device management APIs that can be done via BluetoothProfiles instead Test: Manual sanity tests Bug: b/33554547 Bug: b/30984220 Change-Id: I3485ac5bfe1fcb29c774ad040fdd608e1cacb8df --- .../bluetooth/BluetoothHeadsetClient.java | 55 +++++-------------- .../bluetooth/IBluetoothHeadsetClient.aidl | 11 ++-- 2 files changed, 18 insertions(+), 48 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index c7c64c4391c..544b3b95db0 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -964,38 +964,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } - /** - * Accept the incoming connection. - */ - public boolean acceptIncomingConnect(BluetoothDevice device) { - if (DBG) log("acceptIncomingConnect"); - if (mService != null && isEnabled()) { - try { - return mService.acceptIncomingConnect(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; - } - - /** - * Reject the incoming connection. - */ - public boolean rejectIncomingConnect(BluetoothDevice device) { - if (DBG) log("rejectIncomingConnect"); - if (mService != null) { - try { - return mService.rejectIncomingConnect(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; - } - /** * Returns current audio state of Audio Gateway. * @@ -1017,13 +985,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Sets whether audio routing is allowed. * + * @param device remote device + * @param allowed if routing is allowed to the device * Note: This is an internal function and shouldn't be exposed */ - public void setAudioRouteAllowed(boolean allowed) { + public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - mService.setAudioRouteAllowed(allowed); + mService.setAudioRouteAllowed(device, allowed); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1033,14 +1003,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns whether audio routing is allowed. - * + * @param device remote device + * @return whether the command succeeded * Note: This is an internal function and shouldn't be exposed */ - public boolean getAudioRouteAllowed() { + public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(); + return mService.getAudioRouteAllowed(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1054,15 +1025,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It setup SCO channel with remote connected Handsfree AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean connectAudio() { + public boolean connectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.connectAudio(); + return mService.connectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1078,15 +1050,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It tears down the SCO channel from remote AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean disconnectAudio() { + public boolean disconnectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.disconnectAudio(); + return mService.disconnectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index a351bd2d707..e571b009f04 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -29,9 +29,6 @@ interface IBluetoothHeadsetClient { boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); @@ -58,10 +55,10 @@ interface IBluetoothHeadsetClient { boolean getLastVoiceTagNumber(in BluetoothDevice device); int getAudioState(in BluetoothDevice device); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); + boolean connectAudio(in BluetoothDevice device); + boolean disconnectAudio(in BluetoothDevice device); + void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); + boolean getAudioRouteAllowed(in BluetoothDevice device); Bundle getCurrentAgFeatures(in BluetoothDevice device); } -- GitLab From a83146f12173c1458a7e45e3cc147928d25db0a7 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 20 Jan 2017 14:55:15 -0800 Subject: [PATCH 0675/1408] Change HFP Client API to support multi device - Adds BluetoothDevice as a parameter where required - Gets rid of device management APIs that can be done via BluetoothProfiles instead Test: Manual sanity tests Bug: b/33554547 Bug: b/30984220 Change-Id: I3485ac5bfe1fcb29c774ad040fdd608e1cacb8df (cherry picked from commit 245f21bf8ade5e37407d0f16a546300a9fc546b8) --- .../bluetooth/BluetoothHeadsetClient.java | 55 +++++-------------- .../bluetooth/IBluetoothHeadsetClient.aidl | 11 ++-- 2 files changed, 18 insertions(+), 48 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 93790feecd1..353efffe6c2 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -963,38 +963,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } - /** - * Accept the incoming connection. - */ - public boolean acceptIncomingConnect(BluetoothDevice device) { - if (DBG) log("acceptIncomingConnect"); - if (mService != null && isEnabled()) { - try { - return mService.acceptIncomingConnect(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; - } - - /** - * Reject the incoming connection. - */ - public boolean rejectIncomingConnect(BluetoothDevice device) { - if (DBG) log("rejectIncomingConnect"); - if (mService != null) { - try { - return mService.rejectIncomingConnect(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; - } - /** * Returns current audio state of Audio Gateway. * @@ -1016,13 +984,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Sets whether audio routing is allowed. * + * @param device remote device + * @param allowed if routing is allowed to the device * Note: This is an internal function and shouldn't be exposed */ - public void setAudioRouteAllowed(boolean allowed) { + public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - mService.setAudioRouteAllowed(allowed); + mService.setAudioRouteAllowed(device, allowed); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1032,14 +1002,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns whether audio routing is allowed. - * + * @param device remote device + * @return whether the command succeeded * Note: This is an internal function and shouldn't be exposed */ - public boolean getAudioRouteAllowed() { + public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(); + return mService.getAudioRouteAllowed(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1053,15 +1024,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It setup SCO channel with remote connected Handsfree AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean connectAudio() { + public boolean connectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.connectAudio(); + return mService.connectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1077,15 +1049,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It tears down the SCO channel from remote AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean disconnectAudio() { + public boolean disconnectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.disconnectAudio(); + return mService.disconnectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index a351bd2d707..e571b009f04 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -29,9 +29,6 @@ interface IBluetoothHeadsetClient { boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); @@ -58,10 +55,10 @@ interface IBluetoothHeadsetClient { boolean getLastVoiceTagNumber(in BluetoothDevice device); int getAudioState(in BluetoothDevice device); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); + boolean connectAudio(in BluetoothDevice device); + boolean disconnectAudio(in BluetoothDevice device); + void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); + boolean getAudioRouteAllowed(in BluetoothDevice device); Bundle getCurrentAgFeatures(in BluetoothDevice device); } -- GitLab From 045f03ab9df631c32ea7b75a4ad25fa70ebd3514 Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 20 Jan 2017 14:55:15 -0800 Subject: [PATCH 0676/1408] Change HFP Client API to support multi device - Adds BluetoothDevice as a parameter where required - Gets rid of device management APIs that can be done via BluetoothProfiles instead Test: Manual sanity tests Bug: b/33554547 Bug: b/30984220 Change-Id: I3485ac5bfe1fcb29c774ad040fdd608e1cacb8df (cherry picked from commit 245f21bf8ade5e37407d0f16a546300a9fc546b8) --- .../bluetooth/BluetoothHeadsetClient.java | 55 +++++-------------- .../bluetooth/IBluetoothHeadsetClient.aidl | 11 ++-- 2 files changed, 18 insertions(+), 48 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 93790feecd1..353efffe6c2 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -963,38 +963,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } - /** - * Accept the incoming connection. - */ - public boolean acceptIncomingConnect(BluetoothDevice device) { - if (DBG) log("acceptIncomingConnect"); - if (mService != null && isEnabled()) { - try { - return mService.acceptIncomingConnect(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; - } - - /** - * Reject the incoming connection. - */ - public boolean rejectIncomingConnect(BluetoothDevice device) { - if (DBG) log("rejectIncomingConnect"); - if (mService != null) { - try { - return mService.rejectIncomingConnect(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; - } - /** * Returns current audio state of Audio Gateway. * @@ -1016,13 +984,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Sets whether audio routing is allowed. * + * @param device remote device + * @param allowed if routing is allowed to the device * Note: This is an internal function and shouldn't be exposed */ - public void setAudioRouteAllowed(boolean allowed) { + public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - mService.setAudioRouteAllowed(allowed); + mService.setAudioRouteAllowed(device, allowed); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1032,14 +1002,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns whether audio routing is allowed. - * + * @param device remote device + * @return whether the command succeeded * Note: This is an internal function and shouldn't be exposed */ - public boolean getAudioRouteAllowed() { + public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); if (mService != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(); + return mService.getAudioRouteAllowed(device); } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { Log.w(TAG, "Proxy not attached to service"); @@ -1053,15 +1024,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It setup SCO channel with remote connected Handsfree AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean connectAudio() { + public boolean connectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.connectAudio(); + return mService.connectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1077,15 +1049,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It tears down the SCO channel from remote AG device. * + * @param device remote device * @return true if command has been issued successfully; * false otherwise; * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} * intent; */ - public boolean disconnectAudio() { + public boolean disconnectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { try { - return mService.disconnectAudio(); + return mService.disconnectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl index a351bd2d707..e571b009f04 100644 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -29,9 +29,6 @@ interface IBluetoothHeadsetClient { boolean connect(in BluetoothDevice device); boolean disconnect(in BluetoothDevice device); - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); @@ -58,10 +55,10 @@ interface IBluetoothHeadsetClient { boolean getLastVoiceTagNumber(in BluetoothDevice device); int getAudioState(in BluetoothDevice device); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); + boolean connectAudio(in BluetoothDevice device); + boolean disconnectAudio(in BluetoothDevice device); + void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); + boolean getAudioRouteAllowed(in BluetoothDevice device); Bundle getCurrentAgFeatures(in BluetoothDevice device); } -- GitLab From af9d9d4cf965c352a4bf41da53d13cf6fcf47bf3 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Wed, 18 Jan 2017 09:37:52 -0800 Subject: [PATCH 0677/1408] Bluetooth: fix indentation, formatting in dumpsys Before ====== Enable log: 01-17 09:43:18 Enabled by com.android.systemui 01-18 07:13:10 Disabled by com.andorid.systemui 1 BLE Apps registered: com.google.android.gms After ===== Enable log: 02-06 07:49:07 Enabled by system boot 02-06 07:49:35 Disabled by com.andorid.systemui 1 BLE App registered: com.google.android.gms Test: run bugreport, see differences in dumpsys Change-Id: I6086f7f97c1fcbdb860d70d9516fd5bf630a9f5e --- .../bluetooth/BluetoothManagerService.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 4f43eac8b3c..6e578b94d79 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2018,21 +2018,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" time since enabled: " + onDurationString + "\n"); } - writer.println("Enable log:"); - for (ActiveLog log : mActiveLogs) { - writer.println(log); + if (mActiveLogs.size() == 0) { + writer.println("Bluetooth never enabled!"); + } else { + writer.println("Enable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(" " + log); + } } - writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); + String bleAppString = "No BLE Apps registered."; + if (mBleApps.size() == 1) { + bleAppString = "1 BLE App registered:"; + } else if (mBleApps.size() > 1) { + bleAppString = mBleApps.size() + " BLE Apps registered:"; + } + writer.println("\n" + bleAppString); for (ClientDeathRecipient app : mBleApps.values()) { - writer.println(app.getPackageName()); + writer.println(" " + app.getPackageName()); } + writer.println(""); writer.flush(); if (args.length == 0) { - // Add arg to produce output - args = new String[1]; - args[0] = "--print"; + // Add arg to produce output + args = new String[1]; + args[0] = "--print"; } } -- GitLab From 5e992dbcf18128d45f2d0e8602678ccf8c0903c7 Mon Sep 17 00:00:00 2001 From: Lenka Trochtova Date: Tue, 17 Jan 2017 10:35:49 +0100 Subject: [PATCH 0678/1408] Don't offer the BT sharing option to the user if BT is disallowed. Disable BluetoothOppLauncherActivity component if the UserManager#BLUETOOTH_DISALLOWED user restriction is set. Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases --test com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Bug: 32895060 Bug: 34239380 Change-Id: I3467c82881896f276f542583bb50807b5e27276b --- .../bluetooth/BluetoothManagerService.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6e578b94d79..3b3ce073e09 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -49,6 +49,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -58,6 +59,8 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import com.android.server.pm.PackageManagerService; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; @@ -217,6 +220,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { + if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) + && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { + // The relevant restriction has not changed - do nothing. + return; + } final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { @@ -227,6 +235,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // when from system. } } + updateOppLauncherComponentState(bluetoothDisallowed); } }; @@ -938,7 +947,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - if (isBluetoothDisallowed()) { + final boolean isBluetoothDisallowed = isBluetoothDisallowed(); + PackageManagerService packageManagerService = + (PackageManagerService) ServiceManager.getService("package"); + if (packageManagerService != null && !packageManagerService.isOnlyCoreApps()) { + updateOppLauncherComponentState(isBluetoothDisallowed); + } + if (isBluetoothDisallowed) { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { @@ -1995,6 +2010,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not + * offered to the user if Bluetooth is disallowed. Puts the component to its default state if + * Bluetooth is not disallowed. + * + * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user + * restriction was set. + */ + private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", + "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); + final int newState = bluetoothDisallowed + ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED + : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + try { + mContext.getPackageManager() + .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + } catch (Exception e) { + // The component was not found, do nothing. + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); -- GitLab From ba5e29919dcc67bba3677902b342194bf82c4dda Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sat, 14 Jan 2017 00:41:05 -0800 Subject: [PATCH 0679/1408] Integration of the AAC codec for A2DP source Test: A2DP streaming to AAC headsets Bug: 30958229 Change-Id: I1b530f1c5c495b8231fd68bed788d4567096683d --- framework/java/android/bluetooth/BluetoothCodecConfig.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 52cd2de4c2b..a37a0b38aa7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,9 +51,10 @@ public final class BluetoothCodecConfig implements Parcelable { // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h public static final int SOURCE_CODEC_TYPE_SBC = 0; - public static final int SOURCE_CODEC_TYPE_APTX = 1; - public static final int SOURCE_CODEC_TYPE_APTX_HD = 2; - public static final int SOURCE_CODEC_TYPE_LDAC = 3; + public static final int SOURCE_CODEC_TYPE_AAC = 1; + public static final int SOURCE_CODEC_TYPE_APTX = 2; + public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + public static final int SOURCE_CODEC_TYPE_LDAC = 4; public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; -- GitLab From e692bd35430c6a52ba59a734fdc37925fce5d30e Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Tue, 24 Jan 2017 14:09:59 -0800 Subject: [PATCH 0680/1408] Bluetooth: add getDiscoveryEndMillis() call Method to tell when the adapter finished (or will finish) being in discovery mode. Test: compiles and still can scan Bug: 34395439 Change-Id: I41b48c2b934c0a1d5e1727cec08f3f762e3cb309 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 20 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f9be3a1d7f0..9302cbc480e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1183,6 +1183,25 @@ public final class BluetoothAdapter { } } + /** + * Get the end time of the latest remote device discovery process. + * @return the latest time that the bluetooth adapter was/will be in discovery mode, + * in milliseconds since the epoch. + * This time can be in the future if {@link #startDiscovery()} has been called recently. + * @hide + */ + public long getDiscoveryEndMillis() { + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getDiscoveryEndMillis(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return -1; + } + /** * Start the remote device discovery process. *

        The discovery process usually involves an inquiry scan of about 12 diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 7c5458b7704..53fef2add34 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -52,6 +52,7 @@ interface IBluetooth boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); + long getDiscoveryEndMillis(); int getAdapterConnectionState(); int getProfileConnectionState(int profile); -- GitLab From f6d543a7d0e6919a7d9055f4911d57e209e3e9db Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 25 Jan 2017 16:54:07 -0800 Subject: [PATCH 0681/1408] Update the A2DP Codec Config API Previously, the JNI upcall would contain only the current codec config. In the new API, the upcall contains: 1. The current codec config 2. The list of codecs containing the local codecs capabilities 3. The list of codecs containing the selectable codecs capabilities. This list is the intersection of the local codecs capabilities and the capabilities of the paired device. Also, refactored the Java internals to accomodate the extra information: * Added new class BluetoothCodecStatus that contains the extra info: current codec config, local codecs capabilities and selectable codecs capabilities * Renamed method getCodecConfig() to getCodecStatus() and return the corresponding BluetoothCodecStatus object. * Updates to class BluetoothCodecConfig: new methods isValid(), getCodecName(), and updated toString() so it is more user friendly * Removed BluetoothCodecConfig.EXTRA_CODEC_CONFIG and EXTRA_PREVIOUS_CODEC_CONFIG. The former is superseded by BluetoothCodecStatus.EXTRA_CODEC_STATUS; the latter is not really used. Test: A2DP streaming with headsets and switching the codecs Change-Id: Ia1af2c22e521e863e28a360610aca49f7e62d31b --- .../java/android/bluetooth/BluetoothA2dp.java | 17 +-- .../bluetooth/BluetoothCodecConfig.java | 143 ++++++++++++++---- .../bluetooth/BluetoothCodecStatus.aidl | 19 +++ .../bluetooth/BluetoothCodecStatus.java | 134 ++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 3 +- 5 files changed, 278 insertions(+), 38 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothCodecStatus.aidl create mode 100644 framework/java/android/bluetooth/BluetoothCodecStatus.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1165fce3ce0..4960159db21 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -105,10 +105,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * Intent used to broadcast the change in the Audio Codec state of the * A2DP Source profile. * - *

        This intent will have 3 extras: + *

        This intent will have 2 extras: *

          - *
        • {@link #EXTRA_CODEC_CONFIG} - The current codec configuration.
        • - *
        • {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration.
        • + *
        • {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status.
        • *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently * connected, otherwise it is not included.
        • *
        @@ -564,24 +563,24 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * Gets the current codec configuration. + * Gets the current codec status (configuration and capability). * - * @return the current codec configuration + * @return the current codec status * @hide */ - public BluetoothCodecConfig getCodecConfig() { - if (DBG) Log.d(TAG, "getCodecConfig"); + public BluetoothCodecStatus getCodecStatus() { + if (DBG) Log.d(TAG, "getCodecStatus"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - return mService.getCodecConfig(); + return mService.getCodecStatus(); } if (mService == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getCodecConfig()", e); + Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; } finally { mServiceLock.readLock().unlock(); diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index a37a0b38aa7..a48210340e6 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -29,24 +29,6 @@ import java.util.Objects; * {@hide} */ public final class BluetoothCodecConfig implements Parcelable { - - /** - * Extra for the codec configuration intents of the individual profiles. - * - * This extra represents the current codec configuration of the A2DP - * profile. - */ - public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG"; - - /** - * Extra for the codec configuration intents of the individual profiles. - * - * This extra represents the previous codec configuration of the A2DP - * profile. - */ - public static final String EXTRA_PREVIOUS_CODEC_CONFIG = - "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG"; - // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h @@ -128,13 +110,93 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); } + /** + * Checks whether the object contains valid codec configuration. + * + * @return true if the object contains valid codec configuration, + * otherwise false. + */ + public boolean isValid() { + return (mSampleRate != SAMPLE_RATE_NONE) && + (mBitsPerSample != BITS_PER_SAMPLE_NONE) && + (mChannelMode != CHANNEL_MODE_NONE); + } + + /** + * Adds capability string to an existing string. + * + * @param prevStr the previous string with the capabilities. Can be + * a null pointer. + * @param capStr the capability string to append to prevStr argument. + * @return the result string in the form "prevStr|capStr". + */ + private static String appendCapabilityToString(String prevStr, + String capStr) { + if (prevStr == null) { + return capStr; + } + return prevStr + "|" + capStr; + } + @Override public String toString() { - return "{mCodecType:" + mCodecType + + String sampleRateStr = null; + if (mSampleRate == SAMPLE_RATE_NONE) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE"); + } + if ((mSampleRate & SAMPLE_RATE_44100) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "44100"); + } + if ((mSampleRate & SAMPLE_RATE_48000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "48000"); + } + if ((mSampleRate & SAMPLE_RATE_88200) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "88200"); + } + if ((mSampleRate & SAMPLE_RATE_96000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "96000"); + } + if ((mSampleRate & SAMPLE_RATE_176400) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "176400"); + } + if ((mSampleRate & SAMPLE_RATE_192000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "192000"); + } + + String bitsPerSampleStr = null; + if (mBitsPerSample == BITS_PER_SAMPLE_NONE) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32"); + } + + String channelModeStr = null; + if (mChannelMode == CHANNEL_MODE_NONE) { + channelModeStr = appendCapabilityToString(channelModeStr, "NONE"); + } + if ((mChannelMode & CHANNEL_MODE_MONO) != 0) { + channelModeStr = appendCapabilityToString(channelModeStr, "MONO"); + } + if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) { + channelModeStr = appendCapabilityToString(channelModeStr, "STEREO"); + } + + return "{codecName:" + getCodecName() + + ",mCodecType:" + mCodecType + ",mCodecPriority:" + mCodecPriority + ",mSampleRate:" + String.format("0x%x", mSampleRate) + + "(" + sampleRateStr + ")" + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + + "(" + bitsPerSampleStr + ")" + ",mChannelMode:" + String.format("0x%x", mChannelMode) + + "(" + channelModeStr + ")" + ",mCodecSpecific1:" + mCodecSpecific1 + ",mCodecSpecific2:" + mCodecSpecific2 + ",mCodecSpecific3:" + mCodecSpecific3 + @@ -181,7 +243,32 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec type. + * Gets the codec name. + * + * @return the codec name + */ + public String getCodecName() { + switch (mCodecType) { + case SOURCE_CODEC_TYPE_SBC: + return "SBC"; + case SOURCE_CODEC_TYPE_AAC: + return "AAC"; + case SOURCE_CODEC_TYPE_APTX: + return "aptX"; + case SOURCE_CODEC_TYPE_APTX_HD: + return "aptX HD"; + case SOURCE_CODEC_TYPE_LDAC: + return "LDAC"; + case SOURCE_CODEC_TYPE_INVALID: + return "INVALID CODEC"; + default: + break; + } + return "UNKNOWN CODEC(" + mCodecType + ")"; + } + + /** + * Gets the codec type. * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}. * * @return the codec type @@ -191,7 +278,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec selection priority. + * Gets the codec selection priority. * The codec selection priority is relative to other codecs: larger value * means higher priority. If 0, reset to default. * @@ -202,7 +289,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec sample rate. The value can be a bitmask with all + * Gets the codec sample rate. The value can be a bitmask with all * supported sample rates: * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or @@ -219,7 +306,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec bits per sample. The value can be a bitmask with all + * Gets the codec bits per sample. The value can be a bitmask with all * bits per sample supported: * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or @@ -233,7 +320,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec channel mode. The value can be a bitmask with all + * Gets the codec channel mode. The value can be a bitmask with all * supported channel modes: * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or @@ -246,7 +333,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value1. + * Gets a codec specific value1. * * @return a codec specific value1. */ @@ -255,7 +342,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value2. + * Gets a codec specific value2. * * @return a codec specific value2 */ @@ -264,7 +351,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value3. + * Gets a codec specific value3. * * @return a codec specific value3 */ @@ -273,7 +360,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value4. + * Gets a codec specific value4. * * @return a codec specific value4 */ diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl b/framework/java/android/bluetooth/BluetoothCodecStatus.aidl new file mode 100644 index 00000000000..f9c3a3de2f4 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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; + +parcelable BluetoothCodecStatus; diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java new file mode 100644 index 00000000000..c8cd8d17ce3 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents the codec status (configuration and capability) for a Bluetooth + * A2DP source device. + * + * {@see BluetoothA2dp} + * + * {@hide} + */ +public final class BluetoothCodecStatus implements Parcelable { + /** + * Extra for the codec configuration intents of the individual profiles. + * + * This extra represents the current codec status of the A2DP + * profile. + */ + public static final String EXTRA_CODEC_STATUS = + "android.bluetooth.codec.extra.CODEC_STATUS"; + + private final BluetoothCodecConfig mCodecConfig; + private final BluetoothCodecConfig[] mCodecsLocalCapabilities; + private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; + + public BluetoothCodecStatus(BluetoothCodecConfig codecConfig, + BluetoothCodecConfig[] codecsLocalCapabilities, + BluetoothCodecConfig[] codecsSelectableCapabilities) { + mCodecConfig = codecConfig; + mCodecsLocalCapabilities = codecsLocalCapabilities; + mCodecsSelectableCapabilities = codecsSelectableCapabilities; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothCodecStatus) { + BluetoothCodecStatus other = (BluetoothCodecStatus)o; + return (Objects.equals(other.mCodecConfig, mCodecConfig) && + Objects.equals(other.mCodecsLocalCapabilities, + mCodecsLocalCapabilities) && + Objects.equals(other.mCodecsSelectableCapabilities, + mCodecsSelectableCapabilities)); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, + mCodecsLocalCapabilities); + } + + @Override + public String toString() { + return "{mCodecConfig:" + mCodecConfig + + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + "}"; + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothCodecStatus createFromParcel(Parcel in) { + final BluetoothCodecConfig codecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); + + return new BluetoothCodecStatus(codecConfig, + codecsLocalCapabilities, + codecsSelectableCapabilities); + } + public BluetoothCodecStatus[] newArray(int size) { + return new BluetoothCodecStatus[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeTypedObject(mCodecConfig, 0); + out.writeTypedArray(mCodecsLocalCapabilities, 0); + out.writeTypedArray(mCodecsSelectableCapabilities, 0); + } + + /** + * Gets the current codec configuration. + * + * @return the current codec configuration + */ + public BluetoothCodecConfig getCodecConfig() { + return mCodecConfig; + } + + /** + * Gets the codecs local capabilities. + * + * @return an array with the codecs local capabilities + */ + public BluetoothCodecConfig[] getCodecsLocalCapabilities() { + return mCodecsLocalCapabilities; + } + + /** + * Gets the codecs selectable capabilities. + * + * @return an array with the codecs selectable capabilities + */ + public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { + return mCodecsSelectableCapabilities; + } +} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index 5b524eb18a5..dbb5b7d7944 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.BluetoothCodecConfig; +import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; /** @@ -37,6 +38,6 @@ interface IBluetoothA2dp { oneway void adjustAvrcpAbsoluteVolume(int direction); oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); - BluetoothCodecConfig getCodecConfig(); + BluetoothCodecStatus getCodecStatus(); oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); } -- GitLab From 04b4c7bf98cdce8688d97a393f44362e3c376afb Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Sun, 22 Jan 2017 13:52:51 -0800 Subject: [PATCH 0682/1408] CompanionDeviceManager This introduces an API for apps that support companion devices to provide a more streamlined flow for pairing and setting up the device Bug: 30932767 Test: Using a toy app, invoke the newly introduced API (CompanionDeviceManager), and go through the flow. Ensure filtering works, and device is returned to the calling app. Ensure the calling app can pair to the selected device. Change-Id: I0aeb653afd65e4adead13ea9c7248ec20971b04a --- .../java/android/bluetooth/le/ScanFilter.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 17a802d59f4..b89c64a8cac 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -67,7 +67,9 @@ public final class ScanFilter implements Parcelable { private final byte[] mManufacturerData; @Nullable private final byte[] mManufacturerDataMask; - private static final ScanFilter EMPTY = new ScanFilter.Builder().build() ; + + /** @hide */ + public static final ScanFilter EMPTY = new ScanFilter.Builder().build() ; private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, @@ -318,8 +320,12 @@ public final class ScanFilter implements Parcelable { return true; } - // Check if the uuid pattern is contained in a list of parcel uuids. - private boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, + /** + * Check if the uuid pattern is contained in a list of parcel uuids. + * + * @hide + */ + public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, List uuids) { if (uuid == null) { return true; @@ -338,7 +344,7 @@ public final class ScanFilter implements Parcelable { } // Check if the uuid pattern matches the particular service uuid. - private boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { + private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { if (mask == null) { return uuid.equals(data); } -- GitLab From 23f591d8e8c7009f314f227db5bb16afe1d8be5d Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Sun, 5 Feb 2017 15:45:06 -0800 Subject: [PATCH 0683/1408] Add a mechanism to configure the default A2DP codec priorities Previously, the relative codec priorities were hard-codec internally. The new mechanism uses the following configurable resources in packages/apps/Bluetooth/res/values/config.xml to re-assign the default codec priorities per device, or to explicitly disable a codec. - a2dp_source_codec_priority_sbc - a2dp_source_codec_priority_aac - a2dp_source_codec_priority_aptx - a2dp_source_codec_priority_aptx_hd - a2dp_source_codec_priority_ldac Those values are assigned on startup. Also, they can be changed per device by using an overlay: device///overlay/packages/apps/Bluetooth/res/values/config.xml Test: Manually streaming to a headset Change-Id: Ic4da3a51ac73f00cbae731156cb7878c8fadee06 --- framework/java/android/bluetooth/BluetoothCodecConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index a48210340e6..176e48fb6e0 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -37,9 +37,11 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int SOURCE_CODEC_TYPE_APTX = 2; public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; public static final int SOURCE_CODEC_TYPE_LDAC = 4; + public static final int SOURCE_CODEC_TYPE_MAX = 5; public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + public static final int CODEC_PRIORITY_DISABLED = -1; public static final int CODEC_PRIORITY_DEFAULT = 0; public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -72,7 +74,7 @@ public final class BluetoothCodecConfig implements Parcelable { public BluetoothCodecConfig(int codecType, int codecPriority, int sampleRate, int bitsPerSample, - int channelMode,long codecSpecific1, + int channelMode, long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { mCodecType = codecType; -- GitLab From 2e57588a3beaf78cdd0269e2bb89d2ea1f22603c Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Fri, 24 Feb 2017 11:06:33 -0800 Subject: [PATCH 0684/1408] Bluetooth: fix enable log: display 24h time Test: adb bugreport at 1pm, observe 13:xx:yy instead of 01:xx:yy Change-Id: I172dcc63f0f43563ec692349a9434f63c55eac25 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 3b3ce073e09..66576b5b866 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -166,7 +166,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String toString() { - return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; } -- GitLab From 8ba9f62d6ed501b221ae66b89ee2dacdf13c411f Mon Sep 17 00:00:00 2001 From: Srinivas Visvanathan Date: Fri, 3 Mar 2017 09:57:18 -0800 Subject: [PATCH 0685/1408] 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(+) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 425248224e1..7d8459cb48c 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; -- GitLab From 391254850acb75735fc18f3e38ff20020d9acab6 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 22 Feb 2017 12:23:15 -0800 Subject: [PATCH 0686/1408] Bluetooth: Use content observer for airplane mode We are making this switch as the airplane mode switch intent is going away. Fix: 35256299 Test: Toggle Airplane Mode Change-Id: I3b1e5bbdf689b0db98cfbb0ab377198d34f0ba05 --- .../bluetooth/BluetoothManagerService.java | 135 +++++++++--------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index e8df38bb964..82d86ffd95c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -82,6 +82,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_SYSTEM_BOOT = "system boot"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save //Maximum msec to wait for service restart @@ -195,19 +197,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final boolean mPermissionReviewRequired; - private void registerForAirplaneMode(IntentFilter filter) { - final ContentResolver resolver = mContext.getContentResolver(); - final String airplaneModeRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_RADIOS); - final String toggleableRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); - boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : - airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH); - if (mIsAirplaneSensitive) { - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - } - } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { @@ -240,6 +229,62 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { + @Override + public void onChange(boolean unused) { + synchronized(this) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + } + + int st = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + st = mBluetooth.getState(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return; + } finally { + mBluetoothLock.readLock().unlock(); + } + + Slog.d(TAG, "Airplane Mode change - current state: " + + BluetoothAdapter.nameForState(st)); + + if (isAirplaneModeOn()) { + // Clear registered LE apps to force shut-off + clearBleApps(); + + // If state is BLE_ON make sure we trigger disableBLE + if (st == BluetoothAdapter.STATE_BLE_ON) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + mEnable = false; + mEnableExternal = false; + } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + } else if (st == BluetoothAdapter.STATE_ON){ + sendDisableMsg(REASON_AIRPLANE_MODE); + } + } else if (mEnableExternal) { + sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -250,58 +295,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } - } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { - synchronized(mReceiver) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - } - - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - Slog.d(TAG, "Airplane Mode change - current state: " + - BluetoothAdapter.nameForState(st)); - - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); - if (st == BluetoothAdapter.STATE_BLE_ON) { - //if state is BLE_ON make sure you trigger disableBLE part - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.onBrEdrDown(); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } else if (st == BluetoothAdapter.STATE_ON){ - // disable without persisting the setting - Slog.d(TAG, "Calling disable"); - sendDisableMsg("airplane mode"); - } - } else if (mEnableExternal) { - // enable without persisting the setting - Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal, "airplane mode"); - } - } } } }; @@ -333,7 +326,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - registerForAirplaneMode(filter); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); @@ -342,6 +334,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } + String airplaneModeRadios = Settings.Global.getString(mContentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS); + if (airplaneModeRadios == null || + airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) { + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), + true, mAirplaneModeObserver); + } + int systemUiUid = -1; try { systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", @@ -973,7 +974,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal, "system boot"); + sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); -- GitLab From 335e25c4291e90d490da28f8df15b96cf07fafb7 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 22 Feb 2017 12:23:15 -0800 Subject: [PATCH 0687/1408] Bluetooth: Use content observer for airplane mode We are making this switch as the airplane mode switch intent is going away. Fix: 35256299 Test: Toggle Airplane Mode Change-Id: I3b1e5bbdf689b0db98cfbb0ab377198d34f0ba05 --- .../bluetooth/BluetoothManagerService.java | 134 +++++++++--------- 1 file changed, 68 insertions(+), 66 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 66576b5b866..aff98edf586 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -81,6 +81,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_SYSTEM_BOOT = "system boot"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save //Maximum msec to wait for service restart @@ -194,19 +196,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final boolean mPermissionReviewRequired; - private void registerForAirplaneMode(IntentFilter filter) { - final ContentResolver resolver = mContext.getContentResolver(); - final String airplaneModeRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_RADIOS); - final String toggleableRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); - boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : - airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH); - if (mIsAirplaneSensitive) { - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - } - } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { @@ -239,6 +228,62 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { + @Override + public void onChange(boolean unused) { + synchronized(this) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + } + + int st = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + st = mBluetooth.getState(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return; + } finally { + mBluetoothLock.readLock().unlock(); + } + + Slog.d(TAG, "Airplane Mode change - current state: " + + BluetoothAdapter.nameForState(st)); + + if (isAirplaneModeOn()) { + // Clear registered LE apps to force shut-off + clearBleApps(); + + // If state is BLE_ON make sure we trigger disableBLE + if (st == BluetoothAdapter.STATE_BLE_ON) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + mEnable = false; + mEnableExternal = false; + } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + } else if (st == BluetoothAdapter.STATE_ON){ + sendDisableMsg(REASON_AIRPLANE_MODE); + } + } else if (mEnableExternal) { + sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -249,57 +294,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } - } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { - synchronized(mReceiver) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - } - - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - Slog.d(TAG, "State " + BluetoothAdapter.nameForState(st)); - - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); - if (st == BluetoothAdapter.STATE_BLE_ON) { - //if state is BLE_ON make sure you trigger disableBLE part - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.onBrEdrDown(); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } else if (st == BluetoothAdapter.STATE_ON){ - // disable without persisting the setting - Slog.d(TAG, "Calling disable"); - sendDisableMsg("airplane mode"); - } - } else if (mEnableExternal) { - // enable without persisting the setting - Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal, "airplane mode"); - } - } } } }; @@ -332,7 +326,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - registerForAirplaneMode(filter); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); @@ -340,6 +333,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } + String airplaneModeRadios = Settings.Global.getString(mContentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS); + if (airplaneModeRadios == null || + airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) { + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), + true, mAirplaneModeObserver); + } + int systemUiUid = -1; try { systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", @@ -958,7 +960,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal, "system boot"); + sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); -- GitLab From ae076ee2c3cf6db8ef980110952426bb73fa98d6 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Mon, 27 Feb 2017 16:55:07 -0800 Subject: [PATCH 0688/1408] BluetoothManagerService: continue teardown when BLE apps registered When the adapter is toggled off; when there are BLE apps registered, a request is sent to unregister all of the BLE apps. Upon unregister each app will call through to this function via a callback. When this gets called if the ble app count is 0, we can disable everything. I am keeping track of the flag so that we know explicitly where we are coming from. Bug: 34707848 Test: Compile Change-Id: I9a8322a6fa918e7b410770894c257ec9f7d8d1f5 --- .../bluetooth/BluetoothManagerService.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aff98edf586..5e9cf748182 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -638,6 +638,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (appCount == 0 && mEnable) { disableBleScanMode(); } + if (appCount == 0 && !mEnableExternal) { + sendBrEdrDownCallback(); + } return appCount; } @@ -693,7 +696,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return; } - if (isBleAppPresent() == false) { + if (isBleAppPresent()) { + // Need to stay at BLE ON. Disconnect all Gatt connections + try { + mBluetoothGatt.unregAll(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to disconnect all apps.", e); + } + } else { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) mBluetooth.onBrEdrDown(); @@ -702,14 +712,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - } else { - // Need to stay at BLE ON. Disconnect all Gatt connections - try { - mBluetoothGatt.unregAll(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to disconnect all apps.", e); - } } + } public boolean enableNoAutoConnect(String packageName) -- GitLab From 0849d40e0b2c0f91ac5c161f672c4f17d424eec0 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Fri, 3 Feb 2017 16:38:59 +0530 Subject: [PATCH 0689/1408] Bluetooth: Expose L2CAP API to support OPP 1.2 Add changes to expose L2CAP API to create an insecure L2CAP socket for supporting OPP 1.2. Test: Connect with Remote OPP Client supporting OPP 1.2 and verify that connection and transfer happens over L2CAP. Connect with Remote OPP Client supporting OPP 1.1 and verify that connection and transfer happens over RFCOMM. Bug: 33010988 Change-Id: I21ed672afb4ed5d2355ff0a0f9691af220921c1f --- .../android/bluetooth/BluetoothAdapter.java | 29 +++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 21 ++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9302cbc480e..dbc25afcd08 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1858,6 +1858,35 @@ public final class BluetoothAdapter { return listenUsingL2capOn(port, false, false); } + + /** + * Construct an insecure L2CAP server socket. + * Call #accept to retrieve connections to this socket. + *

        To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_L2CAP, false, false, port, false, false); + int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); + } + return socket; + + } + /** * Read the local Out of Band Pairing Data *

        Requires {@link android.Manifest.permission#BLUETOOTH} diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5c9e2ee4fc4..7b1e687f686 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1411,6 +1411,27 @@ public final class BluetoothDevice implements Parcelable { null); } + /** + * Create an L2cap {@link BluetoothSocket} ready to start an insecure + * outgoing connection to this remote device on given channel. + *

        The remote device will be not authenticated and communication on this + * socket will not be encrypted. + *

        Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. + *

        Valid L2CAP PSM channels are in range 1 to 2^16. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param channel L2cap PSM/channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + * @hide + */ + public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, + null); + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device using SDP lookup of uuid. -- GitLab From 5db7a4c0968b63f99cdcb24a6520352d2817c92e Mon Sep 17 00:00:00 2001 From: Srinivas Visvanathan Date: Tue, 7 Mar 2017 10:22:53 -0800 Subject: [PATCH 0690/1408] 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(-) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 7d8459cb48c..ccab3cdf0b6 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 = -- GitLab From d50e39090d109095e68057c46e0b5a98a650a490 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 7 Feb 2017 18:05:39 -0800 Subject: [PATCH 0691/1408] Bluetooth 5 feature check API (1/2) Bug: 30622771 Test: manual Change-Id: I90e2efe989745c07c2f2fb8f4ea5bc3b718382f6 --- .../android/bluetooth/BluetoothAdapter.java | 72 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 ++ 2 files changed, 76 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 45347677243..3f7e63feb6d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1384,6 +1384,78 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if LE 2M PHY feature is supported. + * + * @return true if chipset supports LE 2M PHY feature + */ + public boolean isLe2MPhySupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLe2MPhySupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Coded PHY feature is supported. + * + * @return true if chipset supports LE Coded PHY feature + */ + public boolean isLeCodedPhySupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLeCodedPhySupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Periodic Advertising feature is supported. + * + * @return true if chipset supports LE Periodic Advertising feature + */ + public boolean isLeExtendedAdvertisingSupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLeExtendedAdvertisingSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Periodic Advertising feature is supported. + * + * @return true if chipset supports LE Periodic Advertising feature + */ + public boolean isLePeriodicAdvertisingSupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLePeriodicAdvertisingSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + /** * Return true if hardware has entries available for matching beacons * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 53fef2add34..76ca554e598 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -104,6 +104,10 @@ interface IBluetooth boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); + boolean isLe2MPhySupported(); + boolean isLeCodedPhySupported(); + boolean isLeExtendedAdvertisingSupported(); + boolean isLePeriodicAdvertisingSupported(); BluetoothActivityEnergyInfo reportActivityInfo(); /** -- GitLab From fe30c47a22eeb404f30235310880aea832064199 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Jan 2017 07:50:46 -0800 Subject: [PATCH 0692/1408] Bluetooth 5 enhanced scanning API Bug: 30622771 Test: manual Change-Id: I2c8065fbcedf48777ce18c7d8fe621e568b3fd75 --- .../java/android/bluetooth/le/ScanResult.java | 205 +++++++++++++++++- .../android/bluetooth/le/ScanSettings.java | 86 +++++++- 2 files changed, 278 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 2fdfe7f8a83..f92357b59c7 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -27,7 +27,56 @@ import java.util.Objects; * ScanResult for Bluetooth LE scan. */ public final class ScanResult implements Parcelable { - // Remote bluetooth device. + + /** + * For chained advertisements, inidcates tha the data contained in this + * scan result is complete. + */ + public static final int DATA_COMPLETE = 0x00; + + /** + * For chained advertisements, indicates that the controller was + * unable to receive all chained packets and the scan result contains + * incomplete truncated data. + */ + public static final int DATA_TRUNCATED = 0x02; + + /** + * Indicates that the secondary physical layer was not used. + */ + public static final int PHY_UNUSED = 0x00; + + /** + * Bluetooth LE 1Mbit advertiser PHY. + */ + public static final int PHY_LE_1M = 0x01; + + /** + * Bluetooth LE 2Mbit advertiser PHY. + */ + public static final int PHY_LE_2M = 0x02; + + /** + * Bluetooth LE Coded advertiser PHY. + */ + public static final int PHY_LE_CODED = 0x03; + + /** + * Advertising Set ID is not present in the packet. + */ + public static final int SID_NOT_PRESENT = 0xFF; + + /** + * Mask for checking wether event type represents legacy advertisement. + */ + private static final int ET_LEGACY_MASK = 0x10; + + /** + * Mask for checking wether event type represents connectable advertisement. + */ + private static final int ET_CONNECTABLE_MASK = 0x01; + + // Remote Bluetooth device. private BluetoothDevice mDevice; // Scan record, including advertising data and scan response data. @@ -40,13 +89,21 @@ public final class ScanResult implements Parcelable { // Device timestamp when the result was last seen. private long mTimestampNanos; + private int mEventType; + private int mPrimaryPhy; + private int mSecondaryPhy; + private int mAdvertisingSid; + private int mTxPower; + private int mPeriodicAdvertisingInterval; + /** - * Constructor of scan result. + * Constructs a new ScanResult. * - * @param device Remote bluetooth device that is found. + * @param device Remote Bluetooth device found. * @param scanRecord Scan record including both advertising data and scan response data. * @param rssi Received signal strength. - * @param timestampNanos Device timestamp when the scan result was observed. + * @param timestampNanos Timestamp at which the scan result was observed. + * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)} */ public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos) { @@ -54,6 +111,41 @@ public final class ScanResult implements Parcelable { mScanRecord = scanRecord; mRssi = rssi; mTimestampNanos = timestampNanos; + mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK; + mPrimaryPhy = PHY_LE_1M; + mSecondaryPhy = PHY_UNUSED; + mAdvertisingSid = SID_NOT_PRESENT; + mTxPower = 127; + mPeriodicAdvertisingInterval = 0; + } + + /** + * Constructs a new ScanResult. + * + * @param device Remote Bluetooth device found. + * @param eventType Event type. + * @param primaryPhy Primary advertising phy. + * @param secondaryPhy Secondary advertising phy. + * @param advertisingSid Advertising set ID. + * @param txPower Transmit power. + * @param rssi Received signal strength. + * @param periodicAdvertisingInterval Periodic advertising interval. + * @param scanRecord Scan record including both advertising data and scan response data. + * @param timestampNanos Timestamp at which the scan result was observed. + */ + public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy, + int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, + ScanRecord scanRecord, long timestampNanos) { + mDevice = device; + mEventType = eventType; + mPrimaryPhy = primaryPhy; + mSecondaryPhy = secondaryPhy; + mAdvertisingSid = advertisingSid; + mTxPower = txPower; + mRssi = rssi; + mPeriodicAdvertisingInterval = periodicAdvertisingInterval; + mScanRecord = scanRecord; + mTimestampNanos = timestampNanos; } private ScanResult(Parcel in) { @@ -76,6 +168,12 @@ public final class ScanResult implements Parcelable { } dest.writeInt(mRssi); dest.writeLong(mTimestampNanos); + dest.writeInt(mEventType); + dest.writeInt(mPrimaryPhy); + dest.writeInt(mSecondaryPhy); + dest.writeInt(mAdvertisingSid); + dest.writeInt(mTxPower); + dest.writeInt(mPeriodicAdvertisingInterval); } private void readFromParcel(Parcel in) { @@ -87,6 +185,12 @@ public final class ScanResult implements Parcelable { } mRssi = in.readInt(); mTimestampNanos = in.readLong(); + mEventType = in.readInt(); + mPrimaryPhy = in.readInt(); + mSecondaryPhy = in.readInt(); + mAdvertisingSid = in.readInt(); + mTxPower = in.readInt(); + mPeriodicAdvertisingInterval = in.readInt(); } @Override @@ -95,7 +199,7 @@ public final class ScanResult implements Parcelable { } /** - * Returns the remote bluetooth device identified by the bluetooth device address. + * Returns the remote Bluetooth device identified by the Bluetooth device address. */ public BluetoothDevice getDevice() { return mDevice; @@ -123,9 +227,79 @@ public final class ScanResult implements Parcelable { return mTimestampNanos; } + /** + * Returns true if this object represents legacy scan result. + * Legacy scan results do not contain advanced advertising information + * as specified in the Bluetooth Core Specification v5. + */ + public boolean isLegacy() { + return (mEventType & ET_LEGACY_MASK) != 0; + } + + /** + * Returns true if this object represents connectable scan result. + */ + public boolean isConnectable() { + return (mEventType & ET_CONNECTABLE_MASK) != 0; + } + + /** + * Returns the data status. + * Can be one of {@link ScanResult#DATA_COMPLETE} or + * {@link ScanResult#DATA_TRUNCATED}. + */ + public int getDataStatus() { + // return bit 5 and 6 + return (mEventType >> 5) & 0x03; + } + + /** + * Returns the primary Physical Layer + * on which this advertisment was received. + * Can be one of {@link ScanResult#PHY_LE_1M} or + * {@link ScanResult#PHY_LE_CODED}. + */ + public int getPrimaryPhy() { return mPrimaryPhy; } + + /** + * Returns the secondary Physical Layer + * on which this advertisment was received. + * Can be one of {@link ScanResult#PHY_LE_1M}, + * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED} + * or {@link ScanResult#PHY_UNUSED} - if the advertisement + * was not received on a secondary physical channel. + */ + public int getSecondaryPhy() { return mSecondaryPhy; } + + /** + * Returns the advertising set id. + * May return {@link ScanResult#SID_NOT_PRESENT} if + * no set id was is present. + */ + public int getAdvertisingSid() { return mAdvertisingSid; } + + /** + * Returns the transmit power in dBm. + * Valid range is [-127, 126]. A value of 127 indicates that the + * advertisement did not indicate TX power. + */ + public int getTxPower() { return mTxPower; } + + /** + * Returns the periodic advertising interval in units of 1.25ms. + * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means + * periodic advertising is not used for this scan result. + */ + public int getPeriodicAdvertisingInterval() { + return mPeriodicAdvertisingInterval; + } + @Override public int hashCode() { - return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos); + return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos, + mEventType, mPrimaryPhy, mSecondaryPhy, + mAdvertisingSid, mTxPower, + mPeriodicAdvertisingInterval); } @Override @@ -138,15 +312,24 @@ public final class ScanResult implements Parcelable { } ScanResult other = (ScanResult) obj; return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.equals(mScanRecord, other.mScanRecord) - && (mTimestampNanos == other.mTimestampNanos); + Objects.equals(mScanRecord, other.mScanRecord) && + (mTimestampNanos == other.mTimestampNanos) && + mEventType == other.mEventType && + mPrimaryPhy == other.mPrimaryPhy && + mSecondaryPhy == other.mSecondaryPhy && + mAdvertisingSid == other.mAdvertisingSid && + mTxPower == other.mTxPower && + mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; } @Override public String toString() { - return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + Objects.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos=" - + mTimestampNanos + '}'; + return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + + Objects.toString(mScanRecord) + ", rssi=" + mRssi + + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; } public static final Parcelable.Creator CREATOR = new Creator() { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index d61662469b6..69c9a8cece3 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -122,6 +122,24 @@ public final class ScanSettings implements Parcelable { @SystemApi public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; + /** + * Use the Bluetooth LE 1Mbit PHY for scanning. + */ + public static final int PHY_LE_1M = 1; + + /** + * Use Bluetooth LE Coded PHY for scanning. + */ + public static final int PHY_LE_CODED = 3; + + /** + * Use all supported PHYs for scanning. + * This will check the controller capabilities, and start + * the scan on 1Mbit and LE Coded PHYs if supported, or on + * the 1Mbit PHY only. + */ + public static final int PHY_LE_ALL_SUPPORTED = 255; + // Bluetooth LE scan mode. private int mScanMode; @@ -138,6 +156,11 @@ public final class ScanSettings implements Parcelable { private int mNumOfMatchesPerFilter; + // Include only legacy advertising results + private boolean mLegacy; + + private int mPhy; + public int getScanMode() { return mScanMode; } @@ -164,6 +187,22 @@ public final class ScanSettings implements Parcelable { return mNumOfMatchesPerFilter; } + /** + * Returns whether only legacy advertisements will be returned. + * Legacy advertisements include advertisements as specified + * by the Bluetooth core specification 4.2 and below. + */ + public boolean getLegacy() { + return mLegacy; + } + + /** + * Returns the physical layer used during a scan. + */ + public int getPhy() { + return mPhy; + } + /** * Returns report delay timestamp based on the device clock. */ @@ -172,13 +211,16 @@ public final class ScanSettings implements Parcelable { } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) { + long reportDelayMillis, int matchMode, + int numOfMatchesPerFilter, boolean legacy, int phy) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; mNumOfMatchesPerFilter = numOfMatchesPerFilter; mMatchMode = matchMode; + mLegacy = legacy; + mPhy = phy; } private ScanSettings(Parcel in) { @@ -188,6 +230,8 @@ public final class ScanSettings implements Parcelable { mReportDelayMillis = in.readLong(); mMatchMode = in.readInt(); mNumOfMatchesPerFilter = in.readInt(); + mLegacy = in.readInt() != 0 ? true : false; + mPhy = in.readInt(); } @Override @@ -198,6 +242,8 @@ public final class ScanSettings implements Parcelable { dest.writeLong(mReportDelayMillis); dest.writeInt(mMatchMode); dest.writeInt(mNumOfMatchesPerFilter); + dest.writeInt(mLegacy ? 1 : 0); + dest.writeInt(mPhy); } @Override @@ -228,6 +274,9 @@ public final class ScanSettings implements Parcelable { private long mReportDelayMillis = 0; private int mMatchMode = MATCH_MODE_AGGRESSIVE; private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; + private boolean mLegacy = true; + private int mPhy = PHY_LE_ALL_SUPPORTED; + /** * Set scan mode for Bluetooth LE scan. * @@ -340,12 +389,45 @@ public final class ScanSettings implements Parcelable { return this; } + /** + * Set whether only legacy advertisments should be returned in scan results. + * Legacy advertisements include advertisements as specified by the + * Bluetooth core specification 4.2 and below. This is true by default + * for compatibility with older apps. + * + * @param legacy true if only legacy advertisements will be returned + */ + public Builder setLegacy(boolean legacy) { + mLegacy = legacy; + return this; + } + + /** + * Set the Physical Layer to use during this scan. + * This is used only if {@link ScanSettings.Builder#setLegacy} + * is set to false. + * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported} + * may be used to check whether LE Coded phy is supported by calling + * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}. + * Selecting an unsupported phy will result in failure to start scan. + * + * @param phy Can be one of + * {@link ScanSettings#PHY_LE_1M}, + * {@link ScanSettings#PHY_LE_CODED} or + * {@link ScanSettings#PHY_LE_ALL_SUPPORTED} + */ + public Builder setPhy(int phy) { + mPhy = phy; + return this; + } + /** * Build {@link ScanSettings}. */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter); + mReportDelayMillis, mMatchMode, + mNumOfMatchesPerFilter, mLegacy, mPhy); } } } -- GitLab From 70dfee6397c2da222ce53a8c48fd82fd7f3d801e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 16 Jan 2017 07:21:01 -0800 Subject: [PATCH 0693/1408] Bluetooth 5 periodc scan API (1/2) Bug: 30622771 Test: manual Change-Id: I61853bc71f6013e9406d1d151bb51ea4484bb92c --- .../android/bluetooth/BluetoothAdapter.java | 26 ++ .../android/bluetooth/IBluetoothGatt.aidl | 5 + .../le/IPeriodicAdvertisingCallback.aidl | 31 +++ .../le/PeriodicAdvertisingCallback.java | 77 ++++++ .../le/PeriodicAdvertisingManager.java | 237 ++++++++++++++++++ .../le/PeriodicAdvertisingReport.aidl | 19 ++ .../le/PeriodicAdvertisingReport.java | 184 ++++++++++++++ 7 files changed, 579 insertions(+) create mode 100644 framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c403487e609..d36692a74a0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.PeriodicAdvertisingManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; @@ -525,6 +526,7 @@ public final class BluetoothAdapter { private static BluetoothLeScanner sBluetoothLeScanner; private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; + private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; private IBluetooth mService; @@ -629,6 +631,30 @@ public final class BluetoothAdapter { return sBluetoothLeAdvertiser; } + /** + * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising + * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic + * Advertising is not supported on this device. + *

        + * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is + * supported on this device before calling this method. + */ + public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { + if (!getLeAccess()) + return null; + + if (!isLePeriodicAdvertisingSupported()) + return null; + + synchronized (mLock) { + if (sPeriodicAdvertisingManager == null) { + sPeriodicAdvertisingManager = + new PeriodicAdvertisingManager(mManagerService); + } + } + return sPeriodicAdvertisingManager; + } + /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index aa2291e072d..69563cb3e6a 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; @@ -29,6 +30,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; +import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; /** @@ -53,6 +55,9 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); + void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); + void unregisterSync(in IPeriodicAdvertisingCallback callback); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl new file mode 100644 index 00000000000..a76c54d4ab4 --- /dev/null +++ b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.PeriodicAdvertisingReport; + +/** + * Callback definitions for interacting with Periodic Advertising + * @hide + */ +oneway interface IPeriodicAdvertisingCallback { + + void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, + in int skip, in int timeout, in int status); + void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); + void onSyncLost(in int syncHandle); +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java new file mode 100644 index 00000000000..6616231bcdf --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; + +/** + * Bluetooth LE periodic advertising callbacks, used to deliver periodic + * advertising operation status. + * + * @see PeriodicAdvertisingManager#createSync + */ +public abstract class PeriodicAdvertisingCallback { + + /** + * The requested operation was successful. + * + * @hide + */ + public static final int SYNC_SUCCESS = 0; + + /** + * Sync failed to be established because remote device did not respond. + */ + public static final int SYNC_NO_RESPONSE = 1; + + /** + * Sync failed to be established because controller can't support more syncs. + */ + public static final int SYNC_NO_RESOURCES = 2; + + + /** + * Callback when synchronization was established. + * + * @param syncHandle handle used to identify this synchronization. + * @param device remote device. + * @param advertisingSid synchronized advertising set id. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive in force. @see PeriodicAdvertisingManager#createSync + * @param timeout Synchronization timeout for the periodic advertising in force. One + * unit is 10ms. @see PeriodicAdvertisingManager#createSync + * @param timeout + * @param status operation status. + */ + public void onSyncEstablished(int syncHandle, BluetoothDevice device, + int advertisingSid, int skip, int timeout, + int status) {} + + /** + * Callback when periodic advertising report is received. + * + * @param report periodic advertising report. + */ + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {} + + /** + * Callback when periodic advertising synchronization was lost. + * + * @param syncHandle handle used to identify this synchronization. + */ + public void onSyncLost(int syncHandle) {} +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java new file mode 100644 index 00000000000..12c8a8c8ffd --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothManager; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * This class provides methods to perform periodic advertising related + * operations. An application can register for periodic advertisements using + * {@link PeriodicAdvertisingManager#registerSync}. + *

        + * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an + * instance of {@link PeriodicAdvertisingManager}. + *

        + * Note: Most of the methods here require + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + */ +public final class PeriodicAdvertisingManager { + + private static final String TAG = "PeriodicAdvertisingManager"; + + private static final int SKIP_MIN = 0; + private static final int SKIP_MAX = 499; + private static final int TIMEOUT_MIN = 10; + private static final int TIMEOUT_MAX = 16384; + + private static final int SYNC_STARTING = -1; + + private final IBluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + + /* maps callback, to callback wrapper and sync handle */ + Map callbackWrappers; + + /** + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. + * + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. + * @hide + */ + public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { + mBluetoothManager = bluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + callbackWrappers = new IdentityHashMap<>(); + } + + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One + * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code + * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback) { + registerSync(scanResult, skip, timeout, callback, null); + } + + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One + * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code + * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback, Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + if (scanResult == null) { + throw new IllegalArgumentException("scanResult can't be null"); + } + + if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { + throw new IllegalArgumentException("scanResult must contain a valid sid"); + } + + if (skip < SKIP_MIN || skip > SKIP_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), + skip, timeout, + PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); + return; + } + + if (handler == null) + handler = new Handler(Looper.getMainLooper()); + + IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); + callbackWrappers.put(callback, wrapped); + + try { + gatt.registerSync(scanResult, skip, timeout, wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register sync - ", e); + return; + } + } + + /** + * Cancel pending attempt to create sync, or terminate existing sync. + * + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code callback} is null, or not a properly + * registered callback. + */ + public void unregisterSync(PeriodicAdvertisingCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + return; + } + + IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); + if (wrapper == null) { + throw new IllegalArgumentException("callback was not properly registered"); + } + + try { + gatt.unregisterSync(wrapper); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel sync creation - ", e); + return; + } + } + + private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { + return new IPeriodicAdvertisingCallback.Stub() { + public void onSyncEstablished(int syncHandle, BluetoothDevice device, + int advertisingSid, int skip, int timeout, int status) { + + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout, + status); + + if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { + // App can still unregister the sync until notified it failed. Remove callback + // after app was notifed. + callbackWrappers.remove(callback); + } + } + }); + } + + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onPeriodicAdvertisingReport(report); + } + }); + } + + public void onSyncLost(int syncHandle) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncLost(syncHandle); + // App can still unregister the sync until notified it's lost. Remove callback after + // app was notifed. + callbackWrappers.remove(callback); + } + }); + } + }; + } +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl new file mode 100644 index 00000000000..547d09611fd --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable PeriodicAdvertisingReport; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java new file mode 100644 index 00000000000..3ff4ca58006 --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 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.le; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. + */ +public final class PeriodicAdvertisingReport implements Parcelable { + + /** + * The data returned is complete + */ + public static final int DATA_COMPLETE = 0; + + /** + * The data returned is incomplete. The controller was unsuccessfull to + * receive all chained packets, returning only partial data. + */ + public static final int DATA_INCOMPLETE_TRUNCATED = 2; + + private int syncHandle; + private int txPower; + private int rssi; + private int dataStatus; + + // periodic advertising data. + @Nullable + private ScanRecord data; + + // Device timestamp when the result was last seen. + private long timestampNanos; + + /** + * Constructor of periodic advertising result. + * + */ + public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi, + int dataStatus, ScanRecord data) { + this.syncHandle = syncHandle; + this.txPower = txPower; + this.rssi = rssi; + this.dataStatus = dataStatus; + this.data = data; + } + + private PeriodicAdvertisingReport(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(syncHandle); + dest.writeLong(txPower); + dest.writeInt(rssi); + dest.writeInt(dataStatus); + if (data != null) { + dest.writeInt(1); + dest.writeByteArray(data.getBytes()); + } else { + dest.writeInt(0); + } + } + + private void readFromParcel(Parcel in) { + syncHandle = in.readInt(); + txPower = in.readInt(); + rssi = in.readInt(); + dataStatus = in.readInt(); + if (in.readInt() == 1) { + data = ScanRecord.parseFromBytes(in.createByteArray()); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the synchronization handle. + */ + public int getSyncHandle() { + return syncHandle; + } + + /** + * Returns the transmit power in dBm. The valid range is [-127, 126]. Value + * of 127 means information was not available. + */ + public int getTxPower() { + return txPower; + } + + /** + * Returns the received signal strength in dBm. The valid range is [-127, 20]. + */ + public int getRssi() { + return rssi; + } + + /** + * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE} + * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}. + */ + public int getDataStatus() { + return dataStatus; + } + + /** + * Returns the data contained in this periodic advertising report. + */ + @Nullable + public ScanRecord getData() { + return data; + } + + /** + * Returns timestamp since boot when the scan record was observed. + */ + public long getTimestampNanos() { + return timestampNanos; + } + + @Override + public int hashCode() { + return Objects.hash(syncHandle, txPower, rssi, dataStatus, data, timestampNanos); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj; + return (syncHandle == other.syncHandle) && + (txPower == other.txPower) && + (rssi == other.rssi) && + (dataStatus == other.dataStatus) && + Objects.equals(data, other.data) && + (timestampNanos == other.timestampNanos); + } + + @Override + public String toString() { + return "PeriodicAdvertisingReport{syncHandle=" + syncHandle + + ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus + + ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}'; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public PeriodicAdvertisingReport createFromParcel(Parcel source) { + return new PeriodicAdvertisingReport(source); + } + + @Override + public PeriodicAdvertisingReport[] newArray(int size) { + return new PeriodicAdvertisingReport[size]; + } + }; +} -- GitLab From 8e4c473d0d8592f7fd6a18f768ea37ec3443034a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 2 Feb 2017 08:07:12 -0800 Subject: [PATCH 0694/1408] Bluetooth 5 PHY selection API Bug: 30622771 Test: manual Change-Id: I50262a56a70466439f9700549c3c0e7bd49e2e8d --- .../android/bluetooth/BluetoothDevice.java | 99 +++++++++- .../java/android/bluetooth/BluetoothGatt.java | 125 +++++++++--- .../bluetooth/BluetoothGattCallback.java | 132 +------------ .../bluetooth/BluetoothGattCallbackExt.java | 182 +++++++++++++++++ .../bluetooth/BluetoothGattServer.java | 96 ++++++++- .../BluetoothGattServerCallback.java | 138 +------------ .../BluetoothGattServerCallbackExt.java | 187 ++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 14 +- ...ck.aidl => IBluetoothGattCallbackExt.aidl} | 4 +- ...l => IBluetoothGattServerCallbackExt.aidl} | 6 +- 10 files changed, 689 insertions(+), 294 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothGattCallbackExt.java create mode 100644 framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java rename framework/java/android/bluetooth/{IBluetoothGattCallback.aidl => IBluetoothGattCallbackExt.aidl} (89%) rename framework/java/android/bluetooth/{IBluetoothGattServerCallback.aidl => IBluetoothGattServerCallbackExt.aidl} (88%) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7b1e687f686..31fc294e318 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -592,6 +592,42 @@ public final class BluetoothDevice implements Parcelable { */ public static final int TRANSPORT_LE = 2; + /** + * 1M initiating PHY. + */ + public static final int PHY_LE_1M = 1; + + /** + * 2M initiating PHY. + */ + public static final int PHY_LE_2M = 2; + + /** + * LE Coded initiating PHY. + */ + public static final int PHY_LE_CODED = 4; + + /** + * Any LE PHY. + */ + public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED; + + /** + * No preferred coding when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_NO_PREFERRED = 0; + + /** + * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_S2 = 1; + + /** + * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_S8 = 2; + + /** @hide */ public static final String EXTRA_MAS_INSTANCE = "android.bluetooth.device.extra.MAS_INSTANCE"; @@ -1615,6 +1651,67 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback, int transport) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, + * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if + * {@code autoConnect} is set to true. + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback, int transport, int phy) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1625,7 +1722,7 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport); + BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 12ebdac76d4..11dbf70e1f3 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -31,7 +31,7 @@ import java.util.UUID; *

        This class provides Bluetooth GATT functionality to enable communication * with Bluetooth Smart or Smart Ready devices. * - *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallback} + *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt} * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. * GATT capable devices can be discovered using the Bluetooth device discovery or BLE * scan process. @@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean VDBG = false; private IBluetoothGatt mService; - private BluetoothGattCallback mCallback; + private BluetoothGattCallbackExt mCallback; private int mClientIf; private BluetoothDevice mDevice; private boolean mAutoConnect; @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; private int mTransport; + private int mPhy; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -132,10 +133,10 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_MITM = 2; /** - * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. + * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation. */ - private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { + private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt = + new IBluetoothGattCallbackExt.Stub() { /** * Application interface registered - app is ready to go * @hide @@ -161,12 +162,50 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } } + /** + * Phy update callback + * @hide + */ + @Override + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + if (!address.equals(mDevice.getAddress())) { + return; + } + + try { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } + + /** + * Phy read callback + * @hide + */ + @Override + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyRead() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + if (!address.equals(mDevice.getAddress())) { + return; + } + + try { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } + /** * Client connection state changed * @hide @@ -503,10 +542,11 @@ public final class BluetoothGatt implements BluetoothProfile { }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport) { + int transport, int phy) { mService = iGatt; mDevice = device; mTransport = transport; + mPhy = phy; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -578,7 +618,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Register an application callback to start using GATT. * - *

        This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + *

        This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered} * is used to notify success or failure if the function returns true. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -587,7 +627,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return If true, the callback will be called to notify success or failure, * false on immediate error */ - private boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallbackExt callback) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -596,7 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -626,7 +666,7 @@ public final class BluetoothGatt implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect parameter determines whether to actively connect to @@ -644,7 +684,7 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) { if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); synchronized(mStateLock) { if (mConnState != CONN_STATE_IDLE) { @@ -696,7 +736,7 @@ public final class BluetoothGatt implements BluetoothProfile { public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), - false, mTransport); // autoConnect is inverse of "isDirect" + false, mTransport, mPhy); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -704,6 +744,45 @@ public final class BluetoothGatt implements BluetoothProfile { } } + /** + * Set the preferred connection PHY for this app. Please note that this is just a + * recommendation, wether the PHY change will happen depends on other applications peferences, + * local and remote controller capabilities. Controller can override these settings. + *

        + * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * if no PHY change happens. It is also triggered when remote device updates the PHY. + * + * @param txPhy preferred transmitter PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, + * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + */ + public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { + try { + mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, + phyOptions); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Read the current transmitter PHY and receiver PHY of the connection. The values are returned + * in {@link BluetoothGattCallbackExt#onPhyRead} + */ + public void readPhy() { + try { + mService.clientReadPhy(mClientIf, mDevice.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + /** * Return the remote bluetooth device this GATT client targets to * @@ -718,7 +797,7 @@ public final class BluetoothGatt implements BluetoothProfile { * characteristics and descriptors. * *

        This is an asynchronous operation. Once service discovery is completed, - * the {@link BluetoothGattCallback#onServicesDiscovered} callback is + * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is * triggered. If the discovery was successful, the remote services can be * retrieved using the {@link #getServices} function. * @@ -797,7 +876,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the requested characteristic from the associated remote device. * *

        This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead} * callback. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -839,7 +918,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Writes a given characteristic and its values to the associated remote device. * *

        Once the write operation has been completed, the - * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked, * reporting the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -883,7 +962,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the value for a given descriptor from the associated remote device. * *

        Once the read operation has been completed, the - * {@link BluetoothGattCallback#onDescriptorRead} callback is + * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is * triggered, signaling the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -924,7 +1003,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Write the value of a given descriptor to the associated remote device. * - *

        A {@link BluetoothGattCallback#onDescriptorWrite} callback is + *

        A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is * triggered to report the result of the write operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -968,7 +1047,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Once a reliable write transaction has been initiated, all calls * to {@link #writeCharacteristic} are sent to the remote device for * verification and queued up for atomic execution. The application will - * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback + * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback * in response to every {@link #writeCharacteristic} call and is responsible * for verifying if the value has been transmitted accurately. * @@ -1002,7 +1081,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        This function will commit all queued up characteristic write * operations for a given remote device. * - *

        A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is + *

        A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is * invoked to indicate whether the transaction has been executed correctly. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1060,7 +1139,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Enable or disable notifications/indications for a given characteristic. * *

        Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be + * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be * triggered if the remote device indicates that the given characteristic * has changed. * @@ -1115,7 +1194,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Read the RSSI for a connected remote device. * - *

        The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be + *

        The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be * invoked when the RSSI value has been read. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1143,7 +1222,7 @@ public final class BluetoothGatt implements BluetoothProfile { * the data sent is truncated to the MTU size. This function may be used * to request a larger MTU size to be able to send more data at once. * - *

        A {@link BluetoothGattCallback#onMtuChanged} callback will indicate + *

        A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate * whether this operation was successful. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index a9156205afc..4da106df610 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -18,138 +18,22 @@ package android.bluetooth; /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. + * @deprecated use {@link BluetoothGattCallbackExt} */ -public abstract class BluetoothGattCallback { +public abstract class BluetoothGattCallback extends BluetoothGattCallbackExt { /** - * Callback indicating when GATT client has connected/disconnected to/from a remote - * GATT server. - * - * @param gatt GATT client - * @param status Status of the connect or disconnect operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @hide */ - public void onConnectionStateChange(BluetoothGatt gatt, int status, - int newState) { + @Override + public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } /** - * Callback invoked when the list of remote services, characteristics and descriptors - * for the remote device have been updated, ie new services have been discovered. - * - * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device - * has been explored successfully. + * @hide */ - public void onServicesDiscovered(BluetoothGatt gatt, int status) { + @Override + public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } - /** - * Callback reporting the result of a characteristic read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} - * @param characteristic Characteristic that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully. - */ - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, - int status) { - } - - /** - * Callback indicating the result of a characteristic write operation. - * - *

        If this callback is invoked while a reliable write transaction is - * in progress, the value of the characteristic represents the value - * reported by the remote device. An application should compare this - * value to the desired value to be written. If the values don't match, - * the application must abort the reliable write transaction. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} - * @param characteristic Characteristic that was written to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - } - - /** - * Callback triggered as a result of a remote characteristic notification. - * - * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result - * of a remote notification event. - */ - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - } - - /** - * Callback reporting the result of a descriptor read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully - */ - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback indicating the result of a descriptor write operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} - * @param descriptor Descriptor that was writte to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback invoked when a reliable write transaction has been completed. - * - * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write - * transaction was executed successfully - */ - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the RSSI for a remote device connection. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#readRemoteRssi} function. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} - * @param rssi The RSSI value for the remote device - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully - */ - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#requestMtu} function, or in response to a connection - * event. - * - * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} - * @param mtu The new MTU size - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully - */ - public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { - } } diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattCallbackExt.java new file mode 100644 index 00000000000..63774c8fbb6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCallbackExt.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 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; + +/** + * This abstract class is used to implement {@link BluetoothGatt} callbacks. + */ +public abstract class BluetoothGattCallbackExt { + + /** + * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of + * remote device changing the PHY. + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation + */ + public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { + } + + /** + * Callback triggered as result of {@link BluetoothGatt#readPhy} + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation + */ + public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { + } + + /** + * Callback indicating when GATT client has connected/disconnected to/from a remote + * GATT server. + * + * @param gatt GATT client + * @param status Status of the connect or disconnect operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothGatt gatt, int status, + int newState) { + } + + /** + * Callback invoked when the list of remote services, characteristics and descriptors + * for the remote device have been updated, ie new services have been discovered. + * + * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device + * has been explored successfully. + */ + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the result of a characteristic read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} + * @param characteristic Characteristic that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully. + */ + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback indicating the result of a characteristic write operation. + * + *

        If this callback is invoked while a reliable write transaction is + * in progress, the value of the characteristic represents the value + * reported by the remote device. An application should compare this + * value to the desired value to be written. If the values don't match, + * the application must abort the reliable write transaction. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} + * @param characteristic Characteristic that was written to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + } + + /** + * Callback triggered as a result of a remote characteristic notification. + * + * @param gatt GATT client the characteristic is associated with + * @param characteristic Characteristic that has been updated as a result + * of a remote notification event. + */ + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + } + + /** + * Callback reporting the result of a descriptor read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param descriptor Descriptor that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully + */ + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback indicating the result of a descriptor write operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} + * @param descriptor Descriptor that was writte to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback invoked when a reliable write transaction has been completed. + * + * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write + * transaction was executed successfully + */ + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the RSSI for a remote device connection. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#readRemoteRssi} function. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} + * @param rssi The RSSI value for the remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully + */ + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#requestMtu} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + */ + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 5ffceba5e11..9ee739f04bf 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -46,7 +46,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; - private BluetoothGattServerCallback mCallback; + private BluetoothGattServerCallbackExt mCallback; private Object mServerIfLock = new Object(); private int mServerIf; @@ -59,8 +59,8 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Bluetooth GATT interface callbacks */ - private final IBluetoothGattServerCallback mBluetoothGattServerCallback = - new IBluetoothGattServerCallback.Stub() { + private final IBluetoothGattServerCallbackExt mBluetoothGattServerCallback = + new IBluetoothGattServerCallbackExt.Stub() { /** * Application interface registered - app is ready to go * @hide @@ -292,6 +292,42 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception: " + ex); } } + + /** + * The PHY for a connection was updated + * @hide + */ + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyUpdate(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * The PHY for a connection was read + * @hide + */ + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyRead(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } }; /** @@ -360,7 +396,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return true, the callback will be called to notify success or failure, * false on immediate error */ - /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + /*package*/ boolean registerCallback(BluetoothGattServerCallbackExt callback) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -436,7 +472,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be + * {@link BluetoothGattServerCallbackExt#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect paramter determines whether to actively connect to @@ -487,6 +523,48 @@ public final class BluetoothGattServer implements BluetoothProfile { } } + /** + * Set the preferred connection PHY for this app. Please note that this is just a + * recommendation, wether the PHY change will happen depends on other applications peferences, + * local and remote controller capabilities. Controller can override these settings. + *

        + * {@link BluetoothGattServerCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * if no PHY change happens. It is also triggered when remote device updates the PHY. + * + * @param device The remote device to send this response to + * @param txPhy preferred transmitter PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, + * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + */ + public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { + try { + mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, + phyOptions); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Read the current transmitter PHY and receiver PHY of the connection. The values are returned + * in {@link BluetoothGattServerCallbackExt#onPhyRead} + * + * @param device The remote device to send this response to + */ + public void readPhy(BluetoothDevice device) { + try { + mService.serverReadPhy(mServerIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + /** * Send a response to a read or write request to a remote device. * @@ -494,10 +572,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * is received by one of these callback methods: * *

          - *
        • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} - *
        • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorReadRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} + *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicReadRequest} + *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicWriteRequest} + *
        • {@link BluetoothGattServerCallbackExt#onDescriptorReadRequest} + *
        • {@link BluetoothGattServerCallbackExt#onDescriptorWriteRequest} *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 2afcf9a322b..75ceb52c2d5 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 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. @@ -20,141 +20,21 @@ import android.bluetooth.BluetoothDevice; /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + * @deprecated please use {@link BluetoothGattServerCallbackExt} */ -public abstract class BluetoothGattServerCallback { +public abstract class BluetoothGattServerCallback extends BluetoothGattServerCallbackExt { /** - * Callback indicating when a remote device has been connected or disconnected. - * - * @param device Remote device that has been connected or disconnected. - * @param status Status of the connect or disconnect operation. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @hide */ - public void onConnectionStateChange(BluetoothDevice device, int status, - int newState) { + @Override + public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } /** - * Indicates whether a local service has been added successfully. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service - * was added successfully. - * @param service The service that has been added + * @hide */ - public void onServiceAdded(int status, BluetoothGattService service) { - } - - /** - * A remote client has requested to read a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param characteristic Characteristic to be read - */ - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) { - } - - /** - * A remote client has requested to write to a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param characteristic Characteristic to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the characteristic - */ - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * A remote client has requested to read a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param descriptor Descriptor to be read - */ - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { - } - - /** - * A remote client has requested to write to a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param descriptor Descriptor to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the descriptor - */ - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * Execute all pending write operations for this device. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operations - * @param requestId The Id of the request - * @param execute Whether the pending writes should be executed (true) or - * cancelled (false) - */ - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { - } - - /** - * Callback invoked when a notification or indication has been sent to - * a remote device. - * - *

        When multiple notifications are to be sent, an application must - * wait for this callback to be received before sending additional - * notifications. - * - * @param device The remote device the notification has been sent to - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful - */ - public void onNotificationSent(BluetoothDevice device, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - *

        This callback will be invoked if a remote client has requested to change - * the MTU for a given connection. - * - * @param device The remote device that requested the MTU change - * @param mtu The new MTU size - */ - public void onMtuChanged(BluetoothDevice device, int mtu) { + @Override + public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java new file mode 100644 index 00000000000..455cce04c58 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + */ +public abstract class BluetoothGattServerCallbackExt { + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Indicates whether a local service has been added successfully. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service + * was added successfully. + * @param service The service that has been added + */ + public void onServiceAdded(int status, BluetoothGattService service) { + } + + /** + * A remote client has requested to read a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param characteristic Characteristic to be read + */ + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattCharacteristic characteristic) { + } + + /** + * A remote client has requested to write to a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param characteristic Characteristic to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the characteristic + */ + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * A remote client has requested to read a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param descriptor Descriptor to be read + */ + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattDescriptor descriptor) { + } + + /** + * A remote client has requested to write to a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param descriptor Descriptor to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the descriptor + */ + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * Execute all pending write operations for this device. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operations + * @param requestId The Id of the request + * @param execute Whether the pending writes should be executed (true) or + * cancelled (false) + */ + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + } + + /** + * Callback invoked when a notification or indication has been sent to + * a remote device. + * + *

        When multiple notifications are to be sent, an application must + * wait for this callback to be received before sending additional + * notifications. + * + * @param device The remote device the notification has been sent to + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful + */ + public void onNotificationSent(BluetoothDevice device, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + *

        This callback will be invoked if a remote client has requested to change + * the MTU for a given connection. + * + * @param device The remote device that requested the MTU change + * @param mtu The new MTU size + */ + public void onMtuChanged(BluetoothDevice device, int mtu) { + } + + /** + * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result + * of remote device changing the PHY. + * + * @param device The remote device + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation + */ + public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { + } + + /** + * Callback triggered as result of {@link BluetoothGattServer#readPhy} + * + * @param device The remote device that requested the PHY read + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation + */ + public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { + } +} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 69563cb3e6a..5282e9fceba 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -27,8 +27,8 @@ import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; import android.os.WorkSource; -import android.bluetooth.IBluetoothGattCallback; -import android.bluetooth.IBluetoothGattServerCallback; +import android.bluetooth.IBluetoothGattCallbackExt; +import android.bluetooth.IBluetoothGattServerCallbackExt; import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; @@ -58,10 +58,12 @@ interface IBluetoothGatt { void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); - void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); void clientDisconnect(in int clientIf, in String address); + void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); + void clientReadPhy(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); @@ -77,10 +79,12 @@ interface IBluetoothGatt { void configureMTU(in int clientIf, in String address, in int mtu); void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); + void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallbackExt callback); void unregisterServer(in int serverIf); void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); + void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); + void serverReadPhy(in int clientIf, in String address); void addService(in int serverIf, in BluetoothGattService service); void removeService(in int serverIf, in int handle); void clearServices(in int serverIf); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl similarity index 89% rename from framework/java/android/bluetooth/IBluetoothGattCallback.aidl rename to framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl index 72cb6182773..736f4b2b048 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl @@ -22,10 +22,12 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattCallback { +oneway interface IBluetoothGattCallbackExt { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); + void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); + void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); void onSearchComplete(in String address, in List services, in int status); void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); void onCharacteristicWrite(in String address, in int status, in int handle); diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl similarity index 88% rename from framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl rename to framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl index 1a924fb4e31..091ffb3fe98 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 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. @@ -21,7 +21,7 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattServerCallback { +oneway interface IBluetoothGattServerCallbackExt { void onServerRegistered(in int status, in int serverIf); void onServerConnectionState(in int status, in int serverIf, in boolean connected, in String address); @@ -40,4 +40,6 @@ oneway interface IBluetoothGattServerCallback { void onExecuteWrite(in String address, in int transId, in boolean execWrite); void onNotificationSent(in String address, in int status); void onMtuChanged(in String address, in int mtu); + void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); + void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); } -- GitLab From 4f6e56208e5e0b0f797533bfe4a834eb2e4dde4e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 10 Jan 2017 06:15:54 -0800 Subject: [PATCH 0695/1408] Bluetooth 5 Advertising API Test: manual Bug: 30622771 Change-Id: Id6856e6110872ec50ff1af54ddc75c0104a6459c --- .../android/bluetooth/IBluetoothGatt.aidl | 17 + .../android/bluetooth/le/AdvertisingSet.java | 162 +++++++ .../bluetooth/le/AdvertisingSetCallback.java | 144 ++++++ .../le/AdvertisingSetParameters.aidl | 19 + .../le/AdvertisingSetParameters.java | 409 ++++++++++++++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 194 +++++++++ .../bluetooth/le/IAdvertisingSetCallback.aidl | 32 ++ .../le/PeriodicAdvertisingParameters.aidl | 19 + .../le/PeriodicAdvertisingParameters.java | 134 ++++++ .../java/android/bluetooth/le/ScanResult.java | 2 +- 10 files changed, 1131 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/le/AdvertisingSet.java create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetCallback.java create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.java create mode 100644 framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 5282e9fceba..33fedc71898 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -20,6 +20,8 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertisingSetParameters; +import android.bluetooth.le.PeriodicAdvertisingParameters; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; @@ -30,6 +32,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallbackExt; import android.bluetooth.IBluetoothGattServerCallbackExt; import android.bluetooth.le.IAdvertiserCallback; +import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; @@ -55,10 +58,24 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); + void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, + in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, + in AdvertiseData periodicData, in IAdvertisingSetCallback callback); + void stopAdvertisingSet(in IAdvertisingSetCallback callback); + + void enableAdverisingSet(in int advertiserId, in boolean enable); + void setAdvertisingData(in int advertiserId, in AdvertiseData data); + void setScanResponseData(in int advertiserId, in AdvertiseData data); + void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); + void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); + void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); + void periodicAdvertisingEnable(in int advertiserId, in boolean enable); + void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback); + void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); void clientDisconnect(in int clientIf, in String address); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java new file mode 100644 index 00000000000..1524022b1f0 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.le.IAdvertisingSetCallback; +import android.os.RemoteException; +import android.util.Log; + +/** + * This class provides a way to control single Bluetooth LE advertising instance. + *

        + * To get an instance of {@link AdvertisingSet}, call the + * {@link BluetoothLeAdvertiser#startAdvertisingSet} method. + *

        + * Note: Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see AdvertiseData + */ +public final class AdvertisingSet { + private static final String TAG = "AdvertisingSet"; + + private final IBluetoothGatt gatt; + private int advertiserId; + + /* package */ AdvertisingSet(int advertiserId, + IBluetoothManager bluetoothManager) { + this.advertiserId = advertiserId; + + try { + this.gatt = bluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + throw new IllegalStateException("Failed to get Bluetooth"); + } + } + + /* package */ void setAdvertiserId(int advertiserId) { + this.advertiserId = advertiserId; + } + + /** + * Enables Advertising. This method returns immediately, the operation status is + * delivered + * through {@code callback.onAdvertisingEnabled()}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + */ + public void enableAdvertising(boolean enable) { + try { + gatt.enableAdverisingSet(this.advertiserId, enable); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for + * specified AdvertisingSetParameters. This method returns immediately, the operation status is + * delivered through {@code callback.onAdvertisingDataSet()}. + *

        + * Advertising data must be empty if non-legacy scannable advertising is used. + */ + public void setAdvertisingData(AdvertiseData data) { + try { + gatt.setAdvertisingData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Set/update scan response data. Make sure that data doesn't exceed the size limit for + * specified AdvertisingSetParameters. This method returns immediately, the operation status + * is delivered through {@code callback.onScanResponseDataSet()}. + */ + public void setScanResponseData(AdvertiseData data) { + try { + gatt.setScanResponseData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Update advertising parameters associated with this AdvertisingSet. Must be called when + * advertising is not active. This method returns immediately, the operation status is delivered + * through {@code callback.onAdvertisingParametersUpdated}. + */ + public void setAdvertisingParameters(AdvertisingSetParameters parameters) { + try { + gatt.setAdvertisingParameters(this.advertiserId, parameters); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Update periodic advertising parameters associated with this set. Must be called when + * periodic advertising is not enabled. This method returns immediately, the operation + * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}. + */ + public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { + try { + gatt.setPeriodicAdvertisingParameters(this.advertiserId, parameters); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters, + * or after advertising was started with periodic advertising data set. This method returns + * immediately, the operation status is delivered through + * {@code callback.onPeriodicAdvertisingDataSet()}. + */ + public void setPeriodicAdvertisingData(AdvertiseData data) { + try { + gatt.setPeriodicAdvertisingData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Used to enable/disable periodic advertising. This method returns immediately, the operation + * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. + */ + public void periodicAdvertisingEnable(boolean enable) { + try { + gatt.periodicAdvertisingEnable(this.advertiserId, enable); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Returns advertiserId associated with thsi advertising set. + * + * @hide + */ + public int getAdvertiserId(){ + return advertiserId; + } +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java new file mode 100644 index 00000000000..ceed8d9e3c6 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; + +/** + * Bluetooth LE advertising set callbacks, used to deliver advertising operation + * status. + */ +public abstract class AdvertisingSetCallback { + + /** + * The requested operation was successful. + */ + public static final int ADVERTISE_SUCCESS = 0; + + /** + * Failed to start advertising as the advertise data to be broadcasted is too + * large. + */ + public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; + + /** + * Failed to start advertising because no advertising instance is available. + */ + public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; + + /** + * Failed to start advertising as the advertising is already started. + */ + public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; + + /** + * Operation failed due to an internal error. + */ + public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; + + /** + * This feature is not supported on this platform. + */ + public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} + * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet + * contains the started set and it is advertising. If error occured, advertisingSet is + * null, and status will be set to proper error code. + * + * @param advertisingSet The advertising set that was started or null if error. + * @param status Status of the operation. + */ + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} + * indicating advertising set is stopped. + * + * @param advertisingSet The advertising set. + */ + public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {} + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} indicating + * result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is advertising. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating + * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating + * result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void + onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onPeriodicAdvertisingEnable(AdvertisingSet advertisingSet, boolean enable, + int status) {} +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl new file mode 100644 index 00000000000..39034a001fa --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable AdvertisingSetParameters; diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java new file mode 100644 index 00000000000..03a01e171ba --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2017 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The {@link AdvertisingSetParameters} provide a way to adjust advertising + * preferences for each + * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to + * create an + * instance of this class. + */ +public final class AdvertisingSetParameters implements Parcelable { + + /** + * 1M advertiser PHY. + */ + public static final int PHY_LE_1M = 1; + + /** + * 2M advertiser PHY. + */ + public static final int PHY_LE_2M = 2; + + /** + * LE Coded advertiser PHY. + */ + public static final int PHY_LE_CODED = 3; + + /** + * Advertise on low frequency, around every 1000ms. This is the default and + * preferred advertising mode as it consumes the least power. + */ + public static final int INTERVAL_LOW = 1600; + + /** + * Advertise on medium frequency, around every 250ms. This is balanced + * between advertising frequency and power consumption. + */ + public static final int INTERVAL_MEDIUM = 400; + + /** + * Perform high frequency, low latency advertising, around every 100ms. This + * has the highest power consumption and should not be used for continuous + * background advertising. + */ + public static final int INTERVAL_HIGH = 160; + + /** + * Minimum value for advertising interval. + */ + public static final int INTERVAL_MIN = 160; + + /** + * Maximum value for advertising interval. + */ + public static final int INTERVAL_MAX = 16777215; + + /** + * Advertise using the lowest transmission (TX) power level. Low transmission + * power can be used to restrict the visibility range of advertising packets. + */ + public static final int TX_POWER_ULTRA_LOW = -21; + + /** + * Advertise using low TX power level. + */ + public static final int TX_POWER_LOW = -15; + + /** + * Advertise using medium TX power level. + */ + public static final int TX_POWER_MEDIUM = -7; + + /** + * Advertise using high TX power level. This corresponds to largest visibility + * range of the advertising packet. + */ + public static final int TX_POWER_HIGH = 1; + + /** + * Minimum value for TX power. + */ + public static final int TX_POWER_MIN = -127; + + /** + * Maximum value for TX power. + */ + public static final int TX_POWER_MAX = 1; + + /** + * The maximum limited advertisement duration as specified by the Bluetooth + * SIG + */ + private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + + private final boolean isLegacy; + private final boolean isAnonymous; + private final boolean includeTxPower; + private final int primaryPhy; + private final int secondaryPhy; + private final boolean connectable; + private final int interval; + private final int txPowerLevel; + private final int timeoutMillis; + + private AdvertisingSetParameters(boolean connectable, boolean isLegacy, + boolean isAnonymous, boolean includeTxPower, + int primaryPhy, int secondaryPhy, + int interval, int txPowerLevel, + int timeoutMillis) { + this.connectable = connectable; + this.isLegacy = isLegacy; + this.isAnonymous = isAnonymous; + this.includeTxPower = includeTxPower; + this.primaryPhy = primaryPhy; + this.secondaryPhy = secondaryPhy; + this.interval = interval; + this.txPowerLevel = txPowerLevel; + this.timeoutMillis = timeoutMillis; + } + + private AdvertisingSetParameters(Parcel in) { + connectable = in.readInt() != 0 ? true : false; + isLegacy = in.readInt() != 0 ? true : false; + isAnonymous = in.readInt() != 0 ? true : false; + includeTxPower = in.readInt() != 0 ? true : false; + primaryPhy = in.readInt(); + secondaryPhy = in.readInt(); + interval = in.readInt(); + txPowerLevel = in.readInt(); + timeoutMillis = in.readInt(); + } + + /** + * Returns whether the advertisement will be connectable. + */ + public boolean isConnectable() { return connectable; } + + /** + * Returns whether the legacy advertisement will be used. + */ + public boolean isLegacy() { return isLegacy; } + + /** + * Returns whether the advertisement will be anonymous. + */ + public boolean isAnonymous() { return isAnonymous; } + + /** + * Returns whether the TX Power will be included. + */ + public boolean includeTxPower() { return includeTxPower; } + + /** + * Returns the primary advertising phy. + */ + public int getPrimaryPhy() { return primaryPhy; } + + /** + * Returns the secondary advertising phy. + */ + public int getSecondaryPhy() { return secondaryPhy; } + + /** + * Returns the advertising interval. + */ + public int getInterval() { return interval; } + + /** + * Returns the TX power level for advertising. + */ + public int getTxPowerLevel() { return txPowerLevel; } + + /** + * Returns the advertising time limit in milliseconds. + */ + public int getTimeout() { return timeoutMillis; } + + @Override + public String toString() { + return "AdvertisingSetParameters [connectable=" + connectable + + ", isLegacy=" + isLegacy + + ", isAnonymous=" + isAnonymous + + ", includeTxPower=" + includeTxPower + + ", primaryPhy=" + primaryPhy + + ", secondaryPhy=" + secondaryPhy + + ", interval=" + interval + + ", txPowerLevel=" + txPowerLevel + + ", timeoutMillis=" + timeoutMillis + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(connectable ? 1 : 0); + dest.writeInt(isLegacy ? 1 : 0); + dest.writeInt(isAnonymous ? 1 : 0); + dest.writeInt(includeTxPower ? 1 : 0); + dest.writeInt(primaryPhy); + dest.writeInt(secondaryPhy); + dest.writeInt(interval); + dest.writeInt(txPowerLevel); + dest.writeInt(timeoutMillis); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertisingSetParameters[] newArray(int size) { + return new AdvertisingSetParameters[size]; + } + + @Override + public AdvertisingSetParameters createFromParcel(Parcel in) { + return new AdvertisingSetParameters(in); + } + }; + + /** + * Builder class for {@link AdvertisingSetParameters}. + */ + public static final class Builder { + + private boolean connectable = true; + private boolean isLegacy = false; + private boolean isAnonymous = false; + private boolean includeTxPower = false; + private int primaryPhy = PHY_LE_1M; + private int secondaryPhy = PHY_LE_1M; + private int interval = INTERVAL_LOW; + private int txPowerLevel = TX_POWER_MEDIUM; + private int timeoutMillis = 0; + + /** + * Set whether the advertisement type should be connectable or + * non-connectable. + * Legacy advertisements can be both connectable and scannable. Other + * advertisements can be connectable only if not scannable. + * @param connectable Controls whether the advertisment type will be + * connectable (true) or non-connectable (false). + */ + public Builder setConnectable(boolean connectable) { + this.connectable = connectable; + return this; + } + + /** + * When set to true, advertising set will advertise 4.x Spec compliant + * advertisements. + * + * @param isLegacy wether legacy advertising mode should be used. + */ + public Builder setLegacyMode(boolean isLegacy) { + this.isLegacy = isLegacy; + return this; + } + + /** + * Set wether advertiser address should be ommited from all packets. If this + * mode is used, periodic advertising can't be enabled for this set. + * + * This is used only if legacy mode is not used. + * + * @param isAnonymous wether anonymous advertising should be used. + */ + public Builder setAnonymouus(boolean isAnonymous) { + this.isAnonymous = isAnonymous; + return this; + } + + /** + * Set wether TX power should be included in the extended header. + * + * This is used only if legacy mode is not used. + * + * @param includeTxPower wether TX power should be included in extended + * header + */ + public Builder setIncludeTxPower(boolean includeTxPower) { + this.includeTxPower = includeTxPower; + return this; + } + + /** + * Set the primary physical channel used for this advertising set. + * + * This is used only if legacy mode is not used. + * + * @param primaryPhy Primary advertising physical channel, can only be + * {@link AdvertisingSetParameters#PHY_LE_1M} or + * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * @throws IllegalArgumentException If the primaryPhy is invalid. + */ + public Builder setPrimaryPhy(int primaryPhy) { + if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) { + throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); + } + this.primaryPhy = primaryPhy; + return this; + } + + /** + * Set the secondary physical channel used for this advertising set. + * + * This is used only if legacy mode is not used. + * + * @param secondaryPhy Secondary advertising physical channel, can only be + * one of {@link AdvertisingSetParameters#PHY_LE_1M}, + * {@link AdvertisingSetParameters#PHY_LE_2M} or + * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * @throws IllegalArgumentException If the secondaryPhy is invalid. + */ + public Builder setSecondaryPhy(int secondaryPhy) { + if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M && + secondaryPhy != PHY_LE_CODED) { + throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); + } + this.secondaryPhy = secondaryPhy; + return this; + } + + /** + * Set advertising interval. + * + * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid + * range is from 160 (100ms) to 16777215 (10,485.759375 s). + * Recommended values are: + * {@link AdvertisingSetParameters#INTERVAL_LOW}, + * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or + * {@link AdvertisingSetParameters#INTERVAL_HIGH}. + * @throws IllegalArgumentException If the interval is invalid. + */ + public Builder setInterval(int interval) { + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + throw new IllegalArgumentException("unknown interval " + interval); + } + this.interval = interval; + return this; + } + + /** + * Set the transmission power level for the advertising. + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in + * dBm. The valid range is [-127, 1] Recommended values are: + * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or + * {@link AdvertisingSetParameters#TX_POWER_HIGH}. + * + * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. + */ + public Builder setTxPowerLevel(int txPowerLevel) { + if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { + throw new IllegalArgumentException("unknown txPowerLevel " + + txPowerLevel); + } + this.txPowerLevel = txPowerLevel; + return this; + } + + /** + * Limit advertising to a given amount of time. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * milliseconds. A value of 0 will disable the time limit. + * @throws IllegalArgumentException If the provided timeout is over 180000 + * ms. + */ + public Builder setTimeout(int timeoutMillis) { + if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { + throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" + + LIMITED_ADVERTISING_MAX_MILLIS + + " milliseconds)"); + } + this.timeoutMillis = timeoutMillis; + return this; + } + + /** + * Build the {@link AdvertisingSetParameters} object. + */ + public AdvertisingSetParameters build() { + return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous, + includeTxPower, primaryPhy, + secondaryPhy, interval, txPowerLevel, + timeoutMillis); + } + } +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 94d03e533df..e03c9477a6a 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -62,6 +62,9 @@ public final class BluetoothLeAdvertiser { private BluetoothAdapter mBluetoothAdapter; private final Map mLeAdvertisers = new HashMap(); + private final Map + advertisingSetCallbackWrappers = new HashMap<>(); + private final Map advertisingSets = new HashMap<>(); /** * Use BluetoothAdapter.getLeAdvertiser() instead. @@ -155,6 +158,93 @@ public final class BluetoothLeAdvertiser { } } + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onNewAdvertisingSet()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param callback Callback for advertising set. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback) { + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, callback, new Handler(Looper.getMainLooper())); + } + + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onNewAdvertisingSet()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback, + Handler handler) { + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); + + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + throw new IllegalStateException("Failed to get Bluetooth"); + } + + IAdvertisingSetCallback wrapped = wrap(callback, handler); + advertisingSetCallbackWrappers.put(callback, wrapped); + + try { + gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to start advertising set - ", e); + throw new IllegalStateException("Failed to start advertising set"); + } + } + + /** + * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link + * BluetoothLeAdvertiser#startAdvertisingSet}. + */ + public void stopAdvertisingSet(AdvertisingSetCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + + IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback); + if (wrapped == null) { + throw new IllegalArgumentException( + "callback does not represent valid registered callback."); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + gatt.stopAdvertisingSet(wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising - ", e); + throw new IllegalStateException("Failed to stop advertising"); + } + } + /** * Cleans up advertisers. Should be called when bluetooth is down. * @@ -219,6 +309,110 @@ public final class BluetoothLeAdvertiser { return array == null ? 0 : array.length; } + IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { + return new IAdvertisingSetCallback.Stub() { + public void onAdvertisingSetStarted(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { + callback.onAdvertisingSetStarted(null, status); + advertisingSetCallbackWrappers.remove(callback); + return; + } + + AdvertisingSet advertisingSet = + new AdvertisingSet(advertiserId, mBluetoothManager); + advertisingSets.put(advertiserId, advertisingSet); + callback.onAdvertisingSetStarted(advertisingSet, status); + } + }); + } + + public void onAdvertisingSetStopped(int advertiserId) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingSetStopped(advertisingSet); + advertisingSets.remove(advertiserId); + advertisingSetCallbackWrappers.remove(callback); + } + }); + } + + public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingEnabled(advertisingSet, enabled, status); + } + }); + } + + public void onAdvertisingDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingDataSet(advertisingSet, status); + } + }); + } + + public void onScanResponseDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onScanResponseDataSet(advertisingSet, status); + } + }); + } + + public void onAdvertisingParametersUpdated(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingParametersUpdated(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingDataSet(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status); + } + }); + } + }; + } + /** * Bluetooth GATT interface callbacks for advertising. */ diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl new file mode 100644 index 00000000000..4b0a111fa3d --- /dev/null +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 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.le; + +/** + * Callback definitions for interacting with Advertiser + * @hide + */ +oneway interface IAdvertisingSetCallback { + void onAdvertisingSetStarted(in int advertiserId, in int status); + void onAdvertisingSetStopped(in int advertiserId); + void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); + void onAdvertisingDataSet(in int advertiserId, in int status); + void onScanResponseDataSet(in int advertiserId, in int status); + void onAdvertisingParametersUpdated(in int advertiserId, in int status); + void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); + void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); + void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status); +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl new file mode 100644 index 00000000000..f4bea22a12f --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable PeriodicAdvertisingParameters; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java new file mode 100644 index 00000000000..ebc92bd0bcf --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic + * advertising preferences for each Bluetooth LE advertising set. Use {@link + * AdvertisingSetParameters.Builder} to create an instance of this class. + */ +public final class PeriodicAdvertisingParameters implements Parcelable { + + private static final int INTERVAL_MAX = 80; + private static final int INTERVAL_MIN = 65519; + + private final boolean enable; + private final boolean includeTxPower; + private final int interval; + + private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) { + this.enable = enable; + this.includeTxPower = includeTxPower; + this.interval = interval; + } + + private PeriodicAdvertisingParameters(Parcel in) { + enable = in.readInt() != 0 ? true : false; + includeTxPower = in.readInt() != 0 ? true : false; + interval = in.readInt(); + } + + /** + * Returns whether the periodic advertising shall be enabled. + */ + public boolean getEnable() { return enable; } + + /** + * Returns whether the TX Power will be included. + */ + public boolean getIncludeTxPower() { return includeTxPower; } + + /** + * Returns the periodic advertising interval, in 1.25ms unit. + * Valid values are from 80 (100ms) to 65519 (81.89875s). + */ + public int getInterval() { return interval; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(enable ? 1 : 0); + dest.writeInt(includeTxPower ? 1 : 0); + dest.writeInt(interval); + } + + public static final Parcelable + .Creator CREATOR = + new Creator() { + @Override + public PeriodicAdvertisingParameters[] newArray(int size) { + return new PeriodicAdvertisingParameters[size]; + } + + @Override + public PeriodicAdvertisingParameters createFromParcel(Parcel in) { + return new PeriodicAdvertisingParameters(in); + } + }; + + public static final class Builder { + private boolean includeTxPower = false; + private boolean enable = false; + private int interval = INTERVAL_MAX; + + /** + * Set wether the Periodic Advertising should be enabled for this set. + */ + public Builder setEnable(boolean enable) { + this.enable = enable; + return this; + } + + /** + * Whether the transmission power level should be included in the periodic + * packet. + */ + public Builder setIncludeTxPower(boolean includeTxPower) { + this.includeTxPower = includeTxPower; + return this; + } + + /** + * Set advertising interval for periodic advertising, in 1.25ms unit. + * Valid values are from 80 (100ms) to 65519 (81.89875s). + * Value from range [interval, interval+20ms] will be picked as the actual value. + * @throws IllegalArgumentException If the interval is invalid. + */ + public Builder setInterval(int interval) { + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN + + "-" + INTERVAL_MAX + ")"); + } + this.interval = interval; + return this; + } + + /** + * Build the {@link AdvertisingSetParameters} object. + */ + public PeriodicAdvertisingParameters build() { + return new PeriodicAdvertisingParameters(enable, includeTxPower, interval); + } + } +} diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index f92357b59c7..583ddd20fd7 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -214,7 +214,7 @@ public final class ScanResult implements Parcelable { } /** - * Returns the received signal strength in dBm. The valid range is [-127, 127]. + * Returns the received signal strength in dBm. The valid range is [-127, 126]. */ public int getRssi() { return mRssi; -- GitLab From 60d90308de61d0a54605829a0328b8d34670c6ba Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 7 Feb 2017 18:05:39 -0800 Subject: [PATCH 0696/1408] Bluetooth 5 feature check API (1/2) Bug: 30622771 Test: manual Change-Id: I90e2efe989745c07c2f2fb8f4ea5bc3b718382f6 (cherry picked from commit 79d66495c32996a5b532328571bf6ceecca70ca5) --- .../android/bluetooth/BluetoothAdapter.java | 72 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 4 ++ 2 files changed, 76 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index dbc25afcd08..4ecf1249d18 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1384,6 +1384,78 @@ public final class BluetoothAdapter { return false; } + /** + * Return true if LE 2M PHY feature is supported. + * + * @return true if chipset supports LE 2M PHY feature + */ + public boolean isLe2MPhySupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLe2MPhySupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Coded PHY feature is supported. + * + * @return true if chipset supports LE Coded PHY feature + */ + public boolean isLeCodedPhySupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLeCodedPhySupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Periodic Advertising feature is supported. + * + * @return true if chipset supports LE Periodic Advertising feature + */ + public boolean isLeExtendedAdvertisingSupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLeExtendedAdvertisingSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Return true if LE Periodic Advertising feature is supported. + * + * @return true if chipset supports LE Periodic Advertising feature + */ + public boolean isLePeriodicAdvertisingSupported() { + if (!getLeAccess()) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.isLePeriodicAdvertisingSupported(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + /** * Return true if hardware has entries available for matching beacons * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 53fef2add34..76ca554e598 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -104,6 +104,10 @@ interface IBluetooth boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); + boolean isLe2MPhySupported(); + boolean isLeCodedPhySupported(); + boolean isLeExtendedAdvertisingSupported(); + boolean isLePeriodicAdvertisingSupported(); BluetoothActivityEnergyInfo reportActivityInfo(); /** -- GitLab From bfe28687cadf9339242844fb662b2bfb8e7ff109 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 17 Jan 2017 07:50:46 -0800 Subject: [PATCH 0697/1408] Bluetooth 5 enhanced scanning API Bug: 30622771 Test: manual Change-Id: I2c8065fbcedf48777ce18c7d8fe621e568b3fd75 (cherry picked from commit 9de522c6e48497028d36a1f8ad8f8adf4b7b1ae6) --- .../java/android/bluetooth/le/ScanResult.java | 205 +++++++++++++++++- .../android/bluetooth/le/ScanSettings.java | 86 +++++++- 2 files changed, 278 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 2fdfe7f8a83..f92357b59c7 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -27,7 +27,56 @@ import java.util.Objects; * ScanResult for Bluetooth LE scan. */ public final class ScanResult implements Parcelable { - // Remote bluetooth device. + + /** + * For chained advertisements, inidcates tha the data contained in this + * scan result is complete. + */ + public static final int DATA_COMPLETE = 0x00; + + /** + * For chained advertisements, indicates that the controller was + * unable to receive all chained packets and the scan result contains + * incomplete truncated data. + */ + public static final int DATA_TRUNCATED = 0x02; + + /** + * Indicates that the secondary physical layer was not used. + */ + public static final int PHY_UNUSED = 0x00; + + /** + * Bluetooth LE 1Mbit advertiser PHY. + */ + public static final int PHY_LE_1M = 0x01; + + /** + * Bluetooth LE 2Mbit advertiser PHY. + */ + public static final int PHY_LE_2M = 0x02; + + /** + * Bluetooth LE Coded advertiser PHY. + */ + public static final int PHY_LE_CODED = 0x03; + + /** + * Advertising Set ID is not present in the packet. + */ + public static final int SID_NOT_PRESENT = 0xFF; + + /** + * Mask for checking wether event type represents legacy advertisement. + */ + private static final int ET_LEGACY_MASK = 0x10; + + /** + * Mask for checking wether event type represents connectable advertisement. + */ + private static final int ET_CONNECTABLE_MASK = 0x01; + + // Remote Bluetooth device. private BluetoothDevice mDevice; // Scan record, including advertising data and scan response data. @@ -40,13 +89,21 @@ public final class ScanResult implements Parcelable { // Device timestamp when the result was last seen. private long mTimestampNanos; + private int mEventType; + private int mPrimaryPhy; + private int mSecondaryPhy; + private int mAdvertisingSid; + private int mTxPower; + private int mPeriodicAdvertisingInterval; + /** - * Constructor of scan result. + * Constructs a new ScanResult. * - * @param device Remote bluetooth device that is found. + * @param device Remote Bluetooth device found. * @param scanRecord Scan record including both advertising data and scan response data. * @param rssi Received signal strength. - * @param timestampNanos Device timestamp when the scan result was observed. + * @param timestampNanos Timestamp at which the scan result was observed. + * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)} */ public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos) { @@ -54,6 +111,41 @@ public final class ScanResult implements Parcelable { mScanRecord = scanRecord; mRssi = rssi; mTimestampNanos = timestampNanos; + mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK; + mPrimaryPhy = PHY_LE_1M; + mSecondaryPhy = PHY_UNUSED; + mAdvertisingSid = SID_NOT_PRESENT; + mTxPower = 127; + mPeriodicAdvertisingInterval = 0; + } + + /** + * Constructs a new ScanResult. + * + * @param device Remote Bluetooth device found. + * @param eventType Event type. + * @param primaryPhy Primary advertising phy. + * @param secondaryPhy Secondary advertising phy. + * @param advertisingSid Advertising set ID. + * @param txPower Transmit power. + * @param rssi Received signal strength. + * @param periodicAdvertisingInterval Periodic advertising interval. + * @param scanRecord Scan record including both advertising data and scan response data. + * @param timestampNanos Timestamp at which the scan result was observed. + */ + public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy, + int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, + ScanRecord scanRecord, long timestampNanos) { + mDevice = device; + mEventType = eventType; + mPrimaryPhy = primaryPhy; + mSecondaryPhy = secondaryPhy; + mAdvertisingSid = advertisingSid; + mTxPower = txPower; + mRssi = rssi; + mPeriodicAdvertisingInterval = periodicAdvertisingInterval; + mScanRecord = scanRecord; + mTimestampNanos = timestampNanos; } private ScanResult(Parcel in) { @@ -76,6 +168,12 @@ public final class ScanResult implements Parcelable { } dest.writeInt(mRssi); dest.writeLong(mTimestampNanos); + dest.writeInt(mEventType); + dest.writeInt(mPrimaryPhy); + dest.writeInt(mSecondaryPhy); + dest.writeInt(mAdvertisingSid); + dest.writeInt(mTxPower); + dest.writeInt(mPeriodicAdvertisingInterval); } private void readFromParcel(Parcel in) { @@ -87,6 +185,12 @@ public final class ScanResult implements Parcelable { } mRssi = in.readInt(); mTimestampNanos = in.readLong(); + mEventType = in.readInt(); + mPrimaryPhy = in.readInt(); + mSecondaryPhy = in.readInt(); + mAdvertisingSid = in.readInt(); + mTxPower = in.readInt(); + mPeriodicAdvertisingInterval = in.readInt(); } @Override @@ -95,7 +199,7 @@ public final class ScanResult implements Parcelable { } /** - * Returns the remote bluetooth device identified by the bluetooth device address. + * Returns the remote Bluetooth device identified by the Bluetooth device address. */ public BluetoothDevice getDevice() { return mDevice; @@ -123,9 +227,79 @@ public final class ScanResult implements Parcelable { return mTimestampNanos; } + /** + * Returns true if this object represents legacy scan result. + * Legacy scan results do not contain advanced advertising information + * as specified in the Bluetooth Core Specification v5. + */ + public boolean isLegacy() { + return (mEventType & ET_LEGACY_MASK) != 0; + } + + /** + * Returns true if this object represents connectable scan result. + */ + public boolean isConnectable() { + return (mEventType & ET_CONNECTABLE_MASK) != 0; + } + + /** + * Returns the data status. + * Can be one of {@link ScanResult#DATA_COMPLETE} or + * {@link ScanResult#DATA_TRUNCATED}. + */ + public int getDataStatus() { + // return bit 5 and 6 + return (mEventType >> 5) & 0x03; + } + + /** + * Returns the primary Physical Layer + * on which this advertisment was received. + * Can be one of {@link ScanResult#PHY_LE_1M} or + * {@link ScanResult#PHY_LE_CODED}. + */ + public int getPrimaryPhy() { return mPrimaryPhy; } + + /** + * Returns the secondary Physical Layer + * on which this advertisment was received. + * Can be one of {@link ScanResult#PHY_LE_1M}, + * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED} + * or {@link ScanResult#PHY_UNUSED} - if the advertisement + * was not received on a secondary physical channel. + */ + public int getSecondaryPhy() { return mSecondaryPhy; } + + /** + * Returns the advertising set id. + * May return {@link ScanResult#SID_NOT_PRESENT} if + * no set id was is present. + */ + public int getAdvertisingSid() { return mAdvertisingSid; } + + /** + * Returns the transmit power in dBm. + * Valid range is [-127, 126]. A value of 127 indicates that the + * advertisement did not indicate TX power. + */ + public int getTxPower() { return mTxPower; } + + /** + * Returns the periodic advertising interval in units of 1.25ms. + * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means + * periodic advertising is not used for this scan result. + */ + public int getPeriodicAdvertisingInterval() { + return mPeriodicAdvertisingInterval; + } + @Override public int hashCode() { - return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos); + return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos, + mEventType, mPrimaryPhy, mSecondaryPhy, + mAdvertisingSid, mTxPower, + mPeriodicAdvertisingInterval); } @Override @@ -138,15 +312,24 @@ public final class ScanResult implements Parcelable { } ScanResult other = (ScanResult) obj; return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.equals(mScanRecord, other.mScanRecord) - && (mTimestampNanos == other.mTimestampNanos); + Objects.equals(mScanRecord, other.mScanRecord) && + (mTimestampNanos == other.mTimestampNanos) && + mEventType == other.mEventType && + mPrimaryPhy == other.mPrimaryPhy && + mSecondaryPhy == other.mSecondaryPhy && + mAdvertisingSid == other.mAdvertisingSid && + mTxPower == other.mTxPower && + mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; } @Override public String toString() { - return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord=" - + Objects.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos=" - + mTimestampNanos + '}'; + return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + + Objects.toString(mScanRecord) + ", rssi=" + mRssi + + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; } public static final Parcelable.Creator CREATOR = new Creator() { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index d61662469b6..69c9a8cece3 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -122,6 +122,24 @@ public final class ScanSettings implements Parcelable { @SystemApi public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; + /** + * Use the Bluetooth LE 1Mbit PHY for scanning. + */ + public static final int PHY_LE_1M = 1; + + /** + * Use Bluetooth LE Coded PHY for scanning. + */ + public static final int PHY_LE_CODED = 3; + + /** + * Use all supported PHYs for scanning. + * This will check the controller capabilities, and start + * the scan on 1Mbit and LE Coded PHYs if supported, or on + * the 1Mbit PHY only. + */ + public static final int PHY_LE_ALL_SUPPORTED = 255; + // Bluetooth LE scan mode. private int mScanMode; @@ -138,6 +156,11 @@ public final class ScanSettings implements Parcelable { private int mNumOfMatchesPerFilter; + // Include only legacy advertising results + private boolean mLegacy; + + private int mPhy; + public int getScanMode() { return mScanMode; } @@ -164,6 +187,22 @@ public final class ScanSettings implements Parcelable { return mNumOfMatchesPerFilter; } + /** + * Returns whether only legacy advertisements will be returned. + * Legacy advertisements include advertisements as specified + * by the Bluetooth core specification 4.2 and below. + */ + public boolean getLegacy() { + return mLegacy; + } + + /** + * Returns the physical layer used during a scan. + */ + public int getPhy() { + return mPhy; + } + /** * Returns report delay timestamp based on the device clock. */ @@ -172,13 +211,16 @@ public final class ScanSettings implements Parcelable { } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) { + long reportDelayMillis, int matchMode, + int numOfMatchesPerFilter, boolean legacy, int phy) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; mNumOfMatchesPerFilter = numOfMatchesPerFilter; mMatchMode = matchMode; + mLegacy = legacy; + mPhy = phy; } private ScanSettings(Parcel in) { @@ -188,6 +230,8 @@ public final class ScanSettings implements Parcelable { mReportDelayMillis = in.readLong(); mMatchMode = in.readInt(); mNumOfMatchesPerFilter = in.readInt(); + mLegacy = in.readInt() != 0 ? true : false; + mPhy = in.readInt(); } @Override @@ -198,6 +242,8 @@ public final class ScanSettings implements Parcelable { dest.writeLong(mReportDelayMillis); dest.writeInt(mMatchMode); dest.writeInt(mNumOfMatchesPerFilter); + dest.writeInt(mLegacy ? 1 : 0); + dest.writeInt(mPhy); } @Override @@ -228,6 +274,9 @@ public final class ScanSettings implements Parcelable { private long mReportDelayMillis = 0; private int mMatchMode = MATCH_MODE_AGGRESSIVE; private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; + private boolean mLegacy = true; + private int mPhy = PHY_LE_ALL_SUPPORTED; + /** * Set scan mode for Bluetooth LE scan. * @@ -340,12 +389,45 @@ public final class ScanSettings implements Parcelable { return this; } + /** + * Set whether only legacy advertisments should be returned in scan results. + * Legacy advertisements include advertisements as specified by the + * Bluetooth core specification 4.2 and below. This is true by default + * for compatibility with older apps. + * + * @param legacy true if only legacy advertisements will be returned + */ + public Builder setLegacy(boolean legacy) { + mLegacy = legacy; + return this; + } + + /** + * Set the Physical Layer to use during this scan. + * This is used only if {@link ScanSettings.Builder#setLegacy} + * is set to false. + * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported} + * may be used to check whether LE Coded phy is supported by calling + * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}. + * Selecting an unsupported phy will result in failure to start scan. + * + * @param phy Can be one of + * {@link ScanSettings#PHY_LE_1M}, + * {@link ScanSettings#PHY_LE_CODED} or + * {@link ScanSettings#PHY_LE_ALL_SUPPORTED} + */ + public Builder setPhy(int phy) { + mPhy = phy; + return this; + } + /** * Build {@link ScanSettings}. */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter); + mReportDelayMillis, mMatchMode, + mNumOfMatchesPerFilter, mLegacy, mPhy); } } } -- GitLab From b74a8cd801d63b94c1770299f02bcb1de5b3b8ea Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 16 Jan 2017 07:21:01 -0800 Subject: [PATCH 0698/1408] Bluetooth 5 periodc scan API (1/2) Bug: 30622771 Test: manual Change-Id: I61853bc71f6013e9406d1d151bb51ea4484bb92c (cherry picked from commit a48e03745becc96181c676dc3d194d0572f11c10) --- .../android/bluetooth/BluetoothAdapter.java | 26 ++ .../android/bluetooth/IBluetoothGatt.aidl | 5 + .../le/IPeriodicAdvertisingCallback.aidl | 31 +++ .../le/PeriodicAdvertisingCallback.java | 77 ++++++ .../le/PeriodicAdvertisingManager.java | 237 ++++++++++++++++++ .../le/PeriodicAdvertisingReport.aidl | 19 ++ .../le/PeriodicAdvertisingReport.java | 184 ++++++++++++++ 7 files changed, 579 insertions(+) create mode 100644 framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4ecf1249d18..818693773a0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.PeriodicAdvertisingManager; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; @@ -525,6 +526,7 @@ public final class BluetoothAdapter { private static BluetoothLeScanner sBluetoothLeScanner; private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; + private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; private IBluetooth mService; @@ -629,6 +631,30 @@ public final class BluetoothAdapter { return sBluetoothLeAdvertiser; } + /** + * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising + * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic + * Advertising is not supported on this device. + *

        + * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is + * supported on this device before calling this method. + */ + public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { + if (!getLeAccess()) + return null; + + if (!isLePeriodicAdvertisingSupported()) + return null; + + synchronized (mLock) { + if (sPeriodicAdvertisingManager == null) { + sPeriodicAdvertisingManager = + new PeriodicAdvertisingManager(mManagerService); + } + } + return sPeriodicAdvertisingManager; + } + /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index aa2291e072d..69563cb3e6a 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; @@ -29,6 +30,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; +import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; /** @@ -53,6 +55,9 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); + void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); + void unregisterSync(in IPeriodicAdvertisingCallback callback); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); diff --git a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl new file mode 100644 index 00000000000..a76c54d4ab4 --- /dev/null +++ b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.PeriodicAdvertisingReport; + +/** + * Callback definitions for interacting with Periodic Advertising + * @hide + */ +oneway interface IPeriodicAdvertisingCallback { + + void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, + in int skip, in int timeout, in int status); + void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); + void onSyncLost(in int syncHandle); +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java new file mode 100644 index 00000000000..6616231bcdf --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; + +/** + * Bluetooth LE periodic advertising callbacks, used to deliver periodic + * advertising operation status. + * + * @see PeriodicAdvertisingManager#createSync + */ +public abstract class PeriodicAdvertisingCallback { + + /** + * The requested operation was successful. + * + * @hide + */ + public static final int SYNC_SUCCESS = 0; + + /** + * Sync failed to be established because remote device did not respond. + */ + public static final int SYNC_NO_RESPONSE = 1; + + /** + * Sync failed to be established because controller can't support more syncs. + */ + public static final int SYNC_NO_RESOURCES = 2; + + + /** + * Callback when synchronization was established. + * + * @param syncHandle handle used to identify this synchronization. + * @param device remote device. + * @param advertisingSid synchronized advertising set id. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive in force. @see PeriodicAdvertisingManager#createSync + * @param timeout Synchronization timeout for the periodic advertising in force. One + * unit is 10ms. @see PeriodicAdvertisingManager#createSync + * @param timeout + * @param status operation status. + */ + public void onSyncEstablished(int syncHandle, BluetoothDevice device, + int advertisingSid, int skip, int timeout, + int status) {} + + /** + * Callback when periodic advertising report is received. + * + * @param report periodic advertising report. + */ + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {} + + /** + * Callback when periodic advertising synchronization was lost. + * + * @param syncHandle handle used to identify this synchronization. + */ + public void onSyncLost(int syncHandle) {} +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java new file mode 100644 index 00000000000..12c8a8c8ffd --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothManager; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * This class provides methods to perform periodic advertising related + * operations. An application can register for periodic advertisements using + * {@link PeriodicAdvertisingManager#registerSync}. + *

        + * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an + * instance of {@link PeriodicAdvertisingManager}. + *

        + * Note: Most of the methods here require + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + */ +public final class PeriodicAdvertisingManager { + + private static final String TAG = "PeriodicAdvertisingManager"; + + private static final int SKIP_MIN = 0; + private static final int SKIP_MAX = 499; + private static final int TIMEOUT_MIN = 10; + private static final int TIMEOUT_MAX = 16384; + + private static final int SYNC_STARTING = -1; + + private final IBluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + + /* maps callback, to callback wrapper and sync handle */ + Map callbackWrappers; + + /** + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. + * + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. + * @hide + */ + public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { + mBluetoothManager = bluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + callbackWrappers = new IdentityHashMap<>(); + } + + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One + * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code + * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback) { + registerSync(scanResult, skip, timeout, callback, null); + } + + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped + * after a successful receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One + * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code + * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback, Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + if (scanResult == null) { + throw new IllegalArgumentException("scanResult can't be null"); + } + + if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { + throw new IllegalArgumentException("scanResult must contain a valid sid"); + } + + if (skip < SKIP_MIN || skip > SKIP_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), + skip, timeout, + PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); + return; + } + + if (handler == null) + handler = new Handler(Looper.getMainLooper()); + + IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); + callbackWrappers.put(callback, wrapped); + + try { + gatt.registerSync(scanResult, skip, timeout, wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register sync - ", e); + return; + } + } + + /** + * Cancel pending attempt to create sync, or terminate existing sync. + * + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code callback} is null, or not a properly + * registered callback. + */ + public void unregisterSync(PeriodicAdvertisingCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + return; + } + + IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); + if (wrapper == null) { + throw new IllegalArgumentException("callback was not properly registered"); + } + + try { + gatt.unregisterSync(wrapper); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel sync creation - ", e); + return; + } + } + + private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { + return new IPeriodicAdvertisingCallback.Stub() { + public void onSyncEstablished(int syncHandle, BluetoothDevice device, + int advertisingSid, int skip, int timeout, int status) { + + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout, + status); + + if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { + // App can still unregister the sync until notified it failed. Remove callback + // after app was notifed. + callbackWrappers.remove(callback); + } + } + }); + } + + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onPeriodicAdvertisingReport(report); + } + }); + } + + public void onSyncLost(int syncHandle) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncLost(syncHandle); + // App can still unregister the sync until notified it's lost. Remove callback after + // app was notifed. + callbackWrappers.remove(callback); + } + }); + } + }; + } +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl new file mode 100644 index 00000000000..547d09611fd --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable PeriodicAdvertisingReport; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java new file mode 100644 index 00000000000..3ff4ca58006 --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 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.le; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. + */ +public final class PeriodicAdvertisingReport implements Parcelable { + + /** + * The data returned is complete + */ + public static final int DATA_COMPLETE = 0; + + /** + * The data returned is incomplete. The controller was unsuccessfull to + * receive all chained packets, returning only partial data. + */ + public static final int DATA_INCOMPLETE_TRUNCATED = 2; + + private int syncHandle; + private int txPower; + private int rssi; + private int dataStatus; + + // periodic advertising data. + @Nullable + private ScanRecord data; + + // Device timestamp when the result was last seen. + private long timestampNanos; + + /** + * Constructor of periodic advertising result. + * + */ + public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi, + int dataStatus, ScanRecord data) { + this.syncHandle = syncHandle; + this.txPower = txPower; + this.rssi = rssi; + this.dataStatus = dataStatus; + this.data = data; + } + + private PeriodicAdvertisingReport(Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(syncHandle); + dest.writeLong(txPower); + dest.writeInt(rssi); + dest.writeInt(dataStatus); + if (data != null) { + dest.writeInt(1); + dest.writeByteArray(data.getBytes()); + } else { + dest.writeInt(0); + } + } + + private void readFromParcel(Parcel in) { + syncHandle = in.readInt(); + txPower = in.readInt(); + rssi = in.readInt(); + dataStatus = in.readInt(); + if (in.readInt() == 1) { + data = ScanRecord.parseFromBytes(in.createByteArray()); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Returns the synchronization handle. + */ + public int getSyncHandle() { + return syncHandle; + } + + /** + * Returns the transmit power in dBm. The valid range is [-127, 126]. Value + * of 127 means information was not available. + */ + public int getTxPower() { + return txPower; + } + + /** + * Returns the received signal strength in dBm. The valid range is [-127, 20]. + */ + public int getRssi() { + return rssi; + } + + /** + * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE} + * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}. + */ + public int getDataStatus() { + return dataStatus; + } + + /** + * Returns the data contained in this periodic advertising report. + */ + @Nullable + public ScanRecord getData() { + return data; + } + + /** + * Returns timestamp since boot when the scan record was observed. + */ + public long getTimestampNanos() { + return timestampNanos; + } + + @Override + public int hashCode() { + return Objects.hash(syncHandle, txPower, rssi, dataStatus, data, timestampNanos); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj; + return (syncHandle == other.syncHandle) && + (txPower == other.txPower) && + (rssi == other.rssi) && + (dataStatus == other.dataStatus) && + Objects.equals(data, other.data) && + (timestampNanos == other.timestampNanos); + } + + @Override + public String toString() { + return "PeriodicAdvertisingReport{syncHandle=" + syncHandle + + ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus + + ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}'; + } + + public static final Parcelable.Creator CREATOR = new Creator() { + @Override + public PeriodicAdvertisingReport createFromParcel(Parcel source) { + return new PeriodicAdvertisingReport(source); + } + + @Override + public PeriodicAdvertisingReport[] newArray(int size) { + return new PeriodicAdvertisingReport[size]; + } + }; +} -- GitLab From ad091bb281755f4c433c0381492b154cdd23e06f Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 2 Feb 2017 08:07:12 -0800 Subject: [PATCH 0699/1408] Bluetooth 5 PHY selection API Bug: 30622771 Test: manual Change-Id: I50262a56a70466439f9700549c3c0e7bd49e2e8d (cherry picked from commit db5a87d50db9d4b3d642603f6c329b83ee5851f6) --- .../android/bluetooth/BluetoothDevice.java | 99 +++++++++- .../java/android/bluetooth/BluetoothGatt.java | 125 +++++++++--- .../bluetooth/BluetoothGattCallback.java | 132 +------------ .../bluetooth/BluetoothGattCallbackExt.java | 182 +++++++++++++++++ .../bluetooth/BluetoothGattServer.java | 96 ++++++++- .../BluetoothGattServerCallback.java | 138 +------------ .../BluetoothGattServerCallbackExt.java | 187 ++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 14 +- ...ck.aidl => IBluetoothGattCallbackExt.aidl} | 4 +- ...l => IBluetoothGattServerCallbackExt.aidl} | 6 +- 10 files changed, 689 insertions(+), 294 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothGattCallbackExt.java create mode 100644 framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java rename framework/java/android/bluetooth/{IBluetoothGattCallback.aidl => IBluetoothGattCallbackExt.aidl} (89%) rename framework/java/android/bluetooth/{IBluetoothGattServerCallback.aidl => IBluetoothGattServerCallbackExt.aidl} (88%) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7b1e687f686..31fc294e318 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -592,6 +592,42 @@ public final class BluetoothDevice implements Parcelable { */ public static final int TRANSPORT_LE = 2; + /** + * 1M initiating PHY. + */ + public static final int PHY_LE_1M = 1; + + /** + * 2M initiating PHY. + */ + public static final int PHY_LE_2M = 2; + + /** + * LE Coded initiating PHY. + */ + public static final int PHY_LE_CODED = 4; + + /** + * Any LE PHY. + */ + public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED; + + /** + * No preferred coding when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_NO_PREFERRED = 0; + + /** + * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_S2 = 1; + + /** + * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY. + */ + public static final int PHY_OPTION_S8 = 2; + + /** @hide */ public static final String EXTRA_MAS_INSTANCE = "android.bluetooth.device.extra.MAS_INSTANCE"; @@ -1615,6 +1651,67 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback, int transport) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, + * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if + * {@code autoConnect} is set to true. + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallbackExt callback, int transport, int phy) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1625,7 +1722,7 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport); + BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 97a32974d2a..0cb69ae519f 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -31,7 +31,7 @@ import java.util.UUID; *

        This class provides Bluetooth GATT functionality to enable communication * with Bluetooth Smart or Smart Ready devices. * - *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallback} + *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt} * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. * GATT capable devices can be discovered using the Bluetooth device discovery or BLE * scan process. @@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean VDBG = false; private IBluetoothGatt mService; - private BluetoothGattCallback mCallback; + private BluetoothGattCallbackExt mCallback; private int mClientIf; private BluetoothDevice mDevice; private boolean mAutoConnect; @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; private int mTransport; + private int mPhy; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -132,10 +133,10 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_MITM = 2; /** - * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. + * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation. */ - private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { + private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt = + new IBluetoothGattCallbackExt.Stub() { /** * Application interface registered - app is ready to go * @hide @@ -161,12 +162,50 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } } + /** + * Phy update callback + * @hide + */ + @Override + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + if (!address.equals(mDevice.getAddress())) { + return; + } + + try { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } + + /** + * Phy read callback + * @hide + */ + @Override + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyRead() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + if (!address.equals(mDevice.getAddress())) { + return; + } + + try { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } + /** * Client connection state changed * @hide @@ -503,10 +542,11 @@ public final class BluetoothGatt implements BluetoothProfile { }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport) { + int transport, int phy) { mService = iGatt; mDevice = device; mTransport = transport; + mPhy = phy; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -578,7 +618,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Register an application callback to start using GATT. * - *

        This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + *

        This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered} * is used to notify success or failure if the function returns true. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -587,7 +627,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return If true, the callback will be called to notify success or failure, * false on immediate error */ - private boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallbackExt callback) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -596,7 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -626,7 +666,7 @@ public final class BluetoothGatt implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattCallback#onConnectionStateChange} callback will be + * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect parameter determines whether to actively connect to @@ -644,7 +684,7 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) { if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); synchronized(mStateLock) { if (mConnState != CONN_STATE_IDLE) { @@ -696,7 +736,7 @@ public final class BluetoothGatt implements BluetoothProfile { public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), - false, mTransport); // autoConnect is inverse of "isDirect" + false, mTransport, mPhy); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); @@ -704,6 +744,45 @@ public final class BluetoothGatt implements BluetoothProfile { } } + /** + * Set the preferred connection PHY for this app. Please note that this is just a + * recommendation, wether the PHY change will happen depends on other applications peferences, + * local and remote controller capabilities. Controller can override these settings. + *

        + * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * if no PHY change happens. It is also triggered when remote device updates the PHY. + * + * @param txPhy preferred transmitter PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, + * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + */ + public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { + try { + mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, + phyOptions); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Read the current transmitter PHY and receiver PHY of the connection. The values are returned + * in {@link BluetoothGattCallbackExt#onPhyRead} + */ + public void readPhy() { + try { + mService.clientReadPhy(mClientIf, mDevice.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + /** * Return the remote bluetooth device this GATT client targets to * @@ -718,7 +797,7 @@ public final class BluetoothGatt implements BluetoothProfile { * characteristics and descriptors. * *

        This is an asynchronous operation. Once service discovery is completed, - * the {@link BluetoothGattCallback#onServicesDiscovered} callback is + * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is * triggered. If the discovery was successful, the remote services can be * retrieved using the {@link #getServices} function. * @@ -797,7 +876,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the requested characteristic from the associated remote device. * *

        This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead} * callback. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -839,7 +918,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Writes a given characteristic and its values to the associated remote device. * *

        Once the write operation has been completed, the - * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked, * reporting the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -883,7 +962,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the value for a given descriptor from the associated remote device. * *

        Once the read operation has been completed, the - * {@link BluetoothGattCallback#onDescriptorRead} callback is + * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is * triggered, signaling the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -924,7 +1003,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Write the value of a given descriptor to the associated remote device. * - *

        A {@link BluetoothGattCallback#onDescriptorWrite} callback is + *

        A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is * triggered to report the result of the write operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -968,7 +1047,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Once a reliable write transaction has been initiated, all calls * to {@link #writeCharacteristic} are sent to the remote device for * verification and queued up for atomic execution. The application will - * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback + * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback * in response to every {@link #writeCharacteristic} call and is responsible * for verifying if the value has been transmitted accurately. * @@ -1002,7 +1081,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        This function will commit all queued up characteristic write * operations for a given remote device. * - *

        A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is + *

        A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is * invoked to indicate whether the transaction has been executed correctly. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1059,7 +1138,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Enable or disable notifications/indications for a given characteristic. * *

        Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be + * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be * triggered if the remote device indicates that the given characteristic * has changed. * @@ -1114,7 +1193,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Read the RSSI for a connected remote device. * - *

        The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be + *

        The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be * invoked when the RSSI value has been read. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1142,7 +1221,7 @@ public final class BluetoothGatt implements BluetoothProfile { * the data sent is truncated to the MTU size. This function may be used * to request a larger MTU size to be able to send more data at once. * - *

        A {@link BluetoothGattCallback#onMtuChanged} callback will indicate + *

        A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate * whether this operation was successful. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index a9156205afc..4da106df610 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -18,138 +18,22 @@ package android.bluetooth; /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. + * @deprecated use {@link BluetoothGattCallbackExt} */ -public abstract class BluetoothGattCallback { +public abstract class BluetoothGattCallback extends BluetoothGattCallbackExt { /** - * Callback indicating when GATT client has connected/disconnected to/from a remote - * GATT server. - * - * @param gatt GATT client - * @param status Status of the connect or disconnect operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @hide */ - public void onConnectionStateChange(BluetoothGatt gatt, int status, - int newState) { + @Override + public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } /** - * Callback invoked when the list of remote services, characteristics and descriptors - * for the remote device have been updated, ie new services have been discovered. - * - * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device - * has been explored successfully. + * @hide */ - public void onServicesDiscovered(BluetoothGatt gatt, int status) { + @Override + public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } - /** - * Callback reporting the result of a characteristic read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} - * @param characteristic Characteristic that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully. - */ - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, - int status) { - } - - /** - * Callback indicating the result of a characteristic write operation. - * - *

        If this callback is invoked while a reliable write transaction is - * in progress, the value of the characteristic represents the value - * reported by the remote device. An application should compare this - * value to the desired value to be written. If the values don't match, - * the application must abort the reliable write transaction. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} - * @param characteristic Characteristic that was written to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - } - - /** - * Callback triggered as a result of a remote characteristic notification. - * - * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result - * of a remote notification event. - */ - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - } - - /** - * Callback reporting the result of a descriptor read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully - */ - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback indicating the result of a descriptor write operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} - * @param descriptor Descriptor that was writte to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback invoked when a reliable write transaction has been completed. - * - * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write - * transaction was executed successfully - */ - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the RSSI for a remote device connection. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#readRemoteRssi} function. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} - * @param rssi The RSSI value for the remote device - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully - */ - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#requestMtu} function, or in response to a connection - * event. - * - * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} - * @param mtu The new MTU size - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully - */ - public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { - } } diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattCallbackExt.java new file mode 100644 index 00000000000..63774c8fbb6 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattCallbackExt.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 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; + +/** + * This abstract class is used to implement {@link BluetoothGatt} callbacks. + */ +public abstract class BluetoothGattCallbackExt { + + /** + * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of + * remote device changing the PHY. + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation + */ + public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { + } + + /** + * Callback triggered as result of {@link BluetoothGatt#readPhy} + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation + */ + public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { + } + + /** + * Callback indicating when GATT client has connected/disconnected to/from a remote + * GATT server. + * + * @param gatt GATT client + * @param status Status of the connect or disconnect operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothGatt gatt, int status, + int newState) { + } + + /** + * Callback invoked when the list of remote services, characteristics and descriptors + * for the remote device have been updated, ie new services have been discovered. + * + * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device + * has been explored successfully. + */ + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the result of a characteristic read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} + * @param characteristic Characteristic that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully. + */ + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback indicating the result of a characteristic write operation. + * + *

        If this callback is invoked while a reliable write transaction is + * in progress, the value of the characteristic represents the value + * reported by the remote device. An application should compare this + * value to the desired value to be written. If the values don't match, + * the application must abort the reliable write transaction. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} + * @param characteristic Characteristic that was written to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + } + + /** + * Callback triggered as a result of a remote characteristic notification. + * + * @param gatt GATT client the characteristic is associated with + * @param characteristic Characteristic that has been updated as a result + * of a remote notification event. + */ + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + } + + /** + * Callback reporting the result of a descriptor read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param descriptor Descriptor that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully + */ + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback indicating the result of a descriptor write operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} + * @param descriptor Descriptor that was writte to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback invoked when a reliable write transaction has been completed. + * + * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write + * transaction was executed successfully + */ + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the RSSI for a remote device connection. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#readRemoteRssi} function. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} + * @param rssi The RSSI value for the remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully + */ + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#requestMtu} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + */ + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + } +} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 5ffceba5e11..9ee739f04bf 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -46,7 +46,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; - private BluetoothGattServerCallback mCallback; + private BluetoothGattServerCallbackExt mCallback; private Object mServerIfLock = new Object(); private int mServerIf; @@ -59,8 +59,8 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Bluetooth GATT interface callbacks */ - private final IBluetoothGattServerCallback mBluetoothGattServerCallback = - new IBluetoothGattServerCallback.Stub() { + private final IBluetoothGattServerCallbackExt mBluetoothGattServerCallback = + new IBluetoothGattServerCallbackExt.Stub() { /** * Application interface registered - app is ready to go * @hide @@ -292,6 +292,42 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception: " + ex); } } + + /** + * The PHY for a connection was updated + * @hide + */ + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyUpdate(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + /** + * The PHY for a connection was read + * @hide + */ + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyRead(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } }; /** @@ -360,7 +396,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return true, the callback will be called to notify success or failure, * false on immediate error */ - /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + /*package*/ boolean registerCallback(BluetoothGattServerCallbackExt callback) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -436,7 +472,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be + * {@link BluetoothGattServerCallbackExt#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect paramter determines whether to actively connect to @@ -487,6 +523,48 @@ public final class BluetoothGattServer implements BluetoothProfile { } } + /** + * Set the preferred connection PHY for this app. Please note that this is just a + * recommendation, wether the PHY change will happen depends on other applications peferences, + * local and remote controller capabilities. Controller can override these settings. + *

        + * {@link BluetoothGattServerCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * if no PHY change happens. It is also triggered when remote device updates the PHY. + * + * @param device The remote device to send this response to + * @param txPhy preferred transmitter PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and + * {@link BluetoothDevice#PHY_LE_CODED}. + * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, + * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + */ + public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { + try { + mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, + phyOptions); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Read the current transmitter PHY and receiver PHY of the connection. The values are returned + * in {@link BluetoothGattServerCallbackExt#onPhyRead} + * + * @param device The remote device to send this response to + */ + public void readPhy(BluetoothDevice device) { + try { + mService.serverReadPhy(mServerIf, device.getAddress()); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + /** * Send a response to a read or write request to a remote device. * @@ -494,10 +572,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * is received by one of these callback methods: * *

          - *
        • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} - *
        • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorReadRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} + *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicReadRequest} + *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicWriteRequest} + *
        • {@link BluetoothGattServerCallbackExt#onDescriptorReadRequest} + *
        • {@link BluetoothGattServerCallbackExt#onDescriptorWriteRequest} *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 2afcf9a322b..75ceb52c2d5 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 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. @@ -20,141 +20,21 @@ import android.bluetooth.BluetoothDevice; /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + * @deprecated please use {@link BluetoothGattServerCallbackExt} */ -public abstract class BluetoothGattServerCallback { +public abstract class BluetoothGattServerCallback extends BluetoothGattServerCallbackExt { /** - * Callback indicating when a remote device has been connected or disconnected. - * - * @param device Remote device that has been connected or disconnected. - * @param status Status of the connect or disconnect operation. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @hide */ - public void onConnectionStateChange(BluetoothDevice device, int status, - int newState) { + @Override + public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } /** - * Indicates whether a local service has been added successfully. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service - * was added successfully. - * @param service The service that has been added + * @hide */ - public void onServiceAdded(int status, BluetoothGattService service) { - } - - /** - * A remote client has requested to read a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param characteristic Characteristic to be read - */ - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) { - } - - /** - * A remote client has requested to write to a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param characteristic Characteristic to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the characteristic - */ - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * A remote client has requested to read a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param descriptor Descriptor to be read - */ - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { - } - - /** - * A remote client has requested to write to a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param descriptor Descriptor to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the descriptor - */ - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * Execute all pending write operations for this device. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operations - * @param requestId The Id of the request - * @param execute Whether the pending writes should be executed (true) or - * cancelled (false) - */ - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { - } - - /** - * Callback invoked when a notification or indication has been sent to - * a remote device. - * - *

        When multiple notifications are to be sent, an application must - * wait for this callback to be received before sending additional - * notifications. - * - * @param device The remote device the notification has been sent to - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful - */ - public void onNotificationSent(BluetoothDevice device, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - *

        This callback will be invoked if a remote client has requested to change - * the MTU for a given connection. - * - * @param device The remote device that requested the MTU change - * @param mtu The new MTU size - */ - public void onMtuChanged(BluetoothDevice device, int mtu) { + @Override + public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java new file mode 100644 index 00000000000..455cce04c58 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * This abstract class is used to implement {@link BluetoothGattServer} callbacks. + */ +public abstract class BluetoothGattServerCallbackExt { + + /** + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Indicates whether a local service has been added successfully. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service + * was added successfully. + * @param service The service that has been added + */ + public void onServiceAdded(int status, BluetoothGattService service) { + } + + /** + * A remote client has requested to read a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param characteristic Characteristic to be read + */ + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattCharacteristic characteristic) { + } + + /** + * A remote client has requested to write to a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param characteristic Characteristic to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the characteristic + */ + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * A remote client has requested to read a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param descriptor Descriptor to be read + */ + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattDescriptor descriptor) { + } + + /** + * A remote client has requested to write to a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param descriptor Descriptor to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the descriptor + */ + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * Execute all pending write operations for this device. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operations + * @param requestId The Id of the request + * @param execute Whether the pending writes should be executed (true) or + * cancelled (false) + */ + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + } + + /** + * Callback invoked when a notification or indication has been sent to + * a remote device. + * + *

        When multiple notifications are to be sent, an application must + * wait for this callback to be received before sending additional + * notifications. + * + * @param device The remote device the notification has been sent to + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful + */ + public void onNotificationSent(BluetoothDevice device, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + *

        This callback will be invoked if a remote client has requested to change + * the MTU for a given connection. + * + * @param device The remote device that requested the MTU change + * @param mtu The new MTU size + */ + public void onMtuChanged(BluetoothDevice device, int mtu) { + } + + /** + * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result + * of remote device changing the PHY. + * + * @param device The remote device + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation + */ + public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { + } + + /** + * Callback triggered as result of {@link BluetoothGattServer#readPhy} + * + * @param device The remote device that requested the PHY read + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation + */ + public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { + } +} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 69563cb3e6a..5282e9fceba 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -27,8 +27,8 @@ import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; import android.os.WorkSource; -import android.bluetooth.IBluetoothGattCallback; -import android.bluetooth.IBluetoothGattServerCallback; +import android.bluetooth.IBluetoothGattCallbackExt; +import android.bluetooth.IBluetoothGattServerCallbackExt; import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; @@ -58,10 +58,12 @@ interface IBluetoothGatt { void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); - void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); void clientDisconnect(in int clientIf, in String address); + void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); + void clientReadPhy(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); @@ -77,10 +79,12 @@ interface IBluetoothGatt { void configureMTU(in int clientIf, in String address, in int mtu); void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); + void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallbackExt callback); void unregisterServer(in int serverIf); void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); + void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); + void serverReadPhy(in int clientIf, in String address); void addService(in int serverIf, in BluetoothGattService service); void removeService(in int serverIf, in int handle); void clearServices(in int serverIf); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl similarity index 89% rename from framework/java/android/bluetooth/IBluetoothGattCallback.aidl rename to framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl index 72cb6182773..736f4b2b048 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl @@ -22,10 +22,12 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattCallback { +oneway interface IBluetoothGattCallbackExt { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); + void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); + void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); void onSearchComplete(in String address, in List services, in int status); void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); void onCharacteristicWrite(in String address, in int status, in int handle); diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl similarity index 88% rename from framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl rename to framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl index 1a924fb4e31..091ffb3fe98 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 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. @@ -21,7 +21,7 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattServerCallback { +oneway interface IBluetoothGattServerCallbackExt { void onServerRegistered(in int status, in int serverIf); void onServerConnectionState(in int status, in int serverIf, in boolean connected, in String address); @@ -40,4 +40,6 @@ oneway interface IBluetoothGattServerCallback { void onExecuteWrite(in String address, in int transId, in boolean execWrite); void onNotificationSent(in String address, in int status); void onMtuChanged(in String address, in int mtu); + void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); + void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); } -- GitLab From 258e43c15602c8246bc565150158b1c56e3c0814 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 10 Jan 2017 06:15:54 -0800 Subject: [PATCH 0700/1408] Bluetooth 5 Advertising API Test: manual Bug: 30622771 Change-Id: Id6856e6110872ec50ff1af54ddc75c0104a6459c (cherry picked from commit 5324a14cf490656269ef862d7f8f6b139a21c0e6) --- .../android/bluetooth/IBluetoothGatt.aidl | 17 + .../android/bluetooth/le/AdvertisingSet.java | 162 +++++++ .../bluetooth/le/AdvertisingSetCallback.java | 144 ++++++ .../le/AdvertisingSetParameters.aidl | 19 + .../le/AdvertisingSetParameters.java | 409 ++++++++++++++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 194 +++++++++ .../bluetooth/le/IAdvertisingSetCallback.aidl | 32 ++ .../le/PeriodicAdvertisingParameters.aidl | 19 + .../le/PeriodicAdvertisingParameters.java | 134 ++++++ .../java/android/bluetooth/le/ScanResult.java | 2 +- 10 files changed, 1131 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/le/AdvertisingSet.java create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetCallback.java create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl create mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.java create mode 100644 framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl create mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 5282e9fceba..33fedc71898 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -20,6 +20,8 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertisingSetParameters; +import android.bluetooth.le.PeriodicAdvertisingParameters; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; @@ -30,6 +32,7 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallbackExt; import android.bluetooth.IBluetoothGattServerCallbackExt; import android.bluetooth.le.IAdvertiserCallback; +import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; @@ -55,10 +58,24 @@ interface IBluetoothGatt { in AdvertiseSettings settings); void stopMultiAdvertising(in int advertiserId); + void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, + in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, + in AdvertiseData periodicData, in IAdvertisingSetCallback callback); + void stopAdvertisingSet(in IAdvertisingSetCallback callback); + + void enableAdverisingSet(in int advertiserId, in boolean enable); + void setAdvertisingData(in int advertiserId, in AdvertiseData data); + void setScanResponseData(in int advertiserId, in AdvertiseData data); + void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); + void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); + void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); + void periodicAdvertisingEnable(in int advertiserId, in boolean enable); + void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback); + void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); void clientDisconnect(in int clientIf, in String address); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java new file mode 100644 index 00000000000..1524022b1f0 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothManager; +import android.bluetooth.le.IAdvertisingSetCallback; +import android.os.RemoteException; +import android.util.Log; + +/** + * This class provides a way to control single Bluetooth LE advertising instance. + *

        + * To get an instance of {@link AdvertisingSet}, call the + * {@link BluetoothLeAdvertiser#startAdvertisingSet} method. + *

        + * Note: Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @see AdvertiseData + */ +public final class AdvertisingSet { + private static final String TAG = "AdvertisingSet"; + + private final IBluetoothGatt gatt; + private int advertiserId; + + /* package */ AdvertisingSet(int advertiserId, + IBluetoothManager bluetoothManager) { + this.advertiserId = advertiserId; + + try { + this.gatt = bluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + throw new IllegalStateException("Failed to get Bluetooth"); + } + } + + /* package */ void setAdvertiserId(int advertiserId) { + this.advertiserId = advertiserId; + } + + /** + * Enables Advertising. This method returns immediately, the operation status is + * delivered + * through {@code callback.onAdvertisingEnabled()}. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * + */ + public void enableAdvertising(boolean enable) { + try { + gatt.enableAdverisingSet(this.advertiserId, enable); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for + * specified AdvertisingSetParameters. This method returns immediately, the operation status is + * delivered through {@code callback.onAdvertisingDataSet()}. + *

        + * Advertising data must be empty if non-legacy scannable advertising is used. + */ + public void setAdvertisingData(AdvertiseData data) { + try { + gatt.setAdvertisingData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Set/update scan response data. Make sure that data doesn't exceed the size limit for + * specified AdvertisingSetParameters. This method returns immediately, the operation status + * is delivered through {@code callback.onScanResponseDataSet()}. + */ + public void setScanResponseData(AdvertiseData data) { + try { + gatt.setScanResponseData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Update advertising parameters associated with this AdvertisingSet. Must be called when + * advertising is not active. This method returns immediately, the operation status is delivered + * through {@code callback.onAdvertisingParametersUpdated}. + */ + public void setAdvertisingParameters(AdvertisingSetParameters parameters) { + try { + gatt.setAdvertisingParameters(this.advertiserId, parameters); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Update periodic advertising parameters associated with this set. Must be called when + * periodic advertising is not enabled. This method returns immediately, the operation + * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}. + */ + public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { + try { + gatt.setPeriodicAdvertisingParameters(this.advertiserId, parameters); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters, + * or after advertising was started with periodic advertising data set. This method returns + * immediately, the operation status is delivered through + * {@code callback.onPeriodicAdvertisingDataSet()}. + */ + public void setPeriodicAdvertisingData(AdvertiseData data) { + try { + gatt.setPeriodicAdvertisingData(this.advertiserId, data); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Used to enable/disable periodic advertising. This method returns immediately, the operation + * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. + */ + public void periodicAdvertisingEnable(boolean enable) { + try { + gatt.periodicAdvertisingEnable(this.advertiserId, enable); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Returns advertiserId associated with thsi advertising set. + * + * @hide + */ + public int getAdvertiserId(){ + return advertiserId; + } +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java new file mode 100644 index 00000000000..ceed8d9e3c6 --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 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.le; + +import android.bluetooth.BluetoothDevice; + +/** + * Bluetooth LE advertising set callbacks, used to deliver advertising operation + * status. + */ +public abstract class AdvertisingSetCallback { + + /** + * The requested operation was successful. + */ + public static final int ADVERTISE_SUCCESS = 0; + + /** + * Failed to start advertising as the advertise data to be broadcasted is too + * large. + */ + public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; + + /** + * Failed to start advertising because no advertising instance is available. + */ + public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; + + /** + * Failed to start advertising as the advertising is already started. + */ + public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; + + /** + * Operation failed due to an internal error. + */ + public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; + + /** + * This feature is not supported on this platform. + */ + public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} + * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet + * contains the started set and it is advertising. If error occured, advertisingSet is + * null, and status will be set to proper error code. + * + * @param advertisingSet The advertising set that was started or null if error. + * @param status Status of the operation. + */ + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} + * indicating advertising set is stopped. + * + * @param advertisingSet The advertising set. + */ + public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {} + + /** + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} indicating + * result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is advertising. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating + * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating + * result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void + onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet, + int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param status Status of the operation. + */ + public void onPeriodicAdvertisingEnable(AdvertisingSet advertisingSet, boolean enable, + int status) {} +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl new file mode 100644 index 00000000000..39034a001fa --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable AdvertisingSetParameters; diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java new file mode 100644 index 00000000000..03a01e171ba --- /dev/null +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2017 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The {@link AdvertisingSetParameters} provide a way to adjust advertising + * preferences for each + * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to + * create an + * instance of this class. + */ +public final class AdvertisingSetParameters implements Parcelable { + + /** + * 1M advertiser PHY. + */ + public static final int PHY_LE_1M = 1; + + /** + * 2M advertiser PHY. + */ + public static final int PHY_LE_2M = 2; + + /** + * LE Coded advertiser PHY. + */ + public static final int PHY_LE_CODED = 3; + + /** + * Advertise on low frequency, around every 1000ms. This is the default and + * preferred advertising mode as it consumes the least power. + */ + public static final int INTERVAL_LOW = 1600; + + /** + * Advertise on medium frequency, around every 250ms. This is balanced + * between advertising frequency and power consumption. + */ + public static final int INTERVAL_MEDIUM = 400; + + /** + * Perform high frequency, low latency advertising, around every 100ms. This + * has the highest power consumption and should not be used for continuous + * background advertising. + */ + public static final int INTERVAL_HIGH = 160; + + /** + * Minimum value for advertising interval. + */ + public static final int INTERVAL_MIN = 160; + + /** + * Maximum value for advertising interval. + */ + public static final int INTERVAL_MAX = 16777215; + + /** + * Advertise using the lowest transmission (TX) power level. Low transmission + * power can be used to restrict the visibility range of advertising packets. + */ + public static final int TX_POWER_ULTRA_LOW = -21; + + /** + * Advertise using low TX power level. + */ + public static final int TX_POWER_LOW = -15; + + /** + * Advertise using medium TX power level. + */ + public static final int TX_POWER_MEDIUM = -7; + + /** + * Advertise using high TX power level. This corresponds to largest visibility + * range of the advertising packet. + */ + public static final int TX_POWER_HIGH = 1; + + /** + * Minimum value for TX power. + */ + public static final int TX_POWER_MIN = -127; + + /** + * Maximum value for TX power. + */ + public static final int TX_POWER_MAX = 1; + + /** + * The maximum limited advertisement duration as specified by the Bluetooth + * SIG + */ + private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + + private final boolean isLegacy; + private final boolean isAnonymous; + private final boolean includeTxPower; + private final int primaryPhy; + private final int secondaryPhy; + private final boolean connectable; + private final int interval; + private final int txPowerLevel; + private final int timeoutMillis; + + private AdvertisingSetParameters(boolean connectable, boolean isLegacy, + boolean isAnonymous, boolean includeTxPower, + int primaryPhy, int secondaryPhy, + int interval, int txPowerLevel, + int timeoutMillis) { + this.connectable = connectable; + this.isLegacy = isLegacy; + this.isAnonymous = isAnonymous; + this.includeTxPower = includeTxPower; + this.primaryPhy = primaryPhy; + this.secondaryPhy = secondaryPhy; + this.interval = interval; + this.txPowerLevel = txPowerLevel; + this.timeoutMillis = timeoutMillis; + } + + private AdvertisingSetParameters(Parcel in) { + connectable = in.readInt() != 0 ? true : false; + isLegacy = in.readInt() != 0 ? true : false; + isAnonymous = in.readInt() != 0 ? true : false; + includeTxPower = in.readInt() != 0 ? true : false; + primaryPhy = in.readInt(); + secondaryPhy = in.readInt(); + interval = in.readInt(); + txPowerLevel = in.readInt(); + timeoutMillis = in.readInt(); + } + + /** + * Returns whether the advertisement will be connectable. + */ + public boolean isConnectable() { return connectable; } + + /** + * Returns whether the legacy advertisement will be used. + */ + public boolean isLegacy() { return isLegacy; } + + /** + * Returns whether the advertisement will be anonymous. + */ + public boolean isAnonymous() { return isAnonymous; } + + /** + * Returns whether the TX Power will be included. + */ + public boolean includeTxPower() { return includeTxPower; } + + /** + * Returns the primary advertising phy. + */ + public int getPrimaryPhy() { return primaryPhy; } + + /** + * Returns the secondary advertising phy. + */ + public int getSecondaryPhy() { return secondaryPhy; } + + /** + * Returns the advertising interval. + */ + public int getInterval() { return interval; } + + /** + * Returns the TX power level for advertising. + */ + public int getTxPowerLevel() { return txPowerLevel; } + + /** + * Returns the advertising time limit in milliseconds. + */ + public int getTimeout() { return timeoutMillis; } + + @Override + public String toString() { + return "AdvertisingSetParameters [connectable=" + connectable + + ", isLegacy=" + isLegacy + + ", isAnonymous=" + isAnonymous + + ", includeTxPower=" + includeTxPower + + ", primaryPhy=" + primaryPhy + + ", secondaryPhy=" + secondaryPhy + + ", interval=" + interval + + ", txPowerLevel=" + txPowerLevel + + ", timeoutMillis=" + timeoutMillis + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(connectable ? 1 : 0); + dest.writeInt(isLegacy ? 1 : 0); + dest.writeInt(isAnonymous ? 1 : 0); + dest.writeInt(includeTxPower ? 1 : 0); + dest.writeInt(primaryPhy); + dest.writeInt(secondaryPhy); + dest.writeInt(interval); + dest.writeInt(txPowerLevel); + dest.writeInt(timeoutMillis); + } + + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public AdvertisingSetParameters[] newArray(int size) { + return new AdvertisingSetParameters[size]; + } + + @Override + public AdvertisingSetParameters createFromParcel(Parcel in) { + return new AdvertisingSetParameters(in); + } + }; + + /** + * Builder class for {@link AdvertisingSetParameters}. + */ + public static final class Builder { + + private boolean connectable = true; + private boolean isLegacy = false; + private boolean isAnonymous = false; + private boolean includeTxPower = false; + private int primaryPhy = PHY_LE_1M; + private int secondaryPhy = PHY_LE_1M; + private int interval = INTERVAL_LOW; + private int txPowerLevel = TX_POWER_MEDIUM; + private int timeoutMillis = 0; + + /** + * Set whether the advertisement type should be connectable or + * non-connectable. + * Legacy advertisements can be both connectable and scannable. Other + * advertisements can be connectable only if not scannable. + * @param connectable Controls whether the advertisment type will be + * connectable (true) or non-connectable (false). + */ + public Builder setConnectable(boolean connectable) { + this.connectable = connectable; + return this; + } + + /** + * When set to true, advertising set will advertise 4.x Spec compliant + * advertisements. + * + * @param isLegacy wether legacy advertising mode should be used. + */ + public Builder setLegacyMode(boolean isLegacy) { + this.isLegacy = isLegacy; + return this; + } + + /** + * Set wether advertiser address should be ommited from all packets. If this + * mode is used, periodic advertising can't be enabled for this set. + * + * This is used only if legacy mode is not used. + * + * @param isAnonymous wether anonymous advertising should be used. + */ + public Builder setAnonymouus(boolean isAnonymous) { + this.isAnonymous = isAnonymous; + return this; + } + + /** + * Set wether TX power should be included in the extended header. + * + * This is used only if legacy mode is not used. + * + * @param includeTxPower wether TX power should be included in extended + * header + */ + public Builder setIncludeTxPower(boolean includeTxPower) { + this.includeTxPower = includeTxPower; + return this; + } + + /** + * Set the primary physical channel used for this advertising set. + * + * This is used only if legacy mode is not used. + * + * @param primaryPhy Primary advertising physical channel, can only be + * {@link AdvertisingSetParameters#PHY_LE_1M} or + * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * @throws IllegalArgumentException If the primaryPhy is invalid. + */ + public Builder setPrimaryPhy(int primaryPhy) { + if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) { + throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); + } + this.primaryPhy = primaryPhy; + return this; + } + + /** + * Set the secondary physical channel used for this advertising set. + * + * This is used only if legacy mode is not used. + * + * @param secondaryPhy Secondary advertising physical channel, can only be + * one of {@link AdvertisingSetParameters#PHY_LE_1M}, + * {@link AdvertisingSetParameters#PHY_LE_2M} or + * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * @throws IllegalArgumentException If the secondaryPhy is invalid. + */ + public Builder setSecondaryPhy(int secondaryPhy) { + if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M && + secondaryPhy != PHY_LE_CODED) { + throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); + } + this.secondaryPhy = secondaryPhy; + return this; + } + + /** + * Set advertising interval. + * + * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid + * range is from 160 (100ms) to 16777215 (10,485.759375 s). + * Recommended values are: + * {@link AdvertisingSetParameters#INTERVAL_LOW}, + * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or + * {@link AdvertisingSetParameters#INTERVAL_HIGH}. + * @throws IllegalArgumentException If the interval is invalid. + */ + public Builder setInterval(int interval) { + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + throw new IllegalArgumentException("unknown interval " + interval); + } + this.interval = interval; + return this; + } + + /** + * Set the transmission power level for the advertising. + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in + * dBm. The valid range is [-127, 1] Recommended values are: + * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or + * {@link AdvertisingSetParameters#TX_POWER_HIGH}. + * + * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. + */ + public Builder setTxPowerLevel(int txPowerLevel) { + if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { + throw new IllegalArgumentException("unknown txPowerLevel " + + txPowerLevel); + } + this.txPowerLevel = txPowerLevel; + return this; + } + + /** + * Limit advertising to a given amount of time. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * milliseconds. A value of 0 will disable the time limit. + * @throws IllegalArgumentException If the provided timeout is over 180000 + * ms. + */ + public Builder setTimeout(int timeoutMillis) { + if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { + throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" + + LIMITED_ADVERTISING_MAX_MILLIS + + " milliseconds)"); + } + this.timeoutMillis = timeoutMillis; + return this; + } + + /** + * Build the {@link AdvertisingSetParameters} object. + */ + public AdvertisingSetParameters build() { + return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous, + includeTxPower, primaryPhy, + secondaryPhy, interval, txPowerLevel, + timeoutMillis); + } + } +} \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 94d03e533df..e03c9477a6a 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -62,6 +62,9 @@ public final class BluetoothLeAdvertiser { private BluetoothAdapter mBluetoothAdapter; private final Map mLeAdvertisers = new HashMap(); + private final Map + advertisingSetCallbackWrappers = new HashMap<>(); + private final Map advertisingSets = new HashMap<>(); /** * Use BluetoothAdapter.getLeAdvertiser() instead. @@ -155,6 +158,93 @@ public final class BluetoothLeAdvertiser { } } + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onNewAdvertisingSet()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param callback Callback for advertising set. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback) { + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, callback, new Handler(Looper.getMainLooper())); + } + + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onNewAdvertisingSet()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback, + Handler handler) { + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); + + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + throw new IllegalStateException("Failed to get Bluetooth"); + } + + IAdvertisingSetCallback wrapped = wrap(callback, handler); + advertisingSetCallbackWrappers.put(callback, wrapped); + + try { + gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to start advertising set - ", e); + throw new IllegalStateException("Failed to start advertising set"); + } + } + + /** + * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link + * BluetoothLeAdvertiser#startAdvertisingSet}. + */ + public void stopAdvertisingSet(AdvertisingSetCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + + IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback); + if (wrapped == null) { + throw new IllegalArgumentException( + "callback does not represent valid registered callback."); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + gatt.stopAdvertisingSet(wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising - ", e); + throw new IllegalStateException("Failed to stop advertising"); + } + } + /** * Cleans up advertisers. Should be called when bluetooth is down. * @@ -219,6 +309,110 @@ public final class BluetoothLeAdvertiser { return array == null ? 0 : array.length; } + IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { + return new IAdvertisingSetCallback.Stub() { + public void onAdvertisingSetStarted(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { + callback.onAdvertisingSetStarted(null, status); + advertisingSetCallbackWrappers.remove(callback); + return; + } + + AdvertisingSet advertisingSet = + new AdvertisingSet(advertiserId, mBluetoothManager); + advertisingSets.put(advertiserId, advertisingSet); + callback.onAdvertisingSetStarted(advertisingSet, status); + } + }); + } + + public void onAdvertisingSetStopped(int advertiserId) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingSetStopped(advertisingSet); + advertisingSets.remove(advertiserId); + advertisingSetCallbackWrappers.remove(callback); + } + }); + } + + public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingEnabled(advertisingSet, enabled, status); + } + }); + } + + public void onAdvertisingDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingDataSet(advertisingSet, status); + } + }); + } + + public void onScanResponseDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onScanResponseDataSet(advertisingSet, status); + } + }); + } + + public void onAdvertisingParametersUpdated(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onAdvertisingParametersUpdated(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingDataSet(int advertiserId, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingDataSet(advertisingSet, status); + } + }); + } + + public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status); + } + }); + } + }; + } + /** * Bluetooth GATT interface callbacks for advertising. */ diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl new file mode 100644 index 00000000000..4b0a111fa3d --- /dev/null +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 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.le; + +/** + * Callback definitions for interacting with Advertiser + * @hide + */ +oneway interface IAdvertisingSetCallback { + void onAdvertisingSetStarted(in int advertiserId, in int status); + void onAdvertisingSetStopped(in int advertiserId); + void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); + void onAdvertisingDataSet(in int advertiserId, in int status); + void onScanResponseDataSet(in int advertiserId, in int status); + void onAdvertisingParametersUpdated(in int advertiserId, in int status); + void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); + void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); + void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status); +} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl new file mode 100644 index 00000000000..f4bea22a12f --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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.le; + +parcelable PeriodicAdvertisingParameters; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java new file mode 100644 index 00000000000..ebc92bd0bcf --- /dev/null +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 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.le; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic + * advertising preferences for each Bluetooth LE advertising set. Use {@link + * AdvertisingSetParameters.Builder} to create an instance of this class. + */ +public final class PeriodicAdvertisingParameters implements Parcelable { + + private static final int INTERVAL_MAX = 80; + private static final int INTERVAL_MIN = 65519; + + private final boolean enable; + private final boolean includeTxPower; + private final int interval; + + private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) { + this.enable = enable; + this.includeTxPower = includeTxPower; + this.interval = interval; + } + + private PeriodicAdvertisingParameters(Parcel in) { + enable = in.readInt() != 0 ? true : false; + includeTxPower = in.readInt() != 0 ? true : false; + interval = in.readInt(); + } + + /** + * Returns whether the periodic advertising shall be enabled. + */ + public boolean getEnable() { return enable; } + + /** + * Returns whether the TX Power will be included. + */ + public boolean getIncludeTxPower() { return includeTxPower; } + + /** + * Returns the periodic advertising interval, in 1.25ms unit. + * Valid values are from 80 (100ms) to 65519 (81.89875s). + */ + public int getInterval() { return interval; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(enable ? 1 : 0); + dest.writeInt(includeTxPower ? 1 : 0); + dest.writeInt(interval); + } + + public static final Parcelable + .Creator CREATOR = + new Creator() { + @Override + public PeriodicAdvertisingParameters[] newArray(int size) { + return new PeriodicAdvertisingParameters[size]; + } + + @Override + public PeriodicAdvertisingParameters createFromParcel(Parcel in) { + return new PeriodicAdvertisingParameters(in); + } + }; + + public static final class Builder { + private boolean includeTxPower = false; + private boolean enable = false; + private int interval = INTERVAL_MAX; + + /** + * Set wether the Periodic Advertising should be enabled for this set. + */ + public Builder setEnable(boolean enable) { + this.enable = enable; + return this; + } + + /** + * Whether the transmission power level should be included in the periodic + * packet. + */ + public Builder setIncludeTxPower(boolean includeTxPower) { + this.includeTxPower = includeTxPower; + return this; + } + + /** + * Set advertising interval for periodic advertising, in 1.25ms unit. + * Valid values are from 80 (100ms) to 65519 (81.89875s). + * Value from range [interval, interval+20ms] will be picked as the actual value. + * @throws IllegalArgumentException If the interval is invalid. + */ + public Builder setInterval(int interval) { + if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { + throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN + + "-" + INTERVAL_MAX + ")"); + } + this.interval = interval; + return this; + } + + /** + * Build the {@link AdvertisingSetParameters} object. + */ + public PeriodicAdvertisingParameters build() { + return new PeriodicAdvertisingParameters(enable, includeTxPower, interval); + } + } +} diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index f92357b59c7..583ddd20fd7 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -214,7 +214,7 @@ public final class ScanResult implements Parcelable { } /** - * Returns the received signal strength in dBm. The valid range is [-127, 127]. + * Returns the received signal strength in dBm. The valid range is [-127, 126]. */ public int getRssi() { return mRssi; -- GitLab From 3e710b55879ed51620e1d70714c149f29f66f914 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 23 Feb 2017 18:24:39 -0800 Subject: [PATCH 0701/1408] Support multiple filters per association request By supporting multiple filters per one request we should be able to cover multiple kinds of use cases such as: - Letting the user select from a list of devices of more then one medium type (e.g. Bluetooth and BLE) - Allowing to provide multiple criteria for any field (e.g. filtering by more than one service UUID) Bug: 30932767 Test: Provide multiple filters and ensure that devices matching either are shown in the list to choose from. Ensure wifi SSIDs are shown in the list if wifi filter is provided Change-Id: I6621da388e2bf4ed97c5af2692629a321d0b63c7 --- framework/java/android/bluetooth/le/ScanFilter.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index b89c64a8cac..457096bf843 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import com.android.internal.util.BitUtils; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -345,15 +347,7 @@ public final class ScanFilter implements Parcelable { // Check if the uuid pattern matches the particular service uuid. private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - if (mask == null) { - return uuid.equals(data); - } - if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != - (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { - return false; - } - return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == - (data.getMostSignificantBits() & mask.getMostSignificantBits())); + return BitUtils.maskedEquals(data, uuid, mask); } // Check whether the data pattern matches the parsed data. -- GitLab From d57b7004e9d5e8b0e033dd8238b9f728bab73de1 Mon Sep 17 00:00:00 2001 From: Hector Tellez Date: Thu, 9 Mar 2017 17:55:43 +0000 Subject: [PATCH 0702/1408] Revert "Support multiple filters per association request" This reverts commit 3e710b55879ed51620e1d70714c149f29f66f914. Change-Id: I12857cbbea0a0c74521191ab5e3713db230626ab --- framework/java/android/bluetooth/le/ScanFilter.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 457096bf843..b89c64a8cac 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -23,8 +23,6 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; -import com.android.internal.util.BitUtils; - import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -347,7 +345,15 @@ public final class ScanFilter implements Parcelable { // Check if the uuid pattern matches the particular service uuid. private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - return BitUtils.maskedEquals(data, uuid, mask); + if (mask == null) { + return uuid.equals(data); + } + if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != + (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { + return false; + } + return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == + (data.getMostSignificantBits() & mask.getMostSignificantBits())); } // Check whether the data pattern matches the parsed data. -- GitLab From b6a687fac36248b927acc18b6a495089b5cc3dda Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 23 Feb 2017 18:24:39 -0800 Subject: [PATCH 0703/1408] Support multiple filters per association request By supporting multiple filters per one request we should be able to cover multiple kinds of use cases such as: - Letting the user select from a list of devices of more then one medium type (e.g. Bluetooth and BLE) - Allowing to provide multiple criteria for any field (e.g. filtering by more than one service UUID) Bug: 30932767 Test: Provide multiple filters and ensure that devices matching either are shown in the list to choose from. Ensure wifi SSIDs are shown in the list if wifi filter is provided Change-Id: I0a978787551a1ee5750ec5544b241d3bbfed5a7c --- framework/java/android/bluetooth/le/ScanFilter.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index b89c64a8cac..457096bf843 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; +import com.android.internal.util.BitUtils; + import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -345,15 +347,7 @@ public final class ScanFilter implements Parcelable { // Check if the uuid pattern matches the particular service uuid. private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - if (mask == null) { - return uuid.equals(data); - } - if ((uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) != - (data.getLeastSignificantBits() & mask.getLeastSignificantBits())) { - return false; - } - return ((uuid.getMostSignificantBits() & mask.getMostSignificantBits()) == - (data.getMostSignificantBits() & mask.getMostSignificantBits())); + return BitUtils.maskedEquals(data, uuid, mask); } // Check whether the data pattern matches the parsed data. -- GitLab From 3930c6d8e003941222bf7fbfdcf6f2aaea04e97e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 10 Mar 2017 16:07:59 -0800 Subject: [PATCH 0704/1408] Bluetooth 5 spelling fixes Bug: 30622771 Test: manual Change-Id: I46b6486619cc7366e56b25ca48937e6792f53e1d --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- .../java/android/bluetooth/le/AdvertisingSetParameters.java | 2 +- framework/java/android/bluetooth/le/ScanResult.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d36692a74a0..488511b49b8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1447,9 +1447,9 @@ public final class BluetoothAdapter { } /** - * Return true if LE Periodic Advertising feature is supported. + * Return true if LE Extended Advertising feature is supported. * - * @return true if chipset supports LE Periodic Advertising feature + * @return true if chipset supports LE Extended Advertising feature */ public boolean isLeExtendedAdvertisingSupported() { if (!getLeAccess()) return false; diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 03a01e171ba..453dd70a589 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -284,7 +284,7 @@ public final class AdvertisingSetParameters implements Parcelable { * * @param isAnonymous wether anonymous advertising should be used. */ - public Builder setAnonymouus(boolean isAnonymous) { + public Builder setAnonymous(boolean isAnonymous) { this.isAnonymous = isAnonymous; return this; } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 583ddd20fd7..745cd16c78f 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -67,12 +67,12 @@ public final class ScanResult implements Parcelable { public static final int SID_NOT_PRESENT = 0xFF; /** - * Mask for checking wether event type represents legacy advertisement. + * Mask for checking whether event type represents legacy advertisement. */ private static final int ET_LEGACY_MASK = 0x10; /** - * Mask for checking wether event type represents connectable advertisement. + * Mask for checking whether event type represents connectable advertisement. */ private static final int ET_CONNECTABLE_MASK = 0x01; -- GitLab From 842b81b37744f6dd4a18c7b02c31d6cb84a29b53 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 10 Mar 2017 16:07:59 -0800 Subject: [PATCH 0705/1408] Bluetooth 5 spelling fixes Bug: 30622771 Test: manual Change-Id: I46b6486619cc7366e56b25ca48937e6792f53e1d (cherry picked from commit 53501ad210dd5c65c7910a6b75b2b481e92dd22f) --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- .../java/android/bluetooth/le/AdvertisingSetParameters.java | 2 +- framework/java/android/bluetooth/le/ScanResult.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 818693773a0..c689da6ca77 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1447,9 +1447,9 @@ public final class BluetoothAdapter { } /** - * Return true if LE Periodic Advertising feature is supported. + * Return true if LE Extended Advertising feature is supported. * - * @return true if chipset supports LE Periodic Advertising feature + * @return true if chipset supports LE Extended Advertising feature */ public boolean isLeExtendedAdvertisingSupported() { if (!getLeAccess()) return false; diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 03a01e171ba..453dd70a589 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -284,7 +284,7 @@ public final class AdvertisingSetParameters implements Parcelable { * * @param isAnonymous wether anonymous advertising should be used. */ - public Builder setAnonymouus(boolean isAnonymous) { + public Builder setAnonymous(boolean isAnonymous) { this.isAnonymous = isAnonymous; return this; } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 583ddd20fd7..745cd16c78f 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -67,12 +67,12 @@ public final class ScanResult implements Parcelable { public static final int SID_NOT_PRESENT = 0xFF; /** - * Mask for checking wether event type represents legacy advertisement. + * Mask for checking whether event type represents legacy advertisement. */ private static final int ET_LEGACY_MASK = 0x10; /** - * Mask for checking wether event type represents connectable advertisement. + * Mask for checking whether event type represents connectable advertisement. */ private static final int ET_CONNECTABLE_MASK = 0x01; -- GitLab From 88b7599e0574c1f7c1969f46dd072dc5611a22a8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 15 Mar 2017 12:34:03 -0700 Subject: [PATCH 0706/1408] Bluetooth 5 AdvertisingSet implementation (1/4) This patch wires up fist methods of AdvertisingSet, making it possible to start advertising and stop advertising. It also replaces legacy implemementation with calls to new implementation. Bug: 30622771 Test: sl4a ConcurrentBleAdvertisingTest Change-Id: I80a4b1b2b632d5ef8a80d958615897684cfa0c16 --- .../android/bluetooth/IBluetoothGatt.aidl | 8 - .../bluetooth/le/BluetoothLeAdvertiser.java | 261 ++++++------------ 2 files changed, 85 insertions(+), 184 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 33fedc71898..29f29e7dba4 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -50,14 +50,6 @@ interface IBluetoothGatt { void stopScan(in int scannerId); void flushPendingBatchResults(in int scannerId); - void registerAdvertiser(in IAdvertiserCallback callback); - void unregisterAdvertiser(in int advertiserId); - void startMultiAdvertising(in int advertiserId, - in AdvertiseData advertiseData, - in AdvertiseData scanResponse, - in AdvertiseSettings settings); - void stopMultiAdvertising(in int advertiserId); - void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, in AdvertiseData periodicData, in IAdvertisingSetCallback callback); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index e03c9477a6a..c9f1d7a32ef 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -28,6 +28,7 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -60,11 +61,12 @@ public final class BluetoothLeAdvertiser { private final IBluetoothManager mBluetoothManager; private final Handler mHandler; private BluetoothAdapter mBluetoothAdapter; - private final Map - mLeAdvertisers = new HashMap(); + private final Map + mLegacyAdvertisers = new HashMap<>(); private final Map - advertisingSetCallbackWrappers = new HashMap<>(); - private final Map advertisingSets = new HashMap<>(); + mCallbackWrappers = Collections.synchronizedMap(new HashMap<>()); + private final Map + mAdvertisingSets = Collections.synchronizedMap(new HashMap<>()); /** * Use BluetoothAdapter.getLeAdvertiser() instead. @@ -109,7 +111,7 @@ public final class BluetoothLeAdvertiser { public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { - synchronized (mLeAdvertisers) { + synchronized (mLegacyAdvertisers) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -120,25 +122,65 @@ public final class BluetoothLeAdvertiser { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } - if (mLeAdvertisers.containsKey(callback)) { + if (mLegacyAdvertisers.containsKey(callback)) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); return; } - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; + AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder(); + parameters.setLegacyMode(true); + parameters.setConnectable(isConnectable); + parameters.setTimeout(settings.getTimeout()); + if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) { + parameters.setInterval(1600); // 1s + } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) { + parameters.setInterval(400); // 250ms + } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) { + parameters.setInterval(160); // 100ms + } + + if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) { + parameters.setTxPowerLevel(-21); + } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) { + parameters.setTxPowerLevel(-15); + } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) { + parameters.setTxPowerLevel(-7); + } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) { + parameters.setTxPowerLevel(1); } - AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData, - scanResponse, settings, gatt); - wrapper.startRegisteration(); + + AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); + mLegacyAdvertisers.put(callback, wrapped); + startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, + wrapped); } } + AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) { + return new AdvertisingSetCallback() { + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) { + if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { + postStartFailure(callback, status); + return; + } + + postStartSuccess(callback, settings); + } + + /* Legacy advertiser is disabled on timeout */ + public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { + if (enabled == true) { + Log.e(TAG, "Legacy advertiser should be only disabled on timeout," + + " but was enabled!"); + return; + } + + stopAdvertising(callback); + } + + }; + } + /** * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in * {@link BluetoothLeAdvertiser#startAdvertising}. @@ -148,20 +190,21 @@ public final class BluetoothLeAdvertiser { * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. */ public void stopAdvertising(final AdvertiseCallback callback) { - synchronized (mLeAdvertisers) { + synchronized (mLegacyAdvertisers) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } - AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback); + AdvertisingSetCallback wrapper = mLegacyAdvertisers.get(callback); if (wrapper == null) return; - wrapper.stopAdvertising(); + + stopAdvertisingSet(wrapper); } } /** * Creates a new advertising set. If operation succeed, device will start advertising. This * method returns immediately, the operation status is delivered through - * {@code callback.onNewAdvertisingSet()}. + * {@code callback.onAdvertisingSetStarted()}. *

        * @param parameters advertising set parameters. * @param advertiseData Advertisement data to be broadcasted. @@ -180,7 +223,7 @@ public final class BluetoothLeAdvertiser { /** * Creates a new advertising set. If operation succeed, device will start advertising. This * method returns immediately, the operation status is delivered through - * {@code callback.onNewAdvertisingSet()}. + * {@code callback.onAdvertisingSetStarted()}. *

        * @param parameters advertising set parameters. * @param advertiseData Advertisement data to be broadcasted. @@ -209,7 +252,10 @@ public final class BluetoothLeAdvertiser { } IAdvertisingSetCallback wrapped = wrap(callback, handler); - advertisingSetCallbackWrappers.put(callback, wrapped); + if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) { + throw new IllegalArgumentException( + "callback instance already associated with advertising"); + } try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, @@ -229,10 +275,9 @@ public final class BluetoothLeAdvertiser { throw new IllegalArgumentException("callback cannot be null"); } - IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback); + IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback); if (wrapped == null) { - throw new IllegalArgumentException( - "callback does not represent valid registered callback."); + return; } IBluetoothGatt gatt; @@ -251,7 +296,9 @@ public final class BluetoothLeAdvertiser { * @hide */ public void cleanup() { - mLeAdvertisers.clear(); + mLegacyAdvertisers.clear(); + mCallbackWrappers.clear(); + mAdvertisingSets.clear(); } // Compute the size of advertisement data or scan resp @@ -317,13 +364,13 @@ public final class BluetoothLeAdvertiser { public void run() { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { callback.onAdvertisingSetStarted(null, status); - advertisingSetCallbackWrappers.remove(callback); + mCallbackWrappers.remove(callback); return; } AdvertisingSet advertisingSet = new AdvertisingSet(advertiserId, mBluetoothManager); - advertisingSets.put(advertiserId, advertisingSet); + mAdvertisingSets.put(advertiserId, advertisingSet); callback.onAdvertisingSetStarted(advertisingSet, status); } }); @@ -333,10 +380,10 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onAdvertisingSetStopped(advertisingSet); - advertisingSets.remove(advertiserId); - advertisingSetCallbackWrappers.remove(callback); + mAdvertisingSets.remove(advertiserId); + mCallbackWrappers.remove(callback); } }); } @@ -345,7 +392,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onAdvertisingEnabled(advertisingSet, enabled, status); } }); @@ -355,7 +402,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onAdvertisingDataSet(advertisingSet, status); } }); @@ -365,7 +412,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onScanResponseDataSet(advertisingSet, status); } }); @@ -375,7 +422,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onAdvertisingParametersUpdated(advertisingSet, status); } }); @@ -385,7 +432,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status); } }); @@ -395,7 +442,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onPeriodicAdvertisingDataSet(advertisingSet, status); } }); @@ -405,7 +452,7 @@ public final class BluetoothLeAdvertiser { handler.post(new Runnable() { @Override public void run() { - AdvertisingSet advertisingSet = advertisingSets.get(advertiserId); + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status); } }); @@ -413,144 +460,6 @@ public final class BluetoothLeAdvertiser { }; } - /** - * Bluetooth GATT interface callbacks for advertising. - */ - private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub { - private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000; - private final AdvertiseCallback mAdvertiseCallback; - private final AdvertiseData mAdvertisement; - private final AdvertiseData mScanResponse; - private final AdvertiseSettings mSettings; - private final IBluetoothGatt mBluetoothGatt; - - // mAdvertiserId -1: not registered - // -2: advertise stopped or registration timeout - // >=0: registered and advertising started - private int mAdvertiserId; - private boolean mIsAdvertising = false; - private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR; - - public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback, - AdvertiseData advertiseData, AdvertiseData scanResponse, - AdvertiseSettings settings, - IBluetoothGatt bluetoothGatt) { - mAdvertiseCallback = advertiseCallback; - mAdvertisement = advertiseData; - mScanResponse = scanResponse; - mSettings = settings; - mBluetoothGatt = bluetoothGatt; - mAdvertiserId = -1; - } - - public void startRegisteration() { - synchronized (this) { - if (mAdvertiserId == -2) return; - - try { - mBluetoothGatt.registerAdvertiser(this); - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException | RemoteException e) { - Log.e(TAG, "Failed to start registeration", e); - } - if (mAdvertiserId >= 0 && mIsAdvertising) { - mLeAdvertisers.put(mAdvertiseCallback, this); - } else if (mAdvertiserId < 0) { - - // Registration timeout, reset mClientIf to -2 so no subsequent operations can - // proceed. - if (mAdvertiserId == -1) mAdvertiserId = -2; - // Post internal error if registration failed. - postStartFailure(mAdvertiseCallback, registrationError); - } else { - // Unregister application if it's already registered but advertise failed. - try { - mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); - mAdvertiserId = -2; - } catch (RemoteException e) { - Log.e(TAG, "remote exception when unregistering", e); - } - } - } - } - - public void stopAdvertising() { - synchronized (this) { - try { - mBluetoothGatt.stopMultiAdvertising(mAdvertiserId); - wait(LE_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException | RemoteException e) { - Log.e(TAG, "Failed to stop advertising", e); - } - // Advertise callback should have been removed from LeAdvertisers when - // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never - // invoked and wait timeout expires, remove callback here. - if (mLeAdvertisers.containsKey(mAdvertiseCallback)) { - mLeAdvertisers.remove(mAdvertiseCallback); - } - } - } - - /** - * Advertiser interface registered - app is ready to go - */ - @Override - public void onAdvertiserRegistered(int status, int advertiserId) { - Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId); - synchronized (this) { - if (status == BluetoothGatt.GATT_SUCCESS) { - try { - if (mAdvertiserId == -2) { - // Registration succeeds after timeout, unregister advertiser. - mBluetoothGatt.unregisterAdvertiser(advertiserId); - } else { - mAdvertiserId = advertiserId; - mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement, - mScanResponse, mSettings); - } - return; - } catch (RemoteException e) { - Log.e(TAG, "failed to start advertising", e); - } - } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { - registrationError = status; - } - // Registration failed. - mAdvertiserId = -2; - notifyAll(); - } - } - - @Override - public void onMultiAdvertiseCallback(int status, boolean isStart, - AdvertiseSettings settings) { - synchronized (this) { - if (isStart) { - if (status == AdvertiseCallback.ADVERTISE_SUCCESS) { - // Start success - mIsAdvertising = true; - postStartSuccess(mAdvertiseCallback, settings); - } else { - // Start failure. - postStartFailure(mAdvertiseCallback, status); - } - } else { - // unregister advertiser for stop. - try { - mBluetoothGatt.unregisterAdvertiser(mAdvertiserId); - mAdvertiserId = -2; - mIsAdvertising = false; - mLeAdvertisers.remove(mAdvertiseCallback); - } catch (RemoteException e) { - Log.e(TAG, "remote exception when unregistering", e); - } - } - notifyAll(); - } - - } - } - private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override -- GitLab From 1e2d5cb0e985d3f624fbaba697bb3a147c17582a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 16 Mar 2017 18:22:36 -0700 Subject: [PATCH 0707/1408] LE Maximum Advertising Data Length (1/4) Add ability to check maximum advertising data length. Bug: 30622771 Test: manual Change-Id: I281f7e9f294c40a47a67a22809cc753b6693f7c4 --- .../android/bluetooth/BluetoothAdapter.java | 19 +++++++++++++++++++ .../java/android/bluetooth/IBluetooth.aidl | 1 + 2 files changed, 20 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c689da6ca77..27640e78825 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1482,6 +1482,25 @@ public final class BluetoothAdapter { return false; } + /** + * Return the maximum LE advertising data length, + * if LE Extended Advertising feature is supported. + * + * @return the maximum LE advertising data length. + */ + public int getLeMaximumAdvertisingDataLength() { + if (!getLeAccess()) return 0; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getLeMaximumAdvertisingDataLength(); + } catch (RemoteException e) { + Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return 0; + } + /** * Return true if hardware has entries available for matching beacons * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 76ca554e598..b33781729b6 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -108,6 +108,7 @@ interface IBluetooth boolean isLeCodedPhySupported(); boolean isLeExtendedAdvertisingSupported(); boolean isLePeriodicAdvertisingSupported(); + int getLeMaximumAdvertisingDataLength(); BluetoothActivityEnergyInfo reportActivityInfo(); /** -- GitLab From 07680072f7038476901bd254543d1a938a282b57 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 17 Mar 2017 11:12:15 -0700 Subject: [PATCH 0708/1408] Bluetooth 5 move timeout parameter (1/2) Timeout is not a parameter, it is a property of enabling the advertising. Move it into more proper place. Test: manual Bug: 30622771 Change-Id: I09ebc6c770bc02938ea16a9738a7e03dc8006fde --- .../android/bluetooth/IBluetoothGatt.aidl | 4 +- .../android/bluetooth/le/AdvertisingSet.java | 4 +- .../le/AdvertisingSetParameters.java | 36 ++----------- .../bluetooth/le/BluetoothLeAdvertiser.java | 52 +++++++++++++++++-- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 29f29e7dba4..c281c7f7b0f 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -52,10 +52,10 @@ interface IBluetoothGatt { void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in IAdvertisingSetCallback callback); + in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); - void enableAdverisingSet(in int advertiserId, in boolean enable); + void enableAdverisingSet(in int advertiserId, in boolean enable, in int timeout); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 1524022b1f0..5524a2bdae1 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -63,9 +63,9 @@ public final class AdvertisingSet { * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * */ - public void enableAdvertising(boolean enable) { + public void enableAdvertising(boolean enable, int timeout) { try { - gatt.enableAdverisingSet(this.advertiserId, enable); + gatt.enableAdverisingSet(this.advertiserId, enable, timeout); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 453dd70a589..59fef8d1d1e 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -118,13 +118,11 @@ public final class AdvertisingSetParameters implements Parcelable { private final boolean connectable; private final int interval; private final int txPowerLevel; - private final int timeoutMillis; private AdvertisingSetParameters(boolean connectable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, - int interval, int txPowerLevel, - int timeoutMillis) { + int interval, int txPowerLevel) { this.connectable = connectable; this.isLegacy = isLegacy; this.isAnonymous = isAnonymous; @@ -133,7 +131,6 @@ public final class AdvertisingSetParameters implements Parcelable { this.secondaryPhy = secondaryPhy; this.interval = interval; this.txPowerLevel = txPowerLevel; - this.timeoutMillis = timeoutMillis; } private AdvertisingSetParameters(Parcel in) { @@ -145,7 +142,6 @@ public final class AdvertisingSetParameters implements Parcelable { secondaryPhy = in.readInt(); interval = in.readInt(); txPowerLevel = in.readInt(); - timeoutMillis = in.readInt(); } /** @@ -188,11 +184,6 @@ public final class AdvertisingSetParameters implements Parcelable { */ public int getTxPowerLevel() { return txPowerLevel; } - /** - * Returns the advertising time limit in milliseconds. - */ - public int getTimeout() { return timeoutMillis; } - @Override public String toString() { return "AdvertisingSetParameters [connectable=" + connectable @@ -202,8 +193,7 @@ public final class AdvertisingSetParameters implements Parcelable { + ", primaryPhy=" + primaryPhy + ", secondaryPhy=" + secondaryPhy + ", interval=" + interval - + ", txPowerLevel=" + txPowerLevel - + ", timeoutMillis=" + timeoutMillis + "]"; + + ", txPowerLevel=" + txPowerLevel + "]"; } @Override @@ -221,7 +211,6 @@ public final class AdvertisingSetParameters implements Parcelable { dest.writeInt(secondaryPhy); dest.writeInt(interval); dest.writeInt(txPowerLevel); - dest.writeInt(timeoutMillis); } public static final Parcelable.Creator CREATOR = @@ -250,7 +239,6 @@ public final class AdvertisingSetParameters implements Parcelable { private int secondaryPhy = PHY_LE_1M; private int interval = INTERVAL_LOW; private int txPowerLevel = TX_POWER_MEDIUM; - private int timeoutMillis = 0; /** * Set whether the advertisement type should be connectable or @@ -379,31 +367,13 @@ public final class AdvertisingSetParameters implements Parcelable { return this; } - /** - * Limit advertising to a given amount of time. - * @param timeoutMillis Advertising time limit. May not exceed 180000 - * milliseconds. A value of 0 will disable the time limit. - * @throws IllegalArgumentException If the provided timeout is over 180000 - * ms. - */ - public Builder setTimeout(int timeoutMillis) { - if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { - throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" + - LIMITED_ADVERTISING_MAX_MILLIS + - " milliseconds)"); - } - this.timeoutMillis = timeoutMillis; - return this; - } - /** * Build the {@link AdvertisingSetParameters} object. */ public AdvertisingSetParameters build() { return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous, includeTxPower, primaryPhy, - secondaryPhy, interval, txPowerLevel, - timeoutMillis); + secondaryPhy, interval, txPowerLevel); } } } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index c9f1d7a32ef..67fd1c86aa3 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -130,7 +130,6 @@ public final class BluetoothLeAdvertiser { AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder(); parameters.setLegacyMode(true); parameters.setConnectable(isConnectable); - parameters.setTimeout(settings.getTimeout()); if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) { parameters.setInterval(1600); // 1s } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) { @@ -152,7 +151,7 @@ public final class BluetoothLeAdvertiser { AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); mLegacyAdvertisers.put(callback, wrapped); startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, - wrapped); + settings.getTimeout(), wrapped); } } @@ -216,8 +215,8 @@ public final class BluetoothLeAdvertiser { AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, AdvertisingSetCallback callback) { - startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, callback, new Handler(Looper.getMainLooper())); + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, 0, callback, new Handler(Looper.getMainLooper())); } /** @@ -237,6 +236,49 @@ public final class BluetoothLeAdvertiser { PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, AdvertisingSetCallback callback, Handler handler) { + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, 0, callback, handler); + } + + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, int timeoutMillis, + AdvertisingSetCallback callback) { + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, timeoutMillis, callback, new Handler(Looper.getMainLooper())); + } + + /** + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. + * @param scanResponse Scan response associated with the advertisement data. + * @param periodicData Periodic advertising data. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + */ + public void startAdvertisingSet(AdvertisingSetParameters parameters, + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, int timeoutMillis, + AdvertisingSetCallback callback, Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { @@ -259,7 +301,7 @@ public final class BluetoothLeAdvertiser { try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, wrapped); + periodicData, timeoutMillis, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); throw new IllegalStateException("Failed to start advertising set"); -- GitLab From cde0e17b3909491e0676c5c914bc12333169bc7b Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 17 Mar 2017 15:33:27 -0700 Subject: [PATCH 0709/1408] Bluetooth 5 AdvertisingSet implementation (1/4) Test: manual Bug: 30622771 Change-Id: Ia89718c0c2ab2eaa71b158ecdcae989af907769f --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 4 ++-- .../java/android/bluetooth/le/AdvertisingSet.java | 6 +++--- .../android/bluetooth/le/AdvertisingSetCallback.java | 8 +++++--- .../android/bluetooth/le/BluetoothLeAdvertiser.java | 10 +++++----- .../android/bluetooth/le/IAdvertisingSetCallback.aidl | 4 ++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index c281c7f7b0f..652a1c6098f 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -55,13 +55,13 @@ interface IBluetoothGatt { in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); - void enableAdverisingSet(in int advertiserId, in boolean enable, in int timeout); + void enableAdvertisingSet(in int advertiserId, in boolean enable, in int timeout); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); - void periodicAdvertisingEnable(in int advertiserId, in boolean enable); + void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable); void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 5524a2bdae1..7355b0d4c76 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -65,7 +65,7 @@ public final class AdvertisingSet { */ public void enableAdvertising(boolean enable, int timeout) { try { - gatt.enableAdverisingSet(this.advertiserId, enable, timeout); + gatt.enableAdvertisingSet(this.advertiserId, enable, timeout); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -143,9 +143,9 @@ public final class AdvertisingSet { * Used to enable/disable periodic advertising. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. */ - public void periodicAdvertisingEnable(boolean enable) { + public void setPeriodicAdvertisingEnable(boolean enable) { try { - gatt.periodicAdvertisingEnable(this.advertiserId, enable); + gatt.setPeriodicAdvertisingEnable(this.advertiserId, enable); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index ceed8d9e3c6..8d2b82ab350 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -62,9 +62,10 @@ public abstract class AdvertisingSetCallback { * null, and status will be set to proper error code. * * @param advertisingSet The advertising set that was started or null if error. + * @param txPower tx power that will be used for this set. * @param status Status of the operation. */ - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {} + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {} /** * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} @@ -106,10 +107,11 @@ public abstract class AdvertisingSetCallback { * indicating result of the operation. * * @param advertisingSet The advertising set. + * @param txPower tx power that will be used for this set. * @param status Status of the operation. */ public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int status) {} + int txPower, int status) {} /** * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} @@ -133,7 +135,7 @@ public abstract class AdvertisingSetCallback { int status) {} /** - * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable} + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnable} * indicating result of the operation. * * @param advertisingSet The advertising set. diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 67fd1c86aa3..4457bdd7762 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -400,12 +400,12 @@ public final class BluetoothLeAdvertiser { IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { return new IAdvertisingSetCallback.Stub() { - public void onAdvertisingSetStarted(int advertiserId, int status) { + public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override public void run() { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { - callback.onAdvertisingSetStarted(null, status); + callback.onAdvertisingSetStarted(null, 0, status); mCallbackWrappers.remove(callback); return; } @@ -413,7 +413,7 @@ public final class BluetoothLeAdvertiser { AdvertisingSet advertisingSet = new AdvertisingSet(advertiserId, mBluetoothManager); mAdvertisingSets.put(advertiserId, advertisingSet); - callback.onAdvertisingSetStarted(advertisingSet, status); + callback.onAdvertisingSetStarted(advertisingSet, txPower, status); } }); } @@ -460,12 +460,12 @@ public final class BluetoothLeAdvertiser { }); } - public void onAdvertisingParametersUpdated(int advertiserId, int status) { + public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override public void run() { AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingParametersUpdated(advertisingSet, status); + callback.onAdvertisingParametersUpdated(advertisingSet, txPower, status); } }); } diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl index 4b0a111fa3d..e6a09f1d71d 100644 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -20,12 +20,12 @@ package android.bluetooth.le; * @hide */ oneway interface IAdvertisingSetCallback { - void onAdvertisingSetStarted(in int advertiserId, in int status); + void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); void onAdvertisingSetStopped(in int advertiserId); void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); void onAdvertisingDataSet(in int advertiserId, in int status); void onScanResponseDataSet(in int advertiserId, in int status); - void onAdvertisingParametersUpdated(in int advertiserId, in int status); + void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status); -- GitLab From 5b90da39fe37b8f7b0ece0860b6e6330b3be005e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 20 Mar 2017 17:02:20 -0700 Subject: [PATCH 0710/1408] Add missing txPower parameter Test: manual Bug: 30622771 Change-Id: Iaf3111d7545f9bb046b5a23b680347a75125ca7e --- .../bluetooth/le/BluetoothLeAdvertiser.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 4457bdd7762..0f35608f852 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -157,7 +157,9 @@ public final class BluetoothLeAdvertiser { AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) { return new AdvertisingSetCallback() { - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) { + @Override + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, + int status) { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { postStartFailure(callback, status); return; @@ -167,7 +169,9 @@ public final class BluetoothLeAdvertiser { } /* Legacy advertiser is disabled on timeout */ - public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { + @Override + public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, + int status) { if (enabled == true) { Log.e(TAG, "Legacy advertiser should be only disabled on timeout," + " but was enabled!"); @@ -400,6 +404,7 @@ public final class BluetoothLeAdvertiser { IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { return new IAdvertisingSetCallback.Stub() { + @Override public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override @@ -418,6 +423,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onAdvertisingSetStopped(int advertiserId) { handler.post(new Runnable() { @Override @@ -430,6 +436,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { handler.post(new Runnable() { @Override @@ -440,6 +447,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onAdvertisingDataSet(int advertiserId, int status) { handler.post(new Runnable() { @Override @@ -450,6 +458,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onScanResponseDataSet(int advertiserId, int status) { handler.post(new Runnable() { @Override @@ -460,6 +469,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { handler.post(new Runnable() { @Override @@ -470,6 +480,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { handler.post(new Runnable() { @Override @@ -480,6 +491,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onPeriodicAdvertisingDataSet(int advertiserId, int status) { handler.post(new Runnable() { @Override @@ -490,6 +502,7 @@ public final class BluetoothLeAdvertiser { }); } + @Override public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) { handler.post(new Runnable() { @Override -- GitLab From 1427501339490ba46fc72aeabee6bf76f3b8504d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 20 Mar 2017 15:57:46 -0700 Subject: [PATCH 0711/1408] Add setScannable to AdvertisingSetParameters (1/2) There must be a proper way to specify if advertisment is scannable, when updating the parameters. Test: manual Bug: 30622771 Change-Id: Ie520609d3fff07ccc2960015c19311d773842852 --- .../le/AdvertisingSetParameters.java | 26 +++++++++++++++++-- .../bluetooth/le/BluetoothLeAdvertiser.java | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 59fef8d1d1e..fe1f425c4fc 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -116,14 +116,16 @@ public final class AdvertisingSetParameters implements Parcelable { private final int primaryPhy; private final int secondaryPhy; private final boolean connectable; + private final boolean scannable; private final int interval; private final int txPowerLevel; - private AdvertisingSetParameters(boolean connectable, boolean isLegacy, + private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, int interval, int txPowerLevel) { this.connectable = connectable; + this.scannable = scannable; this.isLegacy = isLegacy; this.isAnonymous = isAnonymous; this.includeTxPower = includeTxPower; @@ -135,6 +137,7 @@ public final class AdvertisingSetParameters implements Parcelable { private AdvertisingSetParameters(Parcel in) { connectable = in.readInt() != 0 ? true : false; + scannable = in.readInt() != 0 ? true : false; isLegacy = in.readInt() != 0 ? true : false; isAnonymous = in.readInt() != 0 ? true : false; includeTxPower = in.readInt() != 0 ? true : false; @@ -149,6 +152,11 @@ public final class AdvertisingSetParameters implements Parcelable { */ public boolean isConnectable() { return connectable; } + /** + * Returns whether the advertisement will be scannable. + */ + public boolean isScannable() { return scannable; } + /** * Returns whether the legacy advertisement will be used. */ @@ -204,6 +212,7 @@ public final class AdvertisingSetParameters implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(connectable ? 1 : 0); + dest.writeInt(scannable ? 1 : 0); dest.writeInt(isLegacy ? 1 : 0); dest.writeInt(isAnonymous ? 1 : 0); dest.writeInt(includeTxPower ? 1 : 0); @@ -232,6 +241,7 @@ public final class AdvertisingSetParameters implements Parcelable { public static final class Builder { private boolean connectable = true; + private boolean scannable = true; private boolean isLegacy = false; private boolean isAnonymous = false; private boolean includeTxPower = false; @@ -253,6 +263,18 @@ public final class AdvertisingSetParameters implements Parcelable { return this; } + /** + * Set whether the advertisement type should be scannable + * Legacy advertisements can be both connectable and scannable. Other + * advertisements can be scannable only if not connectable. + * @param scannable Controls whether the advertisment type will be + * scannable (true) or non-scannable (false). + */ + public Builder setScannable(boolean scannable) { + this.scannable = scannable; + return this; + } + /** * When set to true, advertising set will advertise 4.x Spec compliant * advertisements. @@ -371,7 +393,7 @@ public final class AdvertisingSetParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public AdvertisingSetParameters build() { - return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous, + return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, includeTxPower, primaryPhy, secondaryPhy, interval, txPowerLevel); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 0f35608f852..ae012d9391e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -130,6 +130,7 @@ public final class BluetoothLeAdvertiser { AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder(); parameters.setLegacyMode(true); parameters.setConnectable(isConnectable); + parameters.setScannable(true); // legacy advertisements we support are always scannable if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) { parameters.setInterval(1600); // 1s } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) { -- GitLab From 90dd95ea51affa3517bf52a71b097fefb5867ae5 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Thu, 16 Mar 2017 18:10:59 -0700 Subject: [PATCH 0712/1408] Bluetooth: log unexpected crashes and restarts Add some reasons to the log when: - the Bluetooth stack crashes underneath us - user switches - we automatically restart List every instance of the stack crashing underneath us in the dumpsys. Clean up some unused constants. Test: notice a Bluetooth crash, adb bugreport Change-Id: I79195f0a574de1bf48a976d31c64084d3ce2e14a --- .../bluetooth/BluetoothManagerService.java | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5e9cf748182..c7c133daa96 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -76,15 +76,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; - private static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED"; - private static final String EXTRA_ACTION="action"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + + private static final int ACTIVE_LOG_MAX_SIZE = 20; + private static final int CRASH_LOG_MAX_SIZE = 100; private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_RESTARTED = "automatic restart"; + private static final String REASON_START_CRASH = "turn-on crash"; private static final String REASON_SYSTEM_BOOT = "system boot"; + private static final String REASON_UNEXPECTED = "unexpected crash"; + private static final String REASON_USER_SWITCH = "user switch"; + private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind - private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save //Maximum msec to wait for service restart private static final int SERVICE_RESTART_TIME_MS = 200; //Maximum msec to wait for restart due to error @@ -149,6 +155,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mQuietEnable = false; private boolean mEnable; + private CharSequence timeToLog(long timestamp) { + return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp); + } + /** * Used for tracking apps that enabled / disabled Bluetooth. */ @@ -168,13 +178,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String toString() { - return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) + - (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; + return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + + mPackageName; } } private LinkedList mActiveLogs; + private LinkedList mCrashTimestamps; + private int mCrashes; // configuration from external IBinder call which is used to // synchronize with broadcast receiver. @@ -308,6 +320,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { com.android.internal.R.bool.config_permissionReviewRequired); mActiveLogs = new LinkedList(); + mCrashTimestamps = new LinkedList(); + mCrashes = 0; mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -1565,6 +1579,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().unlock(); } + // log the unexpected crash + addCrashLog(); + addActiveLog(REASON_UNEXPECTED, false); if (mEnable) { mEnable = false; // Send a Bluetooth Restart message @@ -1600,6 +1617,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { it doesnt change when IBluetooth service restarts */ mEnable = true; + addActiveLog(REASON_RESTARTED, true); handleEnable(mQuietEnable); break; } @@ -1654,6 +1672,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { unbindAllBluetoothProfileServices(); // disable + addActiveLog(REASON_USER_SWITCH, false); handleDisable(); // Pbap service need receive STATE_TURNING_OFF intent to close bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, @@ -1691,6 +1710,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; // enable + addActiveLog(REASON_USER_SWITCH, true); handleEnable(mQuietEnable); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); @@ -1945,13 +1965,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void addActiveLog(String packageName, boolean enable) { synchronized (mActiveLogs) { - if (mActiveLogs.size() > 10) { + if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) { mActiveLogs.remove(); } mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); } } + private void addCrashLog() { + synchronized (mCrashTimestamps) { + if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) mCrashTimestamps.removeFirst(); + mCrashTimestamps.add(System.currentTimeMillis()); + mCrashes++; + } + } + private void recoverBluetoothServiceFromError(boolean clearBle) { Slog.e(TAG,"recoverBluetoothServiceFromError"); try { @@ -1969,6 +1997,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { SystemClock.sleep(500); // disable + addActiveLog(REASON_START_CRASH, false); handleDisable(); waitForOnOff(false, true); @@ -2070,6 +2099,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + writer.println("Bluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); + if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); + for (Long time : mCrashTimestamps) { + writer.println(" " + timeToLog(time.longValue())); + } + String bleAppString = "No BLE Apps registered."; if (mBleApps.size() == 1) { bleAppString = "1 BLE App registered:"; -- GitLab From cd8835eecb6b07c9c97d3dc2b0945263e9e985b3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 11:22:18 -0700 Subject: [PATCH 0713/1408] BluetoothGattCallbackExt removal New methods should be added to BluetoothGattCallback, instead of creating Ext class. Test: manual Bug: 30622771 Change-Id: I2567df5baace6bd2d2f30c36d2f62056408ca5d0 --- .../android/bluetooth/BluetoothDevice.java | 39 +--- .../java/android/bluetooth/BluetoothGatt.java | 42 ++-- .../bluetooth/BluetoothGattCallback.java | 157 ++++++++++++++- .../bluetooth/BluetoothGattCallbackExt.java | 182 ----------------- .../bluetooth/BluetoothGattServer.java | 18 +- .../BluetoothGattServerCallback.java | 159 ++++++++++++++- .../BluetoothGattServerCallbackExt.java | 187 ------------------ 7 files changed, 334 insertions(+), 450 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothGattCallbackExt.java delete mode 100644 framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 31fc294e318..cb6fa052dda 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1654,43 +1654,6 @@ public final class BluetoothDevice implements Parcelable { return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); } - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @throws IllegalArgumentException if callback is null - */ - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallbackExt callback) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} - * @throws IllegalArgumentException if callback is null - */ - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallbackExt callback, int transport) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); - } - /** * Connect to GATT Server hosted by this device. Caller acts as GATT client. * The callback is used to deliver results to Caller, such as connection status as well @@ -1711,7 +1674,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallbackExt callback, int transport, int phy) { + BluetoothGattCallback callback, int transport, int phy) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 0cb69ae519f..56bf1e8604c 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -31,7 +31,7 @@ import java.util.UUID; *

        This class provides Bluetooth GATT functionality to enable communication * with Bluetooth Smart or Smart Ready devices. * - *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt} + *

        To connect to a remote peripheral device, create a {@link BluetoothGattCallback} * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. * GATT capable devices can be discovered using the Bluetooth device discovery or BLE * scan process. @@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean VDBG = false; private IBluetoothGatt mService; - private BluetoothGattCallbackExt mCallback; + private BluetoothGattCallback mCallback; private int mClientIf; private BluetoothDevice mDevice; private boolean mAutoConnect; @@ -133,9 +133,9 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_MITM = 2; /** - * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation. + * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ - private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt = + private final IBluetoothGattCallbackExt mBluetoothGattCallback = new IBluetoothGattCallbackExt.Stub() { /** * Application interface registered - app is ready to go @@ -618,7 +618,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Register an application callback to start using GATT. * - *

        This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered} + *

        This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} * is used to notify success or failure if the function returns true. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -627,7 +627,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return If true, the callback will be called to notify success or failure, * false on immediate error */ - private boolean registerApp(BluetoothGattCallbackExt callback) { + private boolean registerApp(BluetoothGattCallback callback) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -636,7 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -666,7 +666,7 @@ public final class BluetoothGatt implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be + * {@link BluetoothGattCallback#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect parameter determines whether to actively connect to @@ -684,7 +684,7 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) { + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); synchronized(mStateLock) { if (mConnState != CONN_STATE_IDLE) { @@ -749,7 +749,7 @@ public final class BluetoothGatt implements BluetoothProfile { * recommendation, wether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        - * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even * if no PHY change happens. It is also triggered when remote device updates the PHY. * * @param txPhy preferred transmitter PHY. Bitwise OR of any of @@ -773,7 +773,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Read the current transmitter PHY and receiver PHY of the connection. The values are returned - * in {@link BluetoothGattCallbackExt#onPhyRead} + * in {@link BluetoothGattCallback#onPhyRead} */ public void readPhy() { try { @@ -797,7 +797,7 @@ public final class BluetoothGatt implements BluetoothProfile { * characteristics and descriptors. * *

        This is an asynchronous operation. Once service discovery is completed, - * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is + * the {@link BluetoothGattCallback#onServicesDiscovered} callback is * triggered. If the discovery was successful, the remote services can be * retrieved using the {@link #getServices} function. * @@ -876,7 +876,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the requested characteristic from the associated remote device. * *

        This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead} + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} * callback. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -918,7 +918,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Writes a given characteristic and its values to the associated remote device. * *

        Once the write operation has been completed, the - * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked, + * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, * reporting the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -962,7 +962,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the value for a given descriptor from the associated remote device. * *

        Once the read operation has been completed, the - * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is + * {@link BluetoothGattCallback#onDescriptorRead} callback is * triggered, signaling the result of the operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1003,7 +1003,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Write the value of a given descriptor to the associated remote device. * - *

        A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is + *

        A {@link BluetoothGattCallback#onDescriptorWrite} callback is * triggered to report the result of the write operation. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1047,7 +1047,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Once a reliable write transaction has been initiated, all calls * to {@link #writeCharacteristic} are sent to the remote device for * verification and queued up for atomic execution. The application will - * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback + * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback * in response to every {@link #writeCharacteristic} call and is responsible * for verifying if the value has been transmitted accurately. * @@ -1081,7 +1081,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        This function will commit all queued up characteristic write * operations for a given remote device. * - *

        A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is + *

        A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is * invoked to indicate whether the transaction has been executed correctly. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1138,7 +1138,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Enable or disable notifications/indications for a given characteristic. * *

        Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be + * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be * triggered if the remote device indicates that the given characteristic * has changed. * @@ -1193,7 +1193,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Read the RSSI for a connected remote device. * - *

        The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be + *

        The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be * invoked when the RSSI value has been read. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -1221,7 +1221,7 @@ public final class BluetoothGatt implements BluetoothProfile { * the data sent is truncated to the MTU size. This function may be used * to request a larger MTU size to be able to send more data at once. * - *

        A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate + *

        A {@link BluetoothGattCallback#onMtuChanged} callback will indicate * whether this operation was successful. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 4da106df610..be69df928e1 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 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. @@ -18,22 +18,165 @@ package android.bluetooth; /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. - * @deprecated use {@link BluetoothGattCallbackExt} */ -public abstract class BluetoothGattCallback extends BluetoothGattCallbackExt { +public abstract class BluetoothGattCallback{ /** - * @hide + * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of + * remote device changing the PHY. + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation */ - @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } /** - * @hide + * Callback triggered as result of {@link BluetoothGatt#readPhy} + * + * @param gatt GATT client + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status status of the operation */ - @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } + /** + * Callback indicating when GATT client has connected/disconnected to/from a remote + * GATT server. + * + * @param gatt GATT client + * @param status Status of the connect or disconnect operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothGatt gatt, int status, + int newState) { + } + + /** + * Callback invoked when the list of remote services, characteristics and descriptors + * for the remote device have been updated, ie new services have been discovered. + * + * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device + * has been explored successfully. + */ + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the result of a characteristic read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} + * @param characteristic Characteristic that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully. + */ + public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, + int status) { + } + + /** + * Callback indicating the result of a characteristic write operation. + * + *

        If this callback is invoked while a reliable write transaction is + * in progress, the value of the characteristic represents the value + * reported by the remote device. An application should compare this + * value to the desired value to be written. If the values don't match, + * the application must abort the reliable write transaction. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} + * @param characteristic Characteristic that was written to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + } + + /** + * Callback triggered as a result of a remote characteristic notification. + * + * @param gatt GATT client the characteristic is associated with + * @param characteristic Characteristic that has been updated as a result + * of a remote notification event. + */ + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + } + + /** + * Callback reporting the result of a descriptor read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param descriptor Descriptor that was read from the associated + * remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation + * was completed successfully + */ + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback indicating the result of a descriptor write operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} + * @param descriptor Descriptor that was writte to the associated + * remote device. + * @param status The result of the write operation + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + */ + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, + int status) { + } + + /** + * Callback invoked when a reliable write transaction has been completed. + * + * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write + * transaction was executed successfully + */ + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + } + + /** + * Callback reporting the RSSI for a remote device connection. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#readRemoteRssi} function. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} + * @param rssi The RSSI value for the remote device + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully + */ + public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#requestMtu} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + */ + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + } } diff --git a/framework/java/android/bluetooth/BluetoothGattCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattCallbackExt.java deleted file mode 100644 index 63774c8fbb6..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattCallbackExt.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2017 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; - -/** - * This abstract class is used to implement {@link BluetoothGatt} callbacks. - */ -public abstract class BluetoothGattCallbackExt { - - /** - * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of - * remote device changing the PHY. - * - * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation - */ - public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - } - - /** - * Callback triggered as result of {@link BluetoothGatt#readPhy} - * - * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation - */ - public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - } - - /** - * Callback indicating when GATT client has connected/disconnected to/from a remote - * GATT server. - * - * @param gatt GATT client - * @param status Status of the connect or disconnect operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} - */ - public void onConnectionStateChange(BluetoothGatt gatt, int status, - int newState) { - } - - /** - * Callback invoked when the list of remote services, characteristics and descriptors - * for the remote device have been updated, ie new services have been discovered. - * - * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device - * has been explored successfully. - */ - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the result of a characteristic read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} - * @param characteristic Characteristic that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully. - */ - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, - int status) { - } - - /** - * Callback indicating the result of a characteristic write operation. - * - *

        If this callback is invoked while a reliable write transaction is - * in progress, the value of the characteristic represents the value - * reported by the remote device. An application should compare this - * value to the desired value to be written. If the values don't match, - * the application must abort the reliable write transaction. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} - * @param characteristic Characteristic that was written to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - } - - /** - * Callback triggered as a result of a remote characteristic notification. - * - * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result - * of a remote notification event. - */ - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - } - - /** - * Callback reporting the result of a descriptor read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully - */ - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback indicating the result of a descriptor write operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} - * @param descriptor Descriptor that was writte to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - */ - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback invoked when a reliable write transaction has been completed. - * - * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write - * transaction was executed successfully - */ - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the RSSI for a remote device connection. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#readRemoteRssi} function. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} - * @param rssi The RSSI value for the remote device - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully - */ - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#requestMtu} function, or in response to a connection - * event. - * - * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} - * @param mtu The new MTU size - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully - */ - public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { - } -} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 9ee739f04bf..1bd7bd4dc6f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -46,7 +46,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; - private BluetoothGattServerCallbackExt mCallback; + private BluetoothGattServerCallback mCallback; private Object mServerIfLock = new Object(); private int mServerIf; @@ -396,7 +396,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return true, the callback will be called to notify success or failure, * false on immediate error */ - /*package*/ boolean registerCallback(BluetoothGattServerCallbackExt callback) { + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -472,7 +472,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        The connection may not be established right away, but will be * completed when the remote device is available. A - * {@link BluetoothGattServerCallbackExt#onConnectionStateChange} callback will be + * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * *

        The autoConnect paramter determines whether to actively connect to @@ -528,7 +528,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * recommendation, wether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        - * {@link BluetoothGattServerCallbackExt#onPhyUpdate} will be triggered as a result of this call, even + * {@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even * if no PHY change happens. It is also triggered when remote device updates the PHY. * * @param device The remote device to send this response to @@ -553,7 +553,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Read the current transmitter PHY and receiver PHY of the connection. The values are returned - * in {@link BluetoothGattServerCallbackExt#onPhyRead} + * in {@link BluetoothGattServerCallback#onPhyRead} * * @param device The remote device to send this response to */ @@ -572,10 +572,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * is received by one of these callback methods: * *

          - *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicReadRequest} - *
        • {@link BluetoothGattServerCallbackExt#onCharacteristicWriteRequest} - *
        • {@link BluetoothGattServerCallbackExt#onDescriptorReadRequest} - *
        • {@link BluetoothGattServerCallbackExt#onDescriptorWriteRequest} + *
        • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} + *
        • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} + *
        • {@link BluetoothGattServerCallback#onDescriptorReadRequest} + *
        • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 75ceb52c2d5..0a890721de6 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -20,21 +20,168 @@ import android.bluetooth.BluetoothDevice; /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. - * @deprecated please use {@link BluetoothGattServerCallbackExt} */ -public abstract class BluetoothGattServerCallback extends BluetoothGattServerCallbackExt { +public abstract class BluetoothGattServerCallback { /** - * @hide + * Callback indicating when a remote device has been connected or disconnected. + * + * @param device Remote device that has been connected or disconnected. + * @param status Status of the connect or disconnect operation. + * @param newState Returns the new connection state. Can be one of + * {@link BluetoothProfile#STATE_DISCONNECTED} or + * {@link BluetoothProfile#STATE_CONNECTED} + */ + public void onConnectionStateChange(BluetoothDevice device, int status, + int newState) { + } + + /** + * Indicates whether a local service has been added successfully. + * + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service + * was added successfully. + * @param service The service that has been added + */ + public void onServiceAdded(int status, BluetoothGattService service) { + } + + /** + * A remote client has requested to read a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param characteristic Characteristic to be read + */ + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattCharacteristic characteristic) { + } + + /** + * A remote client has requested to write to a local characteristic. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param characteristic Characteristic to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the characteristic + */ + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * A remote client has requested to read a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the read operation + * @param requestId The Id of the request + * @param offset Offset into the value of the characteristic + * @param descriptor Descriptor to be read + */ + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, + int offset, BluetoothGattDescriptor descriptor) { + } + + /** + * A remote client has requested to write to a local descriptor. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operation + * @param requestId The Id of the request + * @param descriptor Descriptor to be written to. + * @param preparedWrite true, if this write operation should be queued for + * later execution. + * @param responseNeeded true, if the remote device requires a response + * @param offset The offset given for the value + * @param value The value the client wants to assign to the descriptor + */ + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { + } + + /** + * Execute all pending write operations for this device. + * + *

        An application must call {@link BluetoothGattServer#sendResponse} + * to complete the request. + * + * @param device The remote device that has requested the write operations + * @param requestId The Id of the request + * @param execute Whether the pending writes should be executed (true) or + * cancelled (false) + */ + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { + } + + /** + * Callback invoked when a notification or indication has been sent to + * a remote device. + * + *

        When multiple notifications are to be sent, an application must + * wait for this callback to be received before sending additional + * notifications. + * + * @param device The remote device the notification has been sent to + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful + */ + public void onNotificationSent(BluetoothDevice device, int status) { + } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + *

        This callback will be invoked if a remote client has requested to change + * the MTU for a given connection. + * + * @param device The remote device that requested the MTU change + * @param mtu The new MTU size + */ + public void onMtuChanged(BluetoothDevice device, int mtu) { + } + + /** + * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result + * of remote device changing the PHY. + * + * @param device The remote device + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation */ - @Override public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } /** - * @hide + * Callback triggered as result of {@link BluetoothGattServer#readPhy} + * + * @param device The remote device that requested the PHY read + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status status of the operation */ - @Override public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java b/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java deleted file mode 100644 index 455cce04c58..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattServerCallbackExt.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * This abstract class is used to implement {@link BluetoothGattServer} callbacks. - */ -public abstract class BluetoothGattServerCallbackExt { - - /** - * Callback indicating when a remote device has been connected or disconnected. - * - * @param device Remote device that has been connected or disconnected. - * @param status Status of the connect or disconnect operation. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} - */ - public void onConnectionStateChange(BluetoothDevice device, int status, - int newState) { - } - - /** - * Indicates whether a local service has been added successfully. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service - * was added successfully. - * @param service The service that has been added - */ - public void onServiceAdded(int status, BluetoothGattService service) { - } - - /** - * A remote client has requested to read a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param characteristic Characteristic to be read - */ - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) { - } - - /** - * A remote client has requested to write to a local characteristic. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param characteristic Characteristic to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the characteristic - */ - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * A remote client has requested to read a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param descriptor Descriptor to be read - */ - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { - } - - /** - * A remote client has requested to write to a local descriptor. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param descriptor Descriptor to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the descriptor - */ - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * Execute all pending write operations for this device. - * - *

        An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operations - * @param requestId The Id of the request - * @param execute Whether the pending writes should be executed (true) or - * cancelled (false) - */ - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { - } - - /** - * Callback invoked when a notification or indication has been sent to - * a remote device. - * - *

        When multiple notifications are to be sent, an application must - * wait for this callback to be received before sending additional - * notifications. - * - * @param device The remote device the notification has been sent to - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful - */ - public void onNotificationSent(BluetoothDevice device, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - *

        This callback will be invoked if a remote client has requested to change - * the MTU for a given connection. - * - * @param device The remote device that requested the MTU change - * @param mtu The new MTU size - */ - public void onMtuChanged(BluetoothDevice device, int mtu) { - } - - /** - * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result - * of remote device changing the PHY. - * - * @param device The remote device - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation - */ - public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { - } - - /** - * Callback triggered as result of {@link BluetoothGattServer#readPhy} - * - * @param device The remote device that requested the PHY read - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation - */ - public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { - } -} -- GitLab From a24e8db434d06e563d0aff77c77e5c7c02848d58 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 22 Feb 2017 12:23:15 -0800 Subject: [PATCH 0714/1408] Bluetooth: Use content observer for airplane mode We are making this switch as the airplane mode switch intent is going away. Fix: 35256299 Test: Toggle Airplane Mode Change-Id: I3b1e5bbdf689b0db98cfbb0ab377198d34f0ba05 --- .../bluetooth/BluetoothManagerService.java | 135 +++++++++--------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index afb4c07563b..421e4a1c8d0 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -82,6 +82,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_SYSTEM_BOOT = "system boot"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save //Maximum msec to wait for service restart @@ -195,19 +197,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final boolean mPermissionReviewRequired; - private void registerForAirplaneMode(IntentFilter filter) { - final ContentResolver resolver = mContext.getContentResolver(); - final String airplaneModeRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_RADIOS); - final String toggleableRadios = Settings.Global.getString(resolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); - boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true : - airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH); - if (mIsAirplaneSensitive) { - filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - } - } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { @@ -240,6 +229,62 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { + @Override + public void onChange(boolean unused) { + synchronized(this) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + } + + int st = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + st = mBluetooth.getState(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return; + } finally { + mBluetoothLock.readLock().unlock(); + } + + Slog.d(TAG, "Airplane Mode change - current state: " + + BluetoothAdapter.nameForState(st)); + + if (isAirplaneModeOn()) { + // Clear registered LE apps to force shut-off + clearBleApps(); + + // If state is BLE_ON make sure we trigger disableBLE + if (st == BluetoothAdapter.STATE_BLE_ON) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + mEnable = false; + mEnableExternal = false; + } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + } else if (st == BluetoothAdapter.STATE_ON){ + sendDisableMsg(REASON_AIRPLANE_MODE); + } + } else if (mEnableExternal) { + sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE); + } + } + } + }; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -250,58 +295,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } - } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { - synchronized(mReceiver) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - } - - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - Slog.d(TAG, "Airplane Mode change - current state: " + - BluetoothAdapter.nameForState(st)); - - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); - if (st == BluetoothAdapter.STATE_BLE_ON) { - //if state is BLE_ON make sure you trigger disableBLE part - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.onBrEdrDown(); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } else if (st == BluetoothAdapter.STATE_ON){ - // disable without persisting the setting - Slog.d(TAG, "Calling disable"); - sendDisableMsg("airplane mode"); - } - } else if (mEnableExternal) { - // enable without persisting the setting - Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal, "airplane mode"); - } - } } } }; @@ -334,7 +327,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - registerForAirplaneMode(filter); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); @@ -343,6 +335,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } + String airplaneModeRadios = Settings.Global.getString(mContentResolver, + Settings.Global.AIRPLANE_MODE_RADIOS); + if (airplaneModeRadios == null || + airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) { + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), + true, mAirplaneModeObserver); + } + int systemUiUid = -1; try { systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", @@ -978,7 +979,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal, "system boot"); + sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); -- GitLab From 85055bdaf6d4036b502ab26c710d22e9481aec83 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 15:40:21 -0700 Subject: [PATCH 0715/1408] Bluetooth 5 Enable->Enabled (1/2) Test: manual Bug: 30622771 Change-Id: I37b198927076a0267de67e1bd94eaea9e88e53bb --- .../java/android/bluetooth/le/AdvertisingSetCallback.java | 2 +- .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 4 ++-- .../java/android/bluetooth/le/IAdvertisingSetCallback.aidl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index 8d2b82ab350..fe3b1cdd63a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -141,6 +141,6 @@ public abstract class AdvertisingSetCallback { * @param advertisingSet The advertising set. * @param status Status of the operation. */ - public void onPeriodicAdvertisingEnable(AdvertisingSet advertisingSet, boolean enable, + public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ae012d9391e..242ee77ce0e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -504,12 +504,12 @@ public final class BluetoothLeAdvertiser { } @Override - public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) { + public void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) { handler.post(new Runnable() { @Override public void run() { AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status); + callback.onPeriodicAdvertisingEnabled(advertisingSet, enable, status); } }); } diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl index e6a09f1d71d..2c9f4baad52 100644 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -28,5 +28,5 @@ oneway interface IAdvertisingSetCallback { void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); - void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status); + void onPeriodicAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); } -- GitLab From b651de2c347368d04dd313f61f719c2f5ae1b92e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 23 Mar 2017 19:05:55 -0700 Subject: [PATCH 0716/1408] Expose connection update callback (4/4) Test: manual Bug: 30622771 Change-Id: I56a3c42814249abc33e95e84ce092c2d8df65434 --- .../java/android/bluetooth/BluetoothGatt.java | 32 ++++++++++++++ .../bluetooth/BluetoothGattCallback.java | 18 ++++++++ .../bluetooth/BluetoothGattServer.java | 43 +++++++++++++++---- .../BluetoothGattServerCallback.java | 19 ++++++++ .../bluetooth/IBluetoothGattCallbackExt.aidl | 2 + .../IBluetoothGattServerCallbackExt.aidl | 2 + 6 files changed, 107 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 56bf1e8604c..a314aaf96ac 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -141,6 +141,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Application interface registered - app is ready to go * @hide */ + @Override public void onClientRegistered(int status, int clientIf) { if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); @@ -210,6 +211,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Client connection state changed * @hide */ + @Override public void onClientConnectionState(int status, int clientIf, boolean connected, String address) { if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status @@ -245,6 +247,7 @@ public final class BluetoothGatt implements BluetoothProfile { * we are done at this point. * @hide */ + @Override public void onSearchComplete(String address, List services, int status) { if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); @@ -288,6 +291,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ + @Override public void onCharacteristicRead(String address, int status, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + " handle=" + handle + " Status=" + status); @@ -336,6 +340,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Let the app know how we did... * @hide */ + @Override public void onCharacteristicWrite(String address, int status, int handle) { if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " handle=" + handle + " Status=" + status); @@ -380,6 +385,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Updates the internal value. * @hide */ + @Override public void onNotify(String address, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); @@ -403,6 +409,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor has been read. * @hide */ + @Override public void onDescriptorRead(String address, int status, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle); @@ -446,6 +453,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Descriptor write operation complete. * @hide */ + @Override public void onDescriptorWrite(String address, int status, int handle) { if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); @@ -488,6 +496,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Prepared write transaction completed (or aborted) * @hide */ + @Override public void onExecuteWrite(String address, int status) { if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status); @@ -510,6 +519,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Remote device RSSI has been read * @hide */ + @Override public void onReadRemoteRssi(String address, int rssi, int status) { if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + " rssi=" + rssi + " status=" + status); @@ -527,6 +537,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Callback invoked when the MTU for a given connection changes * @hide */ + @Override public void onConfigureMTU(String address, int mtu, int status) { if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address + " mtu=" + mtu + " status=" + status); @@ -539,6 +550,27 @@ public final class BluetoothGatt implements BluetoothProfile { Log.w(TAG, "Unhandled exception in callback", ex); } } + + /** + * Callback invoked when the given connection is updated + * @hide + */ + @Override + public void onConnectionUpdated(String address, int interval, int latency, + int timeout, int status) { + if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } + try { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index be69df928e1..11a15c66385 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -179,4 +179,22 @@ public abstract class BluetoothGattCallback{ */ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { } + + /** + * Callback indicating the connection parameters were updated. + * + * @param gatt GATT client involved + * @param interval Connection interval used on this connection, 1.25ms unit. Valid + * range is from 6 (7.5ms) to 3200 (4000ms). + * @param latency Slave latency for the connection in number of connection events. Valid + * range is from 0 to 499 + * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is + * from 10 (0.1s) to 3200 (32s) + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated + * successfully + * @hide + */ + public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout, + int status) { + } } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 1bd7bd4dc6f..c991e2f71bb 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -65,6 +65,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Application interface registered - app is ready to go * @hide */ + @Override public void onServerRegistered(int status, int serverIf) { if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status + " serverIf=" + serverIf); @@ -79,19 +80,11 @@ public final class BluetoothGattServer implements BluetoothProfile { } } - /** - * Callback reporting an LE scan result. - * @hide - */ - public void onScanResult(String address, int rssi, byte[] advData) { - if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - // no op - } - /** * Server connection state changed * @hide */ + @Override public void onServerConnectionState(int status, int serverIf, boolean connected, String address) { if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status @@ -109,6 +102,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Service has been added * @hide */ + @Override public void onServiceAdded(int status, BluetoothGattService service) { if (DBG) Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId() + " uuid=" + service.getUuid() + " status=" + status); @@ -149,6 +143,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Remote client characteristic read request. * @hide */ + @Override public void onCharacteristicReadRequest(String address, int transId, int offset, boolean isLong, int handle) { if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); @@ -171,6 +166,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Remote client descriptor read request. * @hide */ + @Override public void onDescriptorReadRequest(String address, int transId, int offset, boolean isLong, int handle) { if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); @@ -193,6 +189,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Remote client characteristic write request. * @hide */ + @Override public void onCharacteristicWriteRequest(String address, int transId, int offset, int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { @@ -218,6 +215,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Remote client descriptor write request. * @hide */ + @Override public void onDescriptorWriteRequest(String address, int transId, int offset, int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); @@ -241,6 +239,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Execute pending writes. * @hide */ + @Override public void onExecuteWrite(String address, int transId, boolean execWrite) { if (DBG) Log.d(TAG, "onExecuteWrite() - " @@ -261,6 +260,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * A notification/indication has been sent. * @hide */ + @Override public void onNotificationSent(String address, int status) { if (VDBG) Log.d(TAG, "onNotificationSent() - " + "device=" + address + ", status=" + status); @@ -279,6 +279,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * The MTU for a connection has changed * @hide */ + @Override public void onMtuChanged(String address, int mtu) { if (DBG) Log.d(TAG, "onMtuChanged() - " + "device=" + address + ", mtu=" + mtu); @@ -297,6 +298,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * The PHY for a connection was updated * @hide */ + @Override public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + ", rxPHy=" + rxPhy); @@ -315,6 +317,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * The PHY for a connection was read * @hide */ + @Override public void onPhyRead(String address, int txPhy, int rxPhy, int status) { if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + ", rxPHy=" + rxPhy); @@ -328,6 +331,28 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.w(TAG, "Unhandled exception: " + ex); } } + + /** + * Callback invoked when the given connection is updated + * @hide + */ + @Override + public void onConnectionUpdated(String address, int interval, int latency, + int timeout, int status) { + if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onConnectionUpdated(device, interval, latency, + timeout, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + }; /** diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 0a890721de6..3b8f962bf73 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -184,4 +184,23 @@ public abstract class BluetoothGattServerCallback { */ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } + + /** + * Callback indicating the connection parameters were updated. + * + * @param device The remote device involved + * @param interval Connection interval used on this connection, 1.25ms unit. Valid + * range is from 6 (7.5ms) to 3200 (4000ms). + * @param latency Slave latency for the connection in number of connection events. Valid + * range is from 0 to 499 + * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is + * from 10 (0.1s) to 3200 (32s) + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated + * successfully + * @hide + */ + public void onConnectionUpdated(BluetoothDevice gatt, int interval, int latency, int timeout, + int status) { + } + } diff --git a/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl index 736f4b2b048..ed69e54671c 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl @@ -37,4 +37,6 @@ oneway interface IBluetoothGattCallbackExt { void onNotify(in String address, in int handle, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); void onConfigureMTU(in String address, in int mtu, in int status); + void onConnectionUpdated(in String address, in int interval, in int latency, + in int timeout, in int status); } diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl index 091ffb3fe98..267e8824439 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl @@ -42,4 +42,6 @@ oneway interface IBluetoothGattServerCallbackExt { void onMtuChanged(in String address, in int mtu); void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); + void onConnectionUpdated(in String address, in int interval, in int latency, + in int timeout, in int status); } -- GitLab From ddf66d3a26933a1d3152ab5d44ca8fb1aa1fc75c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 27 Mar 2017 12:14:40 -0700 Subject: [PATCH 0717/1408] Bluetooth GATT callback naming fix "Ext" -> "" (1/2) Gatt callback don't need the "Ext" postfix, as it was removed from new API. Test: manual Bug: 30622771 Change-Id: I54596ffdcb818343cc313123266cfa16291c6236 --- framework/java/android/bluetooth/BluetoothGatt.java | 4 ++-- framework/java/android/bluetooth/BluetoothGattServer.java | 4 ++-- framework/java/android/bluetooth/IBluetoothGatt.aidl | 8 ++++---- ...thGattCallbackExt.aidl => IBluetoothGattCallback.aidl} | 2 +- ...CallbackExt.aidl => IBluetoothGattServerCallback.aidl} | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) rename framework/java/android/bluetooth/{IBluetoothGattCallbackExt.aidl => IBluetoothGattCallback.aidl} (97%) rename framework/java/android/bluetooth/{IBluetoothGattServerCallbackExt.aidl => IBluetoothGattServerCallback.aidl} (97%) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a314aaf96ac..99ca11ece50 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -135,8 +135,8 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ - private final IBluetoothGattCallbackExt mBluetoothGattCallback = - new IBluetoothGattCallbackExt.Stub() { + private final IBluetoothGattCallback mBluetoothGattCallback = + new IBluetoothGattCallback.Stub() { /** * Application interface registered - app is ready to go * @hide diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index c991e2f71bb..b35a5933593 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -59,8 +59,8 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Bluetooth GATT interface callbacks */ - private final IBluetoothGattServerCallbackExt mBluetoothGattServerCallback = - new IBluetoothGattServerCallbackExt.Stub() { + private final IBluetoothGattServerCallback mBluetoothGattServerCallback = + new IBluetoothGattServerCallback.Stub() { /** * Application interface registered - app is ready to go * @hide diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 652a1c6098f..0825ee88a45 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -29,8 +29,8 @@ import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; import android.os.WorkSource; -import android.bluetooth.IBluetoothGattCallbackExt; -import android.bluetooth.IBluetoothGattServerCallbackExt; +import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothGattServerCallback; import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; @@ -66,7 +66,7 @@ interface IBluetoothGatt { void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); void unregisterSync(in IPeriodicAdvertisingCallback callback); - void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback); + void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); @@ -88,7 +88,7 @@ interface IBluetoothGatt { void configureMTU(in int clientIf, in String address, in int mtu); void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallbackExt callback); + void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); diff --git a/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl similarity index 97% rename from framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl rename to framework/java/android/bluetooth/IBluetoothGattCallback.aidl index ed69e54671c..4f85cdda87f 100644 --- a/framework/java/android/bluetooth/IBluetoothGattCallbackExt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -22,7 +22,7 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattCallbackExt { +oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl similarity index 97% rename from framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl rename to framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl index 267e8824439..74ee11fbd32 100644 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl @@ -21,7 +21,7 @@ import android.bluetooth.BluetoothGattService; * Callback definitions for interacting with BLE / GATT * @hide */ -oneway interface IBluetoothGattServerCallbackExt { +oneway interface IBluetoothGattServerCallback { void onServerRegistered(in int status, in int serverIf); void onServerConnectionState(in int status, in int serverIf, in boolean connected, in String address); -- GitLab From 1bafa47caf7b1f7584774ea7a479a21d1d79f197 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 22:44:09 -0700 Subject: [PATCH 0718/1408] Bluetooth API spelling fixes ("wether" -> "whether") Bug: 30622771 Test: manual Change-Id: I01c8b18d0057f2fd6e477ce2ca0b779321b6c0e6 --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- .../java/android/bluetooth/BluetoothGattServer.java | 2 +- .../android/bluetooth/le/AdvertisingSetParameters.java | 10 +++++----- .../bluetooth/le/PeriodicAdvertisingParameters.java | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 99ca11ece50..02ba403adf8 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -778,7 +778,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, wether the PHY change will happen depends on other applications peferences, + * recommendation, whether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index b35a5933593..2df2ed8ff86 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -550,7 +550,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, wether the PHY change will happen depends on other applications peferences, + * recommendation, whether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        * {@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index fe1f425c4fc..d36c0d676fc 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -279,7 +279,7 @@ public final class AdvertisingSetParameters implements Parcelable { * When set to true, advertising set will advertise 4.x Spec compliant * advertisements. * - * @param isLegacy wether legacy advertising mode should be used. + * @param isLegacy whether legacy advertising mode should be used. */ public Builder setLegacyMode(boolean isLegacy) { this.isLegacy = isLegacy; @@ -287,12 +287,12 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set wether advertiser address should be ommited from all packets. If this + * Set whether advertiser address should be ommited from all packets. If this * mode is used, periodic advertising can't be enabled for this set. * * This is used only if legacy mode is not used. * - * @param isAnonymous wether anonymous advertising should be used. + * @param isAnonymous whether anonymous advertising should be used. */ public Builder setAnonymous(boolean isAnonymous) { this.isAnonymous = isAnonymous; @@ -300,11 +300,11 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set wether TX power should be included in the extended header. + * Set whether TX power should be included in the extended header. * * This is used only if legacy mode is not used. * - * @param includeTxPower wether TX power should be included in extended + * @param includeTxPower whether TX power should be included in extended * header */ public Builder setIncludeTxPower(boolean includeTxPower) { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index ebc92bd0bcf..149540ce0da 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -93,7 +93,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { private int interval = INTERVAL_MAX; /** - * Set wether the Periodic Advertising should be enabled for this set. + * Set whether the Periodic Advertising should be enabled for this set. */ public Builder setEnable(boolean enable) { this.enable = enable; -- GitLab From 9c63d28a1fa55e05b36e4fb90eedcda6de409745 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 22:53:18 -0700 Subject: [PATCH 0719/1408] Bluetooth: fix comment wording Test: manual Bug: 30622771 Change-Id: I5a589c98553f35248b0d95d332e9f35774075b24 --- .../bluetooth/le/AdvertisingSetParameters.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index d36c0d676fc..3e13ad34f01 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -253,10 +253,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be connectable or * non-connectable. - * Legacy advertisements can be both connectable and scannable. Other - * advertisements can be connectable only if not scannable. + * Legacy advertisements must be both connectable and scannable. Nonlegacy + * advertisements can be only scannable or only connectable. * @param connectable Controls whether the advertisment type will be - * connectable (true) or non-connectable (false). + * connectable (true) or nonconnectable (false). */ public Builder setConnectable(boolean connectable) { this.connectable = connectable; @@ -264,11 +264,11 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set whether the advertisement type should be scannable - * Legacy advertisements can be both connectable and scannable. Other - * advertisements can be scannable only if not connectable. + * Set whether the advertisement type should be scannable. + * Legacy advertisements must be both connectable and scannable. Nonlegacy + * advertisements can be only scannable or only connectable. * @param scannable Controls whether the advertisment type will be - * scannable (true) or non-scannable (false). + * scannable (true) or nonscannable (false). */ public Builder setScannable(boolean scannable) { this.scannable = scannable; -- GitLab From 6319d47c4fed4f35af17fb2e906e5d97c07b3bb6 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Fri, 24 Feb 2017 10:19:14 -0800 Subject: [PATCH 0720/1408] Add new internal API: enableOptionalCodecs()/disableOptionalCodecs() This API can be used to enable the optional codecs, or disable them and use only the mandatory SBC. Internally, it is implemented by raising the SBC priority to highest (so SBC will be used/selected), or reducing the SBC priority to its default value (lowest). Test: A2DP streaming and enabling/disabling/selecting optional codecs Bug: 35873828 Change-Id: Ia82036ac33590a3a402b1f5a36102264d47a9029 --- .../java/android/bluetooth/BluetoothA2dp.java | 45 +++++++++++++++++++ .../bluetooth/BluetoothCodecConfig.java | 22 ++++++++- .../android/bluetooth/IBluetoothA2dp.aidl | 2 + 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 4960159db21..08e5a6ef487 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -610,6 +610,51 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /** + * Enables the optional codecs. + * + * @hide + */ + public void enableOptionalCodecs() { + if (DBG) Log.d(TAG, "enableOptionalCodecs"); + enableDisableOptionalCodecs(true); + } + + /** + * Disables the optional codecs. + * + * @hide + */ + public void disableOptionalCodecs() { + if (DBG) Log.d(TAG, "disableOptionalCodecs"); + enableDisableOptionalCodecs(false); + } + + /** + * Enables or disables the optional codecs. + * + * @param enable if true, enable the optional codecs, other disable them + */ + private void enableDisableOptionalCodecs(boolean enable) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + if (enable) { + mService.enableOptionalCodecs(); + } else { + mService.disableOptionalCodecs(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); + return; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 176e48fb6e0..d5e14298101 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -63,7 +63,7 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final int mCodecType; - private final int mCodecPriority; + private int mCodecPriority; private final int mSampleRate; private final int mBitsPerSample; private final int mChannelMode; @@ -279,6 +279,15 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecType; } + /** + * Checks whether the codec is mandatory. + * + * @return true if the codec is mandatory, otherwise false. + */ + public boolean isMandatoryCodec() { + return mCodecType == SOURCE_CODEC_TYPE_SBC; + } + /** * Gets the codec selection priority. * The codec selection priority is relative to other codecs: larger value @@ -290,6 +299,17 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecPriority; } + /** + * Sets the codec selection priority. + * The codec selection priority is relative to other codecs: larger value + * means higher priority. If 0, reset to default. + * + * @param codecPriority the codec priority + */ + public void setCodecPriority(int codecPriority) { + mCodecPriority = codecPriority; + } + /** * Gets the codec sample rate. The value can be a bitmask with all * supported sample rates: diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index dbb5b7d7944..a775a1f90b8 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -40,4 +40,6 @@ interface IBluetoothA2dp { boolean isA2dpPlaying(in BluetoothDevice device); BluetoothCodecStatus getCodecStatus(); oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); + oneway void enableOptionalCodecs(); + oneway void disableOptionalCodecs(); } -- GitLab From dffdf5e58e208fcbd306284ea9d8daa3d0fc8124 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Fri, 24 Feb 2017 10:19:14 -0800 Subject: [PATCH 0721/1408] Add new internal API: enableOptionalCodecs()/disableOptionalCodecs() This API can be used to enable the optional codecs, or disable them and use only the mandatory SBC. Internally, it is implemented by raising the SBC priority to highest (so SBC will be used/selected), or reducing the SBC priority to its default value (lowest). Test: A2DP streaming and enabling/disabling/selecting optional codecs Bug: 35873828 Change-Id: Ia82036ac33590a3a402b1f5a36102264d47a9029 (cherry picked from commit 6319d47c4fed4f35af17fb2e906e5d97c07b3bb6) --- .../java/android/bluetooth/BluetoothA2dp.java | 45 +++++++++++++++++++ .../bluetooth/BluetoothCodecConfig.java | 22 ++++++++- .../android/bluetooth/IBluetoothA2dp.aidl | 2 + 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 6e31d807a04..1ca2be5b2a1 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -611,6 +611,51 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /** + * Enables the optional codecs. + * + * @hide + */ + public void enableOptionalCodecs() { + if (DBG) Log.d(TAG, "enableOptionalCodecs"); + enableDisableOptionalCodecs(true); + } + + /** + * Disables the optional codecs. + * + * @hide + */ + public void disableOptionalCodecs() { + if (DBG) Log.d(TAG, "disableOptionalCodecs"); + enableDisableOptionalCodecs(false); + } + + /** + * Enables or disables the optional codecs. + * + * @param enable if true, enable the optional codecs, other disable them + */ + private void enableDisableOptionalCodecs(boolean enable) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + if (enable) { + mService.enableOptionalCodecs(); + } else { + mService.disableOptionalCodecs(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); + return; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 176e48fb6e0..d5e14298101 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -63,7 +63,7 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final int mCodecType; - private final int mCodecPriority; + private int mCodecPriority; private final int mSampleRate; private final int mBitsPerSample; private final int mChannelMode; @@ -279,6 +279,15 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecType; } + /** + * Checks whether the codec is mandatory. + * + * @return true if the codec is mandatory, otherwise false. + */ + public boolean isMandatoryCodec() { + return mCodecType == SOURCE_CODEC_TYPE_SBC; + } + /** * Gets the codec selection priority. * The codec selection priority is relative to other codecs: larger value @@ -290,6 +299,17 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecPriority; } + /** + * Sets the codec selection priority. + * The codec selection priority is relative to other codecs: larger value + * means higher priority. If 0, reset to default. + * + * @param codecPriority the codec priority + */ + public void setCodecPriority(int codecPriority) { + mCodecPriority = codecPriority; + } + /** * Gets the codec sample rate. The value can be a bitmask with all * supported sample rates: diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index dbb5b7d7944..a775a1f90b8 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -40,4 +40,6 @@ interface IBluetoothA2dp { boolean isA2dpPlaying(in BluetoothDevice device); BluetoothCodecStatus getCodecStatus(); oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); + oneway void enableOptionalCodecs(); + oneway void disableOptionalCodecs(); } -- GitLab From 409577c38a97b1a919236ba655d5872ca158b77c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 29 Mar 2017 17:00:44 -0700 Subject: [PATCH 0722/1408] Hide periodic scanning Bug: 30622771 Test: manual Change-Id: I5d786b13cf99287732e16769e2563ac7c4fe715c --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingCallback.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingManager.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 1 + 4 files changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 27640e78825..2c4520d391f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -638,6 +638,7 @@ public final class BluetoothAdapter { *

        * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is * supported on this device before calling this method. + * @hide */ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java index 6616231bcdf..364b575b4d8 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice; * advertising operation status. * * @see PeriodicAdvertisingManager#createSync + * @hide */ public abstract class PeriodicAdvertisingCallback { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 12c8a8c8ffd..d9c2d8819a3 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -37,6 +37,7 @@ import java.util.Map; *

        * Note: Most of the methods here require * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * @hide */ public final class PeriodicAdvertisingManager { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 3ff4ca58006..51b93cbd64d 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -24,6 +24,7 @@ import java.util.Objects; /** * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. + * @hide */ public final class PeriodicAdvertisingReport implements Parcelable { -- GitLab From 178b028531db3a15866ff9dba6b875610f6ecbe3 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Tue, 28 Mar 2017 14:28:27 -0700 Subject: [PATCH 0723/1408] Bluetooth: Add additional BluetoothSocket logging to root cause errors Bug: 34780400 Test: Code still compiles Change-Id: I166842ecc6889f9ea403e7aa3678ed818cd80968 --- .../java/android/bluetooth/BluetoothSocket.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 98a5341b302..6bf6aa0a9ef 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -416,6 +416,11 @@ public final class BluetoothSocket implements Closeable { if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); + if (fd == null) { + Log.e(TAG, "bindListen(), null file descriptor"); + return -1; + } + if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket"); mSocket = LocalSocket.createConnectedLocalSocket(fd); if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()"); @@ -556,8 +561,9 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " - + mSocketState); + Log.d(TAG, "close() this: " + this + ", channel: " + mPort + + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + + "mSocket: " + mSocket + ", mSocketState: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -567,9 +573,6 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + - ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + - "mSocket: " + mSocket); if(mSocket != null) { if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); -- GitLab From ec010c2a095832859320d300705bb450f35adc38 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Tue, 28 Mar 2017 14:28:27 -0700 Subject: [PATCH 0724/1408] Bluetooth: Add additional BluetoothSocket logging to root cause errors Bug: 34780400 Test: Code still compiles Change-Id: I166842ecc6889f9ea403e7aa3678ed818cd80968 --- .../java/android/bluetooth/BluetoothSocket.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 98a5341b302..6bf6aa0a9ef 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -416,6 +416,11 @@ public final class BluetoothSocket implements Closeable { if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); + if (fd == null) { + Log.e(TAG, "bindListen(), null file descriptor"); + return -1; + } + if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket"); mSocket = LocalSocket.createConnectedLocalSocket(fd); if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()"); @@ -556,8 +561,9 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " - + mSocketState); + Log.d(TAG, "close() this: " + this + ", channel: " + mPort + + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + + "mSocket: " + mSocket + ", mSocketState: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -567,9 +573,6 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + - ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + - "mSocket: " + mSocket); if(mSocket != null) { if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); -- GitLab From b510927e3663b341adc80c24aea17af4fbab3d93 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 31 Mar 2017 16:49:13 -0700 Subject: [PATCH 0725/1408] Fix advertise data size estimation UUID in service data field can be 2, 4, or 16 bytes long. Test: manual Bug: 36553478 Change-Id: Ib5ba2d16065496ca311e8642a15a7ea6bc84d4c1 --- .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 242ee77ce0e..07d9b6d0822 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -56,7 +56,6 @@ public final class BluetoothLeAdvertiser { // Flags field will be set by system. private static final int FLAGS_FIELD_BYTES = 3; private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; - private static final int SERVICE_DATA_UUID_LENGTH = 2; private final IBluetoothManager mBluetoothManager; private final Handler mHandler; @@ -383,7 +382,8 @@ public final class BluetoothLeAdvertiser { } } for (ParcelUuid uuid : data.getServiceData().keySet()) { - size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH + int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; + size += OVERHEAD_BYTES_PER_FIELD + uuidLen + byteLength(data.getServiceData().get(uuid)); } for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { -- GitLab From 76741757b6061c005b7dcb20d9ebecb43f37a147 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 30 Mar 2017 11:19:24 -0700 Subject: [PATCH 0726/1408] Bluetooth LE Advertising minor improvements This patch adds some additional error checking for the advertising set parameters, and some more comments. Test: manual Bug: 30622771 Change-Id: I87bd44f4179ef63694ad3ed656dc2acc52e40f1e --- .../android/bluetooth/le/AdvertisingSet.java | 38 +++- .../le/AdvertisingSetParameters.java | 35 +++- .../bluetooth/le/BluetoothLeAdvertiser.java | 179 +++++++++++++----- 3 files changed, 195 insertions(+), 57 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 7355b0d4c76..d6991bf8767 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.le.IAdvertisingSetCallback; @@ -57,11 +58,12 @@ public final class AdvertisingSet { /** * Enables Advertising. This method returns immediately, the operation status is - * delivered - * through {@code callback.onAdvertisingEnabled()}. + * delivered through {@code callback.onAdvertisingEnabled()}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * + * @param enable whether the advertising should be enabled (true), or disabled (false) + * @param timeoutMillis duration for which that advertising set is enabled. */ public void enableAdvertising(boolean enable, int timeout) { try { @@ -77,10 +79,16 @@ public final class AdvertisingSet { * delivered through {@code callback.onAdvertisingDataSet()}. *

        * Advertising data must be empty if non-legacy scannable advertising is used. + * + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. If the + * update takes place when the advertising set is enabled, the data can be + * maximum 251 bytes long. */ - public void setAdvertisingData(AdvertiseData data) { + public void setAdvertisingData(AdvertiseData advertiseData) { try { - gatt.setAdvertisingData(this.advertiserId, data); + gatt.setAdvertisingData(this.advertiserId, advertiseData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -90,10 +98,15 @@ public final class AdvertisingSet { * Set/update scan response data. Make sure that data doesn't exceed the size limit for * specified AdvertisingSetParameters. This method returns immediately, the operation status * is delivered through {@code callback.onScanResponseDataSet()}. + * + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * update takes place when the advertising set is enabled, the data can be + * maximum 251 bytes long. */ - public void setScanResponseData(AdvertiseData data) { + public void setScanResponseData(AdvertiseData scanResponse) { try { - gatt.setScanResponseData(this.advertiserId, data); + gatt.setScanResponseData(this.advertiserId, scanResponse); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -103,6 +116,8 @@ public final class AdvertisingSet { * Update advertising parameters associated with this AdvertisingSet. Must be called when * advertising is not active. This method returns immediately, the operation status is delivered * through {@code callback.onAdvertisingParametersUpdated}. + * + * @param parameters advertising set parameters. */ public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { @@ -130,10 +145,15 @@ public final class AdvertisingSet { * or after advertising was started with periodic advertising data set. This method returns * immediately, the operation status is delivered through * {@code callback.onPeriodicAdvertisingDataSet()}. + * + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * update takes place when the periodic advertising is enabled for this set, + * the data can be maximum 251 bytes long. */ - public void setPeriodicAdvertisingData(AdvertiseData data) { + public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { - gatt.setPeriodicAdvertisingData(this.advertiserId, data); + gatt.setPeriodicAdvertisingData(this.advertiserId, periodicData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -142,6 +162,8 @@ public final class AdvertisingSet { /** * Used to enable/disable periodic advertising. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. + * + * @param enable whether the periodic advertising should be enabled (true), or disabled (false). */ public void setPeriodicAdvertisingEnable(boolean enable) { try { diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 3e13ad34f01..7e371574246 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.bluetooth.BluetoothAdapter; import android.os.Parcel; import android.os.Parcelable; @@ -305,7 +306,7 @@ public final class AdvertisingSetParameters implements Parcelable { * This is used only if legacy mode is not used. * * @param includeTxPower whether TX power should be included in extended - * header + * header */ public Builder setIncludeTxPower(boolean includeTxPower) { this.includeTxPower = includeTxPower; @@ -317,6 +318,8 @@ public final class AdvertisingSetParameters implements Parcelable { * * This is used only if legacy mode is not used. * + * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is + * supported on this device. * @param primaryPhy Primary advertising physical channel, can only be * {@link AdvertisingSetParameters#PHY_LE_1M} or * {@link AdvertisingSetParameters#PHY_LE_CODED}. @@ -335,6 +338,10 @@ public final class AdvertisingSetParameters implements Parcelable { * * This is used only if legacy mode is not used. * + * Use {@link BluetoothAdapter#isLeCodedPhySupported} and + * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is + * supported on this device. + * * @param secondaryPhy Secondary advertising physical channel, can only be * one of {@link AdvertisingSetParameters#PHY_LE_1M}, * {@link AdvertisingSetParameters#PHY_LE_2M} or @@ -393,6 +400,32 @@ public final class AdvertisingSetParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public AdvertisingSetParameters build() { + if (isLegacy) { + if (isAnonymous) { + throw new IllegalArgumentException("Legacy advertising can't be anonymous"); + } + + if (connectable == true && scannable == false) { + throw new IllegalArgumentException( + "Legacy advertisement can't be connectable and non-scannable"); + } + + if (includeTxPower) { + throw new IllegalArgumentException( + "Legacy advertising can't include TX power level in header"); + } + } else { + if (connectable && scannable) { + throw new IllegalArgumentException( + "Advertising can't be both connectable and scannable"); + } + + if (isAnonymous && connectable) { + throw new IllegalArgumentException( + "Advertising can't be both connectable and anonymous"); + } + } + return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, includeTxPower, primaryPhy, secondaryPhy, interval, txPowerLevel); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 07d9b6d0822..2ccf08e89b8 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -50,7 +50,8 @@ public final class BluetoothLeAdvertiser { private static final String TAG = "BluetoothLeAdvertiser"; - private static final int MAX_ADVERTISING_DATA_BYTES = 31; + private static final int MAX_ADVERTISING_DATA_BYTES = 1650; + private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31; // Each fields need one byte for field length and another byte for field type. private static final int OVERHEAD_BYTES_PER_FIELD = 2; // Flags field will be set by system. @@ -116,8 +117,8 @@ public final class BluetoothLeAdvertiser { throw new IllegalArgumentException("callback cannot be null"); } boolean isConnectable = settings.isConnectable(); - if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) { + if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES || + totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } @@ -205,16 +206,26 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param callback Callback for advertising set. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param callback Callback for advertising set. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -224,17 +235,27 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -245,17 +266,27 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param timeoutMillis Advertising time limit. May not exceed 180000 - * @param callback Callback for advertising set. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -266,29 +297,81 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param timeoutMillis Advertising time limit. May not exceed 180000 - * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, int timeoutMillis, AdvertisingSetCallback callback, Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } + boolean isConnectable = parameters.isConnectable(); + if (parameters.isLegacy()) { + if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException("Legacy advertising data too big"); + } + + if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException("Legacy scan response data too big"); + } + } else { + boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported(); + boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported(); + int pphy = parameters.getPrimaryPhy(); + int sphy = parameters.getSecondaryPhy(); + if (pphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) { + throw new IllegalArgumentException("Unsupported primary PHY selected"); + } + + if ((sphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) + || (sphy == AdvertisingSetParameters.PHY_LE_2M && !support2MPhy)) { + throw new IllegalArgumentException("Unsupported secondary PHY selected"); + } + + int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength(); + if (totalBytes(advertiseData, isConnectable) > maxData) { + throw new IllegalArgumentException("Advertising data too big"); + } + + if (totalBytes(scanResponse, false) > maxData) { + throw new IllegalArgumentException("Scan response data too big"); + } + + if (totalBytes(periodicData, false) > maxData) { + throw new IllegalArgumentException("Periodic advertising data too big"); + } + + boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); + if (periodicParameters != null && periodicParameters.getEnable() && !supportPeriodic) { + throw new IllegalArgumentException( + "Controller does not support LE Periodic Advertising"); + } + } + IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); -- GitLab From 89268ba576c2ee0f290e28b5710c1f69aff0a953 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 30 Mar 2017 19:10:08 -0700 Subject: [PATCH 0727/1408] Bluetooth 5 advertising duration refactoring (4/4) Expose both duration and maximum extended advertising events to limit advertising time. Test: manual Bug: 30622771 Change-Id: I44df300995ef985526b93f8c24389775720b3432 --- .../android/bluetooth/IBluetoothGatt.aidl | 5 +- .../android/bluetooth/le/AdvertisingSet.java | 13 ++- .../bluetooth/le/BluetoothLeAdvertiser.java | 82 +++++++++++++------ 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 0825ee88a45..334e88b69fd 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -52,10 +52,11 @@ interface IBluetoothGatt { void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback); + in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, + in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); - void enableAdvertisingSet(in int advertiserId, in boolean enable, in int timeout); + void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index d6991bf8767..51571b2746a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -63,11 +63,18 @@ public final class AdvertisingSet { * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param enable whether the advertising should be enabled (true), or disabled (false) - * @param timeoutMillis duration for which that advertising set is enabled. + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms) + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. */ - public void enableAdvertising(boolean enable, int timeout) { + public void enableAdvertising(boolean enable, int duration, + int maxExtendedAdvertisingEvents) { try { - gatt.enableAdvertisingSet(this.advertiserId, enable, timeout); + gatt.enableAdvertisingSet(this.advertiserId, enable, duration, + maxExtendedAdvertisingEvents); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 2ccf08e89b8..a9deb752e08 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -149,10 +149,16 @@ public final class BluetoothLeAdvertiser { parameters.setTxPowerLevel(1); } + int duration = 0; + int timeoutMillis = settings.getTimeout(); + if (timeoutMillis > 0) { + duration = (timeoutMillis < 10) ? 1 : timeoutMillis/10; + } + AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); mLegacyAdvertisers.put(callback, wrapped); startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, - settings.getTimeout(), wrapped); + duration, 0, wrapped); } } @@ -214,8 +220,8 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed @@ -231,7 +237,7 @@ public final class BluetoothLeAdvertiser { PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, AdvertisingSetCallback callback) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, callback, new Handler(Looper.getMainLooper())); + periodicData, 0, 0, callback, new Handler(Looper.getMainLooper())); } /** @@ -243,8 +249,8 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed @@ -262,7 +268,7 @@ public final class BluetoothLeAdvertiser { AdvertiseData periodicData, AdvertisingSetCallback callback, Handler handler) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, callback, handler); + periodicData, 0, 0, callback, handler); } /** @@ -274,13 +280,18 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable * size, or unsupported advertising PHY is selected, or when attempt to use @@ -290,10 +301,12 @@ public final class BluetoothLeAdvertiser { public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int timeoutMillis, + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, timeoutMillis, callback, new Handler(Looper.getMainLooper())); + periodicData, duration, maxExtendedAdvertisingEvents, callback, + new Handler(Looper.getMainLooper())); } /** @@ -301,29 +314,36 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        - * @param parameters advertising set parameters. + * @param parameters Advertising set parameters. * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * @param handler Thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable * size, or unsupported advertising PHY is selected, or when attempt to use * Periodic Advertising feature is made when it's not supported by the - * controller. + * controller, or when maxExtendedAdvertisingEvents is used on a controller + * that doesn't support the LE Extended Advertising */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int timeoutMillis, - AdvertisingSetCallback callback, Handler handler) { + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback, + Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -372,6 +392,22 @@ public final class BluetoothLeAdvertiser { } } + if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) { + throw new IllegalArgumentException( + "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); + } + + if (maxExtendedAdvertisingEvents != 0 && + !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { + throw new IllegalArgumentException( + "Can't use maxExtendedAdvertisingEvents with controller that don't support " + + "LE Extended Advertising"); + } + + if (duration < 0 || duration > 65535) { + throw new IllegalArgumentException("duration out of range: " + duration); + } + IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); @@ -388,7 +424,7 @@ public final class BluetoothLeAdvertiser { try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, timeoutMillis, wrapped); + periodicData, duration, maxExtendedAdvertisingEvents, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); throw new IllegalStateException("Failed to start advertising set"); -- GitLab From 6ae3c030205fe169631a9cb496465c15f20852bd Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 22:44:09 -0700 Subject: [PATCH 0728/1408] Bluetooth API spelling fixes ("wether" -> "whether") Bug: 30622771 Test: manual Change-Id: I01c8b18d0057f2fd6e477ce2ca0b779321b6c0e6 (cherry picked from commit 1bafa47caf7b1f7584774ea7a479a21d1d79f197) --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- .../java/android/bluetooth/BluetoothGattServer.java | 2 +- .../android/bluetooth/le/AdvertisingSetParameters.java | 10 +++++----- .../bluetooth/le/PeriodicAdvertisingParameters.java | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index aa61ce282c0..5d1e8ec58ce 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -778,7 +778,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, wether the PHY change will happen depends on other applications peferences, + * recommendation, whether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index b35a5933593..2df2ed8ff86 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -550,7 +550,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, wether the PHY change will happen depends on other applications peferences, + * recommendation, whether the PHY change will happen depends on other applications peferences, * local and remote controller capabilities. Controller can override these settings. *

        * {@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index fe1f425c4fc..d36c0d676fc 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -279,7 +279,7 @@ public final class AdvertisingSetParameters implements Parcelable { * When set to true, advertising set will advertise 4.x Spec compliant * advertisements. * - * @param isLegacy wether legacy advertising mode should be used. + * @param isLegacy whether legacy advertising mode should be used. */ public Builder setLegacyMode(boolean isLegacy) { this.isLegacy = isLegacy; @@ -287,12 +287,12 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set wether advertiser address should be ommited from all packets. If this + * Set whether advertiser address should be ommited from all packets. If this * mode is used, periodic advertising can't be enabled for this set. * * This is used only if legacy mode is not used. * - * @param isAnonymous wether anonymous advertising should be used. + * @param isAnonymous whether anonymous advertising should be used. */ public Builder setAnonymous(boolean isAnonymous) { this.isAnonymous = isAnonymous; @@ -300,11 +300,11 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set wether TX power should be included in the extended header. + * Set whether TX power should be included in the extended header. * * This is used only if legacy mode is not used. * - * @param includeTxPower wether TX power should be included in extended + * @param includeTxPower whether TX power should be included in extended * header */ public Builder setIncludeTxPower(boolean includeTxPower) { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index ebc92bd0bcf..149540ce0da 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -93,7 +93,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { private int interval = INTERVAL_MAX; /** - * Set wether the Periodic Advertising should be enabled for this set. + * Set whether the Periodic Advertising should be enabled for this set. */ public Builder setEnable(boolean enable) { this.enable = enable; -- GitLab From 8d0c4baa427a7a6f6c84227c0585096534fed614 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 29 Mar 2017 17:00:44 -0700 Subject: [PATCH 0729/1408] Hide periodic scanning Bug: 30622771 Test: manual Change-Id: I5d786b13cf99287732e16769e2563ac7c4fe715c (cherry picked from commit 409577c38a97b1a919236ba655d5872ca158b77c) --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingCallback.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingManager.java | 1 + .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 1 + 4 files changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4e1e42da4fe..845a47d9984 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -638,6 +638,7 @@ public final class BluetoothAdapter { *

        * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is * supported on this device before calling this method. + * @hide */ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java index 6616231bcdf..364b575b4d8 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice; * advertising operation status. * * @see PeriodicAdvertisingManager#createSync + * @hide */ public abstract class PeriodicAdvertisingCallback { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 12c8a8c8ffd..d9c2d8819a3 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -37,6 +37,7 @@ import java.util.Map; *

        * Note: Most of the methods here require * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * @hide */ public final class PeriodicAdvertisingManager { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 3ff4ca58006..51b93cbd64d 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -24,6 +24,7 @@ import java.util.Objects; /** * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. + * @hide */ public final class PeriodicAdvertisingReport implements Parcelable { -- GitLab From d9ea49e8ddd327bb80a00132a8b6d6a1a4b5b554 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 31 Mar 2017 16:49:13 -0700 Subject: [PATCH 0730/1408] Fix advertise data size estimation UUID in service data field can be 2, 4, or 16 bytes long. Test: manual Bug: 36553478 Change-Id: Ib5ba2d16065496ca311e8642a15a7ea6bc84d4c1 (cherry picked from commit 72e9e9f81504559ca18b71358203b1b39d9f0581) --- .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 242ee77ce0e..07d9b6d0822 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -56,7 +56,6 @@ public final class BluetoothLeAdvertiser { // Flags field will be set by system. private static final int FLAGS_FIELD_BYTES = 3; private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; - private static final int SERVICE_DATA_UUID_LENGTH = 2; private final IBluetoothManager mBluetoothManager; private final Handler mHandler; @@ -383,7 +382,8 @@ public final class BluetoothLeAdvertiser { } } for (ParcelUuid uuid : data.getServiceData().keySet()) { - size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH + int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; + size += OVERHEAD_BYTES_PER_FIELD + uuidLen + byteLength(data.getServiceData().get(uuid)); } for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { -- GitLab From 55fd3fdfaa29413a18f67f1d1436c947d1050d7a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 30 Mar 2017 11:19:24 -0700 Subject: [PATCH 0731/1408] Bluetooth LE Advertising minor improvements This patch adds some additional error checking for the advertising set parameters, and some more comments. Test: manual Bug: 30622771 Change-Id: I87bd44f4179ef63694ad3ed656dc2acc52e40f1e (cherry picked from commit f4ed33f5fa6ffa3bda6faff773a3fb90b16a760c) --- .../android/bluetooth/le/AdvertisingSet.java | 38 +++- .../le/AdvertisingSetParameters.java | 35 +++- .../bluetooth/le/BluetoothLeAdvertiser.java | 179 +++++++++++++----- 3 files changed, 195 insertions(+), 57 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 7355b0d4c76..d6991bf8767 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.le.IAdvertisingSetCallback; @@ -57,11 +58,12 @@ public final class AdvertisingSet { /** * Enables Advertising. This method returns immediately, the operation status is - * delivered - * through {@code callback.onAdvertisingEnabled()}. + * delivered through {@code callback.onAdvertisingEnabled()}. *

        * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * + * @param enable whether the advertising should be enabled (true), or disabled (false) + * @param timeoutMillis duration for which that advertising set is enabled. */ public void enableAdvertising(boolean enable, int timeout) { try { @@ -77,10 +79,16 @@ public final class AdvertisingSet { * delivered through {@code callback.onAdvertisingDataSet()}. *

        * Advertising data must be empty if non-legacy scannable advertising is used. + * + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. If the + * update takes place when the advertising set is enabled, the data can be + * maximum 251 bytes long. */ - public void setAdvertisingData(AdvertiseData data) { + public void setAdvertisingData(AdvertiseData advertiseData) { try { - gatt.setAdvertisingData(this.advertiserId, data); + gatt.setAdvertisingData(this.advertiserId, advertiseData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -90,10 +98,15 @@ public final class AdvertisingSet { * Set/update scan response data. Make sure that data doesn't exceed the size limit for * specified AdvertisingSetParameters. This method returns immediately, the operation status * is delivered through {@code callback.onScanResponseDataSet()}. + * + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * update takes place when the advertising set is enabled, the data can be + * maximum 251 bytes long. */ - public void setScanResponseData(AdvertiseData data) { + public void setScanResponseData(AdvertiseData scanResponse) { try { - gatt.setScanResponseData(this.advertiserId, data); + gatt.setScanResponseData(this.advertiserId, scanResponse); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -103,6 +116,8 @@ public final class AdvertisingSet { * Update advertising parameters associated with this AdvertisingSet. Must be called when * advertising is not active. This method returns immediately, the operation status is delivered * through {@code callback.onAdvertisingParametersUpdated}. + * + * @param parameters advertising set parameters. */ public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { @@ -130,10 +145,15 @@ public final class AdvertisingSet { * or after advertising was started with periodic advertising data set. This method returns * immediately, the operation status is delivered through * {@code callback.onPeriodicAdvertisingDataSet()}. + * + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * update takes place when the periodic advertising is enabled for this set, + * the data can be maximum 251 bytes long. */ - public void setPeriodicAdvertisingData(AdvertiseData data) { + public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { - gatt.setPeriodicAdvertisingData(this.advertiserId, data); + gatt.setPeriodicAdvertisingData(this.advertiserId, periodicData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -142,6 +162,8 @@ public final class AdvertisingSet { /** * Used to enable/disable periodic advertising. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. + * + * @param enable whether the periodic advertising should be enabled (true), or disabled (false). */ public void setPeriodicAdvertisingEnable(boolean enable) { try { diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index d36c0d676fc..f5c1f08562f 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.bluetooth.BluetoothAdapter; import android.os.Parcel; import android.os.Parcelable; @@ -305,7 +306,7 @@ public final class AdvertisingSetParameters implements Parcelable { * This is used only if legacy mode is not used. * * @param includeTxPower whether TX power should be included in extended - * header + * header */ public Builder setIncludeTxPower(boolean includeTxPower) { this.includeTxPower = includeTxPower; @@ -317,6 +318,8 @@ public final class AdvertisingSetParameters implements Parcelable { * * This is used only if legacy mode is not used. * + * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is + * supported on this device. * @param primaryPhy Primary advertising physical channel, can only be * {@link AdvertisingSetParameters#PHY_LE_1M} or * {@link AdvertisingSetParameters#PHY_LE_CODED}. @@ -335,6 +338,10 @@ public final class AdvertisingSetParameters implements Parcelable { * * This is used only if legacy mode is not used. * + * Use {@link BluetoothAdapter#isLeCodedPhySupported} and + * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is + * supported on this device. + * * @param secondaryPhy Secondary advertising physical channel, can only be * one of {@link AdvertisingSetParameters#PHY_LE_1M}, * {@link AdvertisingSetParameters#PHY_LE_2M} or @@ -393,6 +400,32 @@ public final class AdvertisingSetParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public AdvertisingSetParameters build() { + if (isLegacy) { + if (isAnonymous) { + throw new IllegalArgumentException("Legacy advertising can't be anonymous"); + } + + if (connectable == true && scannable == false) { + throw new IllegalArgumentException( + "Legacy advertisement can't be connectable and non-scannable"); + } + + if (includeTxPower) { + throw new IllegalArgumentException( + "Legacy advertising can't include TX power level in header"); + } + } else { + if (connectable && scannable) { + throw new IllegalArgumentException( + "Advertising can't be both connectable and scannable"); + } + + if (isAnonymous && connectable) { + throw new IllegalArgumentException( + "Advertising can't be both connectable and anonymous"); + } + } + return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, includeTxPower, primaryPhy, secondaryPhy, interval, txPowerLevel); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 07d9b6d0822..2ccf08e89b8 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -50,7 +50,8 @@ public final class BluetoothLeAdvertiser { private static final String TAG = "BluetoothLeAdvertiser"; - private static final int MAX_ADVERTISING_DATA_BYTES = 31; + private static final int MAX_ADVERTISING_DATA_BYTES = 1650; + private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31; // Each fields need one byte for field length and another byte for field type. private static final int OVERHEAD_BYTES_PER_FIELD = 2; // Flags field will be set by system. @@ -116,8 +117,8 @@ public final class BluetoothLeAdvertiser { throw new IllegalArgumentException("callback cannot be null"); } boolean isConnectable = settings.isConnectable(); - if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) { + if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES || + totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } @@ -205,16 +206,26 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param callback Callback for advertising set. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param callback Callback for advertising set. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -224,17 +235,27 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -245,17 +266,27 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param timeoutMillis Advertising time limit. May not exceed 180000 - * @param callback Callback for advertising set. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -266,29 +297,81 @@ public final class BluetoothLeAdvertiser { } /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - *

        - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. - * @param scanResponse Scan response associated with the advertisement data. - * @param periodicData Periodic advertising data. - * @param timeoutMillis Advertising time limit. May not exceed 180000 - * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - */ + * Creates a new advertising set. If operation succeed, device will start advertising. This + * method returns immediately, the operation status is delivered through + * {@code callback.onAdvertisingSetStarted()}. + *

        + * @param parameters advertising set parameters. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the + * advertisement is connectable, three bytes will be added for flags. + * @param scanResponse Scan response associated with the advertisement data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed + * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param callback Callback for advertising set. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * size, or unsupported advertising PHY is selected, or when attempt to use + * Periodic Advertising feature is made when it's not supported by the + * controller. + */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, int timeoutMillis, AdvertisingSetCallback callback, Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } + boolean isConnectable = parameters.isConnectable(); + if (parameters.isLegacy()) { + if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException("Legacy advertising data too big"); + } + + if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { + throw new IllegalArgumentException("Legacy scan response data too big"); + } + } else { + boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported(); + boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported(); + int pphy = parameters.getPrimaryPhy(); + int sphy = parameters.getSecondaryPhy(); + if (pphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) { + throw new IllegalArgumentException("Unsupported primary PHY selected"); + } + + if ((sphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) + || (sphy == AdvertisingSetParameters.PHY_LE_2M && !support2MPhy)) { + throw new IllegalArgumentException("Unsupported secondary PHY selected"); + } + + int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength(); + if (totalBytes(advertiseData, isConnectable) > maxData) { + throw new IllegalArgumentException("Advertising data too big"); + } + + if (totalBytes(scanResponse, false) > maxData) { + throw new IllegalArgumentException("Scan response data too big"); + } + + if (totalBytes(periodicData, false) > maxData) { + throw new IllegalArgumentException("Periodic advertising data too big"); + } + + boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); + if (periodicParameters != null && periodicParameters.getEnable() && !supportPeriodic) { + throw new IllegalArgumentException( + "Controller does not support LE Periodic Advertising"); + } + } + IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); -- GitLab From 02026a86f1bd2ad2178b5f0193e48d169814f8e6 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 30 Mar 2017 19:10:08 -0700 Subject: [PATCH 0732/1408] Bluetooth 5 advertising duration refactoring (4/4) Expose both duration and maximum extended advertising events to limit advertising time. Test: manual Bug: 30622771 Change-Id: I44df300995ef985526b93f8c24389775720b3432 (cherry picked from commit 5a355610fe6ac0460f7130375de97b4d7bae7ba4) --- .../android/bluetooth/IBluetoothGatt.aidl | 5 +- .../android/bluetooth/le/AdvertisingSet.java | 13 ++- .../bluetooth/le/BluetoothLeAdvertiser.java | 82 +++++++++++++------ 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 0825ee88a45..334e88b69fd 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -52,10 +52,11 @@ interface IBluetoothGatt { void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback); + in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, + in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); - void enableAdvertisingSet(in int advertiserId, in boolean enable, in int timeout); + void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index d6991bf8767..51571b2746a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -63,11 +63,18 @@ public final class AdvertisingSet { * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param enable whether the advertising should be enabled (true), or disabled (false) - * @param timeoutMillis duration for which that advertising set is enabled. + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms) + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. */ - public void enableAdvertising(boolean enable, int timeout) { + public void enableAdvertising(boolean enable, int duration, + int maxExtendedAdvertisingEvents) { try { - gatt.enableAdvertisingSet(this.advertiserId, enable, timeout); + gatt.enableAdvertisingSet(this.advertiserId, enable, duration, + maxExtendedAdvertisingEvents); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 2ccf08e89b8..a9deb752e08 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -149,10 +149,16 @@ public final class BluetoothLeAdvertiser { parameters.setTxPowerLevel(1); } + int duration = 0; + int timeoutMillis = settings.getTimeout(); + if (timeoutMillis > 0) { + duration = (timeoutMillis < 10) ? 1 : timeoutMillis/10; + } + AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); mLegacyAdvertisers.put(callback, wrapped); startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, - settings.getTimeout(), wrapped); + duration, 0, wrapped); } } @@ -214,8 +220,8 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed @@ -231,7 +237,7 @@ public final class BluetoothLeAdvertiser { PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData, AdvertisingSetCallback callback) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, callback, new Handler(Looper.getMainLooper())); + periodicData, 0, 0, callback, new Handler(Looper.getMainLooper())); } /** @@ -243,8 +249,8 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed @@ -262,7 +268,7 @@ public final class BluetoothLeAdvertiser { AdvertiseData periodicData, AdvertisingSetCallback callback, Handler handler) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, callback, handler); + periodicData, 0, 0, callback, handler); } /** @@ -274,13 +280,18 @@ public final class BluetoothLeAdvertiser { * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable * size, or unsupported advertising PHY is selected, or when attempt to use @@ -290,10 +301,12 @@ public final class BluetoothLeAdvertiser { public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int timeoutMillis, + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, timeoutMillis, callback, new Handler(Looper.getMainLooper())); + periodicData, duration, maxExtendedAdvertisingEvents, callback, + new Handler(Looper.getMainLooper())); } /** @@ -301,29 +314,36 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        - * @param parameters advertising set parameters. + * @param parameters Advertising set parameters. * @param advertiseData Advertisement data to be broadcasted. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the * advertisement is connectable, three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will + * @param scanResponse Scan response associated with the advertisement data. Size must not + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will * not be started. * @param periodicData Periodic advertising data. Size must not exceed * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param timeoutMillis Advertising time limit. May not exceed 180000 + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to + * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the + * controller shall attempt to send prior to terminating the extended + * advertising, even if the duration has not expired. Valid range is + * from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable + * @param handler Thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable * size, or unsupported advertising PHY is selected, or when attempt to use * Periodic Advertising feature is made when it's not supported by the - * controller. + * controller, or when maxExtendedAdvertisingEvents is used on a controller + * that doesn't support the LE Extended Advertising */ public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int timeoutMillis, - AdvertisingSetCallback callback, Handler handler) { + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback, + Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -372,6 +392,22 @@ public final class BluetoothLeAdvertiser { } } + if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) { + throw new IllegalArgumentException( + "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); + } + + if (maxExtendedAdvertisingEvents != 0 && + !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { + throw new IllegalArgumentException( + "Can't use maxExtendedAdvertisingEvents with controller that don't support " + + "LE Extended Advertising"); + } + + if (duration < 0 || duration > 65535) { + throw new IllegalArgumentException("duration out of range: " + duration); + } + IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); @@ -388,7 +424,7 @@ public final class BluetoothLeAdvertiser { try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, timeoutMillis, wrapped); + periodicData, duration, maxExtendedAdvertisingEvents, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); throw new IllegalStateException("Failed to start advertising set"); -- GitLab From dec018bb591c81f99b581c274230c0313c487038 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 31 Mar 2017 14:08:23 -0600 Subject: [PATCH 0733/1408] Consistent dump() permission checking. This change introduces new methods on DumpUtils that can check if the caller has DUMP and/or PACKAGE_USAGE_STATS access. It then moves all existing dump() methods to use these checks so that we emit consistent error messages. Test: cts-tradefed run commandAndExit cts-dev -m CtsSecurityTestCases -t android.security.cts.ServicePermissionsTest Bug: 32806790 Change-Id: Iaff6b9506818ee082b1e169c89ebe1001b3bfeca --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 58e86318e80..6c4895c91f2 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import com.android.internal.util.DumpUtils; import com.android.server.pm.PackageManagerService; import java.io.FileDescriptor; @@ -2084,7 +2085,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; String errorMsg = null; boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); -- GitLab From f5e074bd3ff5de54e336cfab8ecf86f5377792a4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 6 Apr 2017 07:22:57 -0700 Subject: [PATCH 0734/1408] Bluetooth 5 fix default AdvertisingSetParameters values Having advertiser be scannable and connectable by default is a bad choice for new advertising set. Bug: 30622771 Test: sl4a Bt5ScanTest Change-Id: I4fc270e78ca4e62d3077c5cd28aa59b0518d2e77 --- .../bluetooth/le/AdvertisingSetParameters.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 7e371574246..4e9fac3ee22 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -241,8 +241,8 @@ public final class AdvertisingSetParameters implements Parcelable { */ public static final class Builder { - private boolean connectable = true; - private boolean scannable = true; + private boolean connectable = false; + private boolean scannable = false; private boolean isLegacy = false; private boolean isAnonymous = false; private boolean includeTxPower = false; @@ -254,10 +254,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be connectable or * non-connectable. - * Legacy advertisements must be both connectable and scannable. Nonlegacy + * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. * @param connectable Controls whether the advertisment type will be - * connectable (true) or nonconnectable (false). + * connectable (true) or non-connectable (false). */ public Builder setConnectable(boolean connectable) { this.connectable = connectable; @@ -266,10 +266,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be scannable. - * Legacy advertisements must be both connectable and scannable. Nonlegacy + * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. * @param scannable Controls whether the advertisment type will be - * scannable (true) or nonscannable (false). + * scannable (true) or non-scannable (false). */ public Builder setScannable(boolean scannable) { this.scannable = scannable; -- GitLab From d8d204d4c79bc04026870d02e48f1166f5543c15 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 17 Nov 2016 16:19:43 -0800 Subject: [PATCH 0735/1408] Add developer setting and system API for inband ringing support * Add 1 system API to check inband ringing flag in config.xml static isInbandRingingSupported(Context) * Add developer menu options to enable this feature Bug: 19171297 Test: mm -j 40, HFP regression testing, testplans/82144 Change-Id: Iaf56ea41911f546bbc7ae1f82e399d0f8d48f75f --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index f46a3b3c733..2d25659bd77 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -1009,6 +1009,18 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * check if in-band ringing is supported for this platform. + * + * @return true if in-band ringing is supported + * false if in-band ringing is not supported + * @hide + */ + public static boolean isInbandRingingSupported(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); + } + /** * Send Headset the BIND response from AG to report change in the status of the * HF indicators to the headset -- GitLab From a08273e783b89d5b31755d4319d6dd04f0d8a608 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 10 Apr 2017 13:45:16 -0700 Subject: [PATCH 0736/1408] Remove enable from PeriodicAdvertisingPariameters (1/2) Instead of setting enable to true, one can just pass null PeriodicAdvertisingParameters and achieve same result when starting the set. Passing the "enable" when updating the parameters make no sense, and might be confusing. Experience with "timeout" field, which was a part of AdvertiseSettings show that merging fields that go into different HCI commands can cause problems during processing, so keep enable as separate field. Test: manual Bug: 30622771 Change-Id: Ida02c59eb8433537179b4d22202fe745f8b4bb3e --- .../bluetooth/le/BluetoothLeAdvertiser.java | 2 +- .../le/PeriodicAdvertisingParameters.java | 22 ++----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index a9deb752e08..73fc1339afa 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -386,7 +386,7 @@ public final class BluetoothLeAdvertiser { } boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); - if (periodicParameters != null && periodicParameters.getEnable() && !supportPeriodic) { + if (periodicParameters != null && !supportPeriodic) { throw new IllegalArgumentException( "Controller does not support LE Periodic Advertising"); } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index 149540ce0da..8891d2e842d 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -29,27 +29,19 @@ public final class PeriodicAdvertisingParameters implements Parcelable { private static final int INTERVAL_MAX = 80; private static final int INTERVAL_MIN = 65519; - private final boolean enable; private final boolean includeTxPower; private final int interval; - private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) { - this.enable = enable; + private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) { this.includeTxPower = includeTxPower; this.interval = interval; } private PeriodicAdvertisingParameters(Parcel in) { - enable = in.readInt() != 0 ? true : false; includeTxPower = in.readInt() != 0 ? true : false; interval = in.readInt(); } - /** - * Returns whether the periodic advertising shall be enabled. - */ - public boolean getEnable() { return enable; } - /** * Returns whether the TX Power will be included. */ @@ -68,7 +60,6 @@ public final class PeriodicAdvertisingParameters implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(enable ? 1 : 0); dest.writeInt(includeTxPower ? 1 : 0); dest.writeInt(interval); } @@ -89,17 +80,8 @@ public final class PeriodicAdvertisingParameters implements Parcelable { public static final class Builder { private boolean includeTxPower = false; - private boolean enable = false; private int interval = INTERVAL_MAX; - /** - * Set whether the Periodic Advertising should be enabled for this set. - */ - public Builder setEnable(boolean enable) { - this.enable = enable; - return this; - } - /** * Whether the transmission power level should be included in the periodic * packet. @@ -128,7 +110,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public PeriodicAdvertisingParameters build() { - return new PeriodicAdvertisingParameters(enable, includeTxPower, interval); + return new PeriodicAdvertisingParameters(includeTxPower, interval); } } } -- GitLab From 2697a69c0e65f845f0fcca59399773d6600f21fe Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Tue, 28 Mar 2017 13:44:04 +0100 Subject: [PATCH 0737/1408] Introduce DISALLOW_BLUETOOTH_SHARING. When this restriction is enforced Bluetooth sharing option should not be present when the user tries to share something. Previously this was handled by explicitly disabling bluetooth sharing activity during managed provisioning, now this code is to be removed (see topic CLs) and the same behavior should be achieved by setting this restriction for profile owners by default. In Bluetooth: 1) Don't check restrictions on boot, it is invoked anyway through the listener during boot. 2) Ignore when the restriction is "changed" from true to true - i think it was the initial intent in that condition. 3) Disable the component for a particular user and not always the system user. This is something that has to be fixed in O I think since currently in secondary user the bluetooth itself gets disabled but the sharing thing still shows up. In DPMS: 1) Now ActiveAdmin for PO also contains a set of restrictions applied by default. 2) Now all ActiveAdmins for POs are loaded quite early. That shouldn't have huge impact though. Bug: 36249732 Test: run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.ManagedProfileTest#testBluetoothSharingRestriction Test: run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java Change-Id: I78c4ffbd503c4a10138e8c0862a9f206f24c5631 --- .../bluetooth/BluetoothManagerService.java | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6c4895c91f2..7b7d340a158 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import android.Manifest; import android.app.ActivityManager; +import android.app.AppGlobals; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; @@ -37,11 +38,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -50,7 +51,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -61,15 +61,15 @@ import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import com.android.internal.util.DumpUtils; -import com.android.server.pm.PackageManagerService; +import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; class BluetoothManagerService extends IBluetoothManager.Stub { @@ -120,7 +120,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; - private static final int MAX_SAVE_RETRIES = 3; private static final int MAX_ERROR_RESTART_RETRIES = 6; // Bluetooth persisted setting is off @@ -223,22 +222,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { - if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) - && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { - // The relevant restriction has not changed - do nothing. - return; + if (!UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, + UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH_SHARING)) { + return; // No relevant changes, nothing to do. } - final boolean bluetoothDisallowed = - newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); - if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + + final boolean disallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + + // DISALLOW_BLUETOOTH is a global restriction that can only be set by DO or PO on the + // system user, so we only look at the system user. + if (userId == UserHandle.USER_SYSTEM && disallowed && (mEnable || mEnableExternal)) { try { - disable(null, true); + disable(null /* packageName */, true /* persist */); } catch (RemoteException e) { - Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", - e); + Slog.w(TAG, "Exception when disabling Bluetooth", e); } } - updateOppLauncherComponentState(bluetoothDisallowed); + final boolean sharingDisallowed = disallowed + || newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING); + updateOppLauncherComponentState(userId, sharingDisallowed); } }; @@ -983,11 +985,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); final boolean isBluetoothDisallowed = isBluetoothDisallowed(); - PackageManagerService packageManagerService = - (PackageManagerService) ServiceManager.getService("package"); - if (packageManagerService != null && !packageManagerService.isOnlyCoreApps()) { - updateOppLauncherComponentState(isBluetoothDisallowed); - } if (isBluetoothDisallowed) { return; } @@ -2063,21 +2060,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not - * offered to the user if Bluetooth is disallowed. Puts the component to its default state if - * Bluetooth is not disallowed. + * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default + * state if Bluetooth is not disallowed. * - * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user - * restriction was set. + * @param userId user to disable bluetooth sharing for. + * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed. */ - private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) { final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); - final int newState = bluetoothDisallowed + final int newState = bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; try { - mContext.getPackageManager() - .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + final IPackageManager imp = AppGlobals.getPackageManager(); + imp.setComponentEnabledSetting(oppLauncherComponent, newState, 0 /* flags */, userId); } catch (Exception e) { // The component was not found, do nothing. } -- GitLab From 8d05b43f911c064b32c2eb069831d8144a00703a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Mar 2017 22:53:18 -0700 Subject: [PATCH 0738/1408] Bluetooth: fix comment wording Test: manual Bug: 30622771 Change-Id: I5a589c98553f35248b0d95d332e9f35774075b24 (cherry picked from commit 9c63d28a1fa55e05b36e4fb90eedcda6de409745) --- .../bluetooth/le/AdvertisingSetParameters.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index f5c1f08562f..7e371574246 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -254,10 +254,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be connectable or * non-connectable. - * Legacy advertisements can be both connectable and scannable. Other - * advertisements can be connectable only if not scannable. + * Legacy advertisements must be both connectable and scannable. Nonlegacy + * advertisements can be only scannable or only connectable. * @param connectable Controls whether the advertisment type will be - * connectable (true) or non-connectable (false). + * connectable (true) or nonconnectable (false). */ public Builder setConnectable(boolean connectable) { this.connectable = connectable; @@ -265,11 +265,11 @@ public final class AdvertisingSetParameters implements Parcelable { } /** - * Set whether the advertisement type should be scannable - * Legacy advertisements can be both connectable and scannable. Other - * advertisements can be scannable only if not connectable. + * Set whether the advertisement type should be scannable. + * Legacy advertisements must be both connectable and scannable. Nonlegacy + * advertisements can be only scannable or only connectable. * @param scannable Controls whether the advertisment type will be - * scannable (true) or non-scannable (false). + * scannable (true) or nonscannable (false). */ public Builder setScannable(boolean scannable) { this.scannable = scannable; -- GitLab From a71cf09d7eab4a0986b6547a0842ab30ccec5f0e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 6 Apr 2017 07:22:57 -0700 Subject: [PATCH 0739/1408] Bluetooth 5 fix default AdvertisingSetParameters values Having advertiser be scannable and connectable by default is a bad choice for new advertising set. Bug: 30622771 Test: sl4a Bt5ScanTest Change-Id: I4fc270e78ca4e62d3077c5cd28aa59b0518d2e77 (cherry picked from commit 010cc95583d6b93bdcc9cf7959f8470d109f8a3d) --- .../bluetooth/le/AdvertisingSetParameters.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 7e371574246..4e9fac3ee22 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -241,8 +241,8 @@ public final class AdvertisingSetParameters implements Parcelable { */ public static final class Builder { - private boolean connectable = true; - private boolean scannable = true; + private boolean connectable = false; + private boolean scannable = false; private boolean isLegacy = false; private boolean isAnonymous = false; private boolean includeTxPower = false; @@ -254,10 +254,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be connectable or * non-connectable. - * Legacy advertisements must be both connectable and scannable. Nonlegacy + * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. * @param connectable Controls whether the advertisment type will be - * connectable (true) or nonconnectable (false). + * connectable (true) or non-connectable (false). */ public Builder setConnectable(boolean connectable) { this.connectable = connectable; @@ -266,10 +266,10 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set whether the advertisement type should be scannable. - * Legacy advertisements must be both connectable and scannable. Nonlegacy + * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. * @param scannable Controls whether the advertisment type will be - * scannable (true) or nonscannable (false). + * scannable (true) or non-scannable (false). */ public Builder setScannable(boolean scannable) { this.scannable = scannable; -- GitLab From 28b326608ce7b4ed5fed935c085945ea3bb6a164 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 10 Apr 2017 13:45:16 -0700 Subject: [PATCH 0740/1408] Remove enable from PeriodicAdvertisingPariameters (1/2) Instead of setting enable to true, one can just pass null PeriodicAdvertisingParameters and achieve same result when starting the set. Passing the "enable" when updating the parameters make no sense, and might be confusing. Experience with "timeout" field, which was a part of AdvertiseSettings show that merging fields that go into different HCI commands can cause problems during processing, so keep enable as separate field. Test: manual Bug: 30622771 Change-Id: Ida02c59eb8433537179b4d22202fe745f8b4bb3e (cherry picked from commit e6c453d828fec3df82708d2b0c3bb00bd0a0dbf8) --- .../bluetooth/le/BluetoothLeAdvertiser.java | 2 +- .../le/PeriodicAdvertisingParameters.java | 22 ++----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index a9deb752e08..73fc1339afa 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -386,7 +386,7 @@ public final class BluetoothLeAdvertiser { } boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); - if (periodicParameters != null && periodicParameters.getEnable() && !supportPeriodic) { + if (periodicParameters != null && !supportPeriodic) { throw new IllegalArgumentException( "Controller does not support LE Periodic Advertising"); } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index 149540ce0da..8891d2e842d 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -29,27 +29,19 @@ public final class PeriodicAdvertisingParameters implements Parcelable { private static final int INTERVAL_MAX = 80; private static final int INTERVAL_MIN = 65519; - private final boolean enable; private final boolean includeTxPower; private final int interval; - private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) { - this.enable = enable; + private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) { this.includeTxPower = includeTxPower; this.interval = interval; } private PeriodicAdvertisingParameters(Parcel in) { - enable = in.readInt() != 0 ? true : false; includeTxPower = in.readInt() != 0 ? true : false; interval = in.readInt(); } - /** - * Returns whether the periodic advertising shall be enabled. - */ - public boolean getEnable() { return enable; } - /** * Returns whether the TX Power will be included. */ @@ -68,7 +60,6 @@ public final class PeriodicAdvertisingParameters implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(enable ? 1 : 0); dest.writeInt(includeTxPower ? 1 : 0); dest.writeInt(interval); } @@ -89,17 +80,8 @@ public final class PeriodicAdvertisingParameters implements Parcelable { public static final class Builder { private boolean includeTxPower = false; - private boolean enable = false; private int interval = INTERVAL_MAX; - /** - * Set whether the Periodic Advertising should be enabled for this set. - */ - public Builder setEnable(boolean enable) { - this.enable = enable; - return this; - } - /** * Whether the transmission power level should be included in the periodic * packet. @@ -128,7 +110,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public PeriodicAdvertisingParameters build() { - return new PeriodicAdvertisingParameters(enable, includeTxPower, interval); + return new PeriodicAdvertisingParameters(includeTxPower, interval); } } } -- GitLab From 9582b46a4b3b91c36295c9d771a9d5cc07fb10e8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 12 Apr 2017 08:51:22 -0700 Subject: [PATCH 0741/1408] Bluetooth 5 PHY simplification Having PHY_LE_* constants defined in four different places, with one value being different than others is misleading. Leave just PHY_LE_* definitions in BluetoothDevice, and add PHY_LE*_MASK for the mask used in PHY update API. This patch also removes need to translate PHY value between PHY update request and event, as mask is used for request, and the value is returned in event. Bug: 30622771 Test: manual Change-Id: I897effa1204a024465d55501c83c542566c4d37c --- .../android/bluetooth/BluetoothDevice.java | 28 +++++++++----- .../java/android/bluetooth/BluetoothGatt.java | 8 ++-- .../bluetooth/BluetoothGattServer.java | 8 ++-- .../le/AdvertisingSetParameters.java | 38 +++++++------------ .../bluetooth/le/BluetoothLeAdvertiser.java | 7 ++-- .../java/android/bluetooth/le/ScanResult.java | 25 +++--------- .../android/bluetooth/le/ScanSettings.java | 15 ++------ 7 files changed, 52 insertions(+), 77 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cb6fa052dda..e6cebc0819f 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -593,24 +593,34 @@ public final class BluetoothDevice implements Parcelable { public static final int TRANSPORT_LE = 2; /** - * 1M initiating PHY. + * Bluetooth LE 1M PHY. */ public static final int PHY_LE_1M = 1; /** - * 2M initiating PHY. + * Bluetooth LE 2M PHY. */ public static final int PHY_LE_2M = 2; /** - * LE Coded initiating PHY. + * Bluetooth LE Coded PHY. */ - public static final int PHY_LE_CODED = 4; + public static final int PHY_LE_CODED = 3; /** - * Any LE PHY. + * Bluetooth LE 1M PHY mask. */ - public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED; + public static final int PHY_LE_1M_MASK = 1; + + /** + * Bluetooth LE 2M PHY mask. + */ + public static final int PHY_LE_2M_MASK = 2; + + /** + * Bluetooth LE Coded PHY mask. + */ + public static final int PHY_LE_CODED_MASK = 4; /** * No preferred coding when transmitting on the LE Coded PHY. @@ -1651,7 +1661,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK)); } /** @@ -1668,8 +1678,8 @@ public final class BluetoothDevice implements Parcelable { * {@link BluetoothDevice#TRANSPORT_AUTO} or * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, - * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if * {@code autoConnect} is set to true. * @throws IllegalArgumentException if callback is null */ diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 02ba403adf8..1a969b9ca72 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -785,11 +785,11 @@ public final class BluetoothGatt implements BluetoothProfile { * if no PHY change happens. It is also triggered when remote device updates the PHY. * * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 2df2ed8ff86..eddc278851f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -558,11 +558,11 @@ public final class BluetoothGattServer implements BluetoothProfile { * * @param device The remote device to send this response to * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 4e9fac3ee22..31d8f482090 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; @@ -29,21 +30,6 @@ import android.os.Parcelable; */ public final class AdvertisingSetParameters implements Parcelable { - /** - * 1M advertiser PHY. - */ - public static final int PHY_LE_1M = 1; - - /** - * 2M advertiser PHY. - */ - public static final int PHY_LE_2M = 2; - - /** - * LE Coded advertiser PHY. - */ - public static final int PHY_LE_CODED = 3; - /** * Advertise on low frequency, around every 1000ms. This is the default and * preferred advertising mode as it consumes the least power. @@ -246,8 +232,8 @@ public final class AdvertisingSetParameters implements Parcelable { private boolean isLegacy = false; private boolean isAnonymous = false; private boolean includeTxPower = false; - private int primaryPhy = PHY_LE_1M; - private int secondaryPhy = PHY_LE_1M; + private int primaryPhy = BluetoothDevice.PHY_LE_1M; + private int secondaryPhy = BluetoothDevice.PHY_LE_1M; private int interval = INTERVAL_LOW; private int txPowerLevel = TX_POWER_MEDIUM; @@ -321,12 +307,13 @@ public final class AdvertisingSetParameters implements Parcelable { * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is * supported on this device. * @param primaryPhy Primary advertising physical channel, can only be - * {@link AdvertisingSetParameters#PHY_LE_1M} or - * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M} or + * {@link BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the primaryPhy is invalid. */ public Builder setPrimaryPhy(int primaryPhy) { - if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) { + if (primaryPhy != BluetoothDevice.PHY_LE_1M && + primaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); } this.primaryPhy = primaryPhy; @@ -343,14 +330,15 @@ public final class AdvertisingSetParameters implements Parcelable { * supported on this device. * * @param secondaryPhy Secondary advertising physical channel, can only be - * one of {@link AdvertisingSetParameters#PHY_LE_1M}, - * {@link AdvertisingSetParameters#PHY_LE_2M} or - * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * one of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M} or + * {@link BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the secondaryPhy is invalid. */ public Builder setSecondaryPhy(int secondaryPhy) { - if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M && - secondaryPhy != PHY_LE_CODED) { + if (secondaryPhy != BluetoothDevice.PHY_LE_1M && + secondaryPhy != BluetoothDevice.PHY_LE_2M && + secondaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); } this.secondaryPhy = secondaryPhy; diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 73fc1339afa..ea3031b2017 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; @@ -363,12 +364,12 @@ public final class BluetoothLeAdvertiser { boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported(); int pphy = parameters.getPrimaryPhy(); int sphy = parameters.getSecondaryPhy(); - if (pphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) { + if (pphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) { throw new IllegalArgumentException("Unsupported primary PHY selected"); } - if ((sphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) - || (sphy == AdvertisingSetParameters.PHY_LE_2M && !support2MPhy)) { + if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) + || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) { throw new IllegalArgumentException("Unsupported secondary PHY selected"); } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 745cd16c78f..5b2fa406cd3 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -46,21 +46,6 @@ public final class ScanResult implements Parcelable { */ public static final int PHY_UNUSED = 0x00; - /** - * Bluetooth LE 1Mbit advertiser PHY. - */ - public static final int PHY_LE_1M = 0x01; - - /** - * Bluetooth LE 2Mbit advertiser PHY. - */ - public static final int PHY_LE_2M = 0x02; - - /** - * Bluetooth LE Coded advertiser PHY. - */ - public static final int PHY_LE_CODED = 0x03; - /** * Advertising Set ID is not present in the packet. */ @@ -112,7 +97,7 @@ public final class ScanResult implements Parcelable { mRssi = rssi; mTimestampNanos = timestampNanos; mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK; - mPrimaryPhy = PHY_LE_1M; + mPrimaryPhy = BluetoothDevice.PHY_LE_1M; mSecondaryPhy = PHY_UNUSED; mAdvertisingSid = SID_NOT_PRESENT; mTxPower = 127; @@ -256,16 +241,16 @@ public final class ScanResult implements Parcelable { /** * Returns the primary Physical Layer * on which this advertisment was received. - * Can be one of {@link ScanResult#PHY_LE_1M} or - * {@link ScanResult#PHY_LE_CODED}. + * Can be one of {@link BluetoothDevice#PHY_LE_1M} or + * {@link BluetoothDevice#PHY_LE_CODED}. */ public int getPrimaryPhy() { return mPrimaryPhy; } /** * Returns the secondary Physical Layer * on which this advertisment was received. - * Can be one of {@link ScanResult#PHY_LE_1M}, - * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED} + * Can be one of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED} * or {@link ScanResult#PHY_UNUSED} - if the advertisement * was not received on a secondary physical channel. */ diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 69c9a8cece3..36e48e9bd0b 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.SystemApi; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; @@ -122,16 +123,6 @@ public final class ScanSettings implements Parcelable { @SystemApi public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; - /** - * Use the Bluetooth LE 1Mbit PHY for scanning. - */ - public static final int PHY_LE_1M = 1; - - /** - * Use Bluetooth LE Coded PHY for scanning. - */ - public static final int PHY_LE_CODED = 3; - /** * Use all supported PHYs for scanning. * This will check the controller capabilities, and start @@ -412,8 +403,8 @@ public final class ScanSettings implements Parcelable { * Selecting an unsupported phy will result in failure to start scan. * * @param phy Can be one of - * {@link ScanSettings#PHY_LE_1M}, - * {@link ScanSettings#PHY_LE_CODED} or + * {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_CODED} or * {@link ScanSettings#PHY_LE_ALL_SUPPORTED} */ public Builder setPhy(int phy) { -- GitLab From a80fe2b51ea131b4dde36f12f18bb1beba83a66d Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 17 Nov 2016 16:19:43 -0800 Subject: [PATCH 0742/1408] Add developer setting and system API for inband ringing support * Add 1 system API to check inband ringing flag in config.xml static isInbandRingingSupported(Context) * Add developer menu options to enable this feature Bug: 19171297 Test: mm -j 40, HFP regression testing, testplans/82144 Change-Id: Iaf56ea41911f546bbc7ae1f82e399d0f8d48f75f (cherry picked from commit e86bdcaed1372aa05bdfba175007b05613aecd9b) --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 28421ebc4cc..8519dbaadad 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -1010,6 +1010,18 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * check if in-band ringing is supported for this platform. + * + * @return true if in-band ringing is supported + * false if in-band ringing is not supported + * @hide + */ + public static boolean isInbandRingingSupported(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); + } + /** * Send Headset the BIND response from AG to report change in the status of the * HF indicators to the headset -- GitLab From 20456fc92659d61f6eecc7a075f05dd42cd3a84a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 12 Apr 2017 08:51:22 -0700 Subject: [PATCH 0743/1408] Bluetooth 5 PHY simplification Having PHY_LE_* constants defined in four different places, with one value being different than others is misleading. Leave just PHY_LE_* definitions in BluetoothDevice, and add PHY_LE*_MASK for the mask used in PHY update API. This patch also removes need to translate PHY value between PHY update request and event, as mask is used for request, and the value is returned in event. Bug: 30622771 Test: manual Change-Id: I897effa1204a024465d55501c83c542566c4d37c (cherry picked from commit 9e377194e35c0fb9ac5771f5658c095ed97e0838) --- .../android/bluetooth/BluetoothDevice.java | 28 +++++++++----- .../java/android/bluetooth/BluetoothGatt.java | 8 ++-- .../bluetooth/BluetoothGattServer.java | 8 ++-- .../le/AdvertisingSetParameters.java | 38 +++++++------------ .../bluetooth/le/BluetoothLeAdvertiser.java | 7 ++-- .../java/android/bluetooth/le/ScanResult.java | 25 +++--------- .../android/bluetooth/le/ScanSettings.java | 15 ++------ 7 files changed, 52 insertions(+), 77 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cb6fa052dda..e6cebc0819f 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -593,24 +593,34 @@ public final class BluetoothDevice implements Parcelable { public static final int TRANSPORT_LE = 2; /** - * 1M initiating PHY. + * Bluetooth LE 1M PHY. */ public static final int PHY_LE_1M = 1; /** - * 2M initiating PHY. + * Bluetooth LE 2M PHY. */ public static final int PHY_LE_2M = 2; /** - * LE Coded initiating PHY. + * Bluetooth LE Coded PHY. */ - public static final int PHY_LE_CODED = 4; + public static final int PHY_LE_CODED = 3; /** - * Any LE PHY. + * Bluetooth LE 1M PHY mask. */ - public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED; + public static final int PHY_LE_1M_MASK = 1; + + /** + * Bluetooth LE 2M PHY mask. + */ + public static final int PHY_LE_2M_MASK = 2; + + /** + * Bluetooth LE Coded PHY mask. + */ + public static final int PHY_LE_CODED_MASK = 4; /** * No preferred coding when transmitting on the LE Coded PHY. @@ -1651,7 +1661,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M)); + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK)); } /** @@ -1668,8 +1678,8 @@ public final class BluetoothDevice implements Parcelable { * {@link BluetoothDevice#TRANSPORT_AUTO} or * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, - * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if * {@code autoConnect} is set to true. * @throws IllegalArgumentException if callback is null */ diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 5d1e8ec58ce..4aaf6bd3eb1 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -785,11 +785,11 @@ public final class BluetoothGatt implements BluetoothProfile { * if no PHY change happens. It is also triggered when remote device updates the PHY. * * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 2df2ed8ff86..eddc278851f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -558,11 +558,11 @@ public final class BluetoothGattServer implements BluetoothProfile { * * @param device The remote device to send this response to * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and - * {@link BluetoothDevice#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 4e9fac3ee22..31d8f482090 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; @@ -29,21 +30,6 @@ import android.os.Parcelable; */ public final class AdvertisingSetParameters implements Parcelable { - /** - * 1M advertiser PHY. - */ - public static final int PHY_LE_1M = 1; - - /** - * 2M advertiser PHY. - */ - public static final int PHY_LE_2M = 2; - - /** - * LE Coded advertiser PHY. - */ - public static final int PHY_LE_CODED = 3; - /** * Advertise on low frequency, around every 1000ms. This is the default and * preferred advertising mode as it consumes the least power. @@ -246,8 +232,8 @@ public final class AdvertisingSetParameters implements Parcelable { private boolean isLegacy = false; private boolean isAnonymous = false; private boolean includeTxPower = false; - private int primaryPhy = PHY_LE_1M; - private int secondaryPhy = PHY_LE_1M; + private int primaryPhy = BluetoothDevice.PHY_LE_1M; + private int secondaryPhy = BluetoothDevice.PHY_LE_1M; private int interval = INTERVAL_LOW; private int txPowerLevel = TX_POWER_MEDIUM; @@ -321,12 +307,13 @@ public final class AdvertisingSetParameters implements Parcelable { * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is * supported on this device. * @param primaryPhy Primary advertising physical channel, can only be - * {@link AdvertisingSetParameters#PHY_LE_1M} or - * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * {@link BluetoothDevice#PHY_LE_1M} or + * {@link BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the primaryPhy is invalid. */ public Builder setPrimaryPhy(int primaryPhy) { - if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) { + if (primaryPhy != BluetoothDevice.PHY_LE_1M && + primaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); } this.primaryPhy = primaryPhy; @@ -343,14 +330,15 @@ public final class AdvertisingSetParameters implements Parcelable { * supported on this device. * * @param secondaryPhy Secondary advertising physical channel, can only be - * one of {@link AdvertisingSetParameters#PHY_LE_1M}, - * {@link AdvertisingSetParameters#PHY_LE_2M} or - * {@link AdvertisingSetParameters#PHY_LE_CODED}. + * one of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M} or + * {@link BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the secondaryPhy is invalid. */ public Builder setSecondaryPhy(int secondaryPhy) { - if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M && - secondaryPhy != PHY_LE_CODED) { + if (secondaryPhy != BluetoothDevice.PHY_LE_1M && + secondaryPhy != BluetoothDevice.PHY_LE_2M && + secondaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); } this.secondaryPhy = secondaryPhy; diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 73fc1339afa..ea3031b2017 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; @@ -363,12 +364,12 @@ public final class BluetoothLeAdvertiser { boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported(); int pphy = parameters.getPrimaryPhy(); int sphy = parameters.getSecondaryPhy(); - if (pphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) { + if (pphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) { throw new IllegalArgumentException("Unsupported primary PHY selected"); } - if ((sphy == AdvertisingSetParameters.PHY_LE_CODED && !supportCodedPhy) - || (sphy == AdvertisingSetParameters.PHY_LE_2M && !support2MPhy)) { + if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) + || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) { throw new IllegalArgumentException("Unsupported secondary PHY selected"); } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 745cd16c78f..5b2fa406cd3 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -46,21 +46,6 @@ public final class ScanResult implements Parcelable { */ public static final int PHY_UNUSED = 0x00; - /** - * Bluetooth LE 1Mbit advertiser PHY. - */ - public static final int PHY_LE_1M = 0x01; - - /** - * Bluetooth LE 2Mbit advertiser PHY. - */ - public static final int PHY_LE_2M = 0x02; - - /** - * Bluetooth LE Coded advertiser PHY. - */ - public static final int PHY_LE_CODED = 0x03; - /** * Advertising Set ID is not present in the packet. */ @@ -112,7 +97,7 @@ public final class ScanResult implements Parcelable { mRssi = rssi; mTimestampNanos = timestampNanos; mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK; - mPrimaryPhy = PHY_LE_1M; + mPrimaryPhy = BluetoothDevice.PHY_LE_1M; mSecondaryPhy = PHY_UNUSED; mAdvertisingSid = SID_NOT_PRESENT; mTxPower = 127; @@ -256,16 +241,16 @@ public final class ScanResult implements Parcelable { /** * Returns the primary Physical Layer * on which this advertisment was received. - * Can be one of {@link ScanResult#PHY_LE_1M} or - * {@link ScanResult#PHY_LE_CODED}. + * Can be one of {@link BluetoothDevice#PHY_LE_1M} or + * {@link BluetoothDevice#PHY_LE_CODED}. */ public int getPrimaryPhy() { return mPrimaryPhy; } /** * Returns the secondary Physical Layer * on which this advertisment was received. - * Can be one of {@link ScanResult#PHY_LE_1M}, - * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED} + * Can be one of {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED} * or {@link ScanResult#PHY_UNUSED} - if the advertisement * was not received on a secondary physical channel. */ diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 69c9a8cece3..36e48e9bd0b 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.SystemApi; +import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; @@ -122,16 +123,6 @@ public final class ScanSettings implements Parcelable { @SystemApi public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; - /** - * Use the Bluetooth LE 1Mbit PHY for scanning. - */ - public static final int PHY_LE_1M = 1; - - /** - * Use Bluetooth LE Coded PHY for scanning. - */ - public static final int PHY_LE_CODED = 3; - /** * Use all supported PHYs for scanning. * This will check the controller capabilities, and start @@ -412,8 +403,8 @@ public final class ScanSettings implements Parcelable { * Selecting an unsupported phy will result in failure to start scan. * * @param phy Can be one of - * {@link ScanSettings#PHY_LE_1M}, - * {@link ScanSettings#PHY_LE_CODED} or + * {@link BluetoothDevice#PHY_LE_1M}, + * {@link BluetoothDevice#PHY_LE_CODED} or * {@link ScanSettings#PHY_LE_ALL_SUPPORTED} */ public Builder setPhy(int phy) { -- GitLab From f740d1eadc6ed3b0be2087e81f414b77d58effde Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Thu, 13 Apr 2017 17:46:53 -0700 Subject: [PATCH 0744/1408] BLE scan API using PendingIntent This allows apps to listen for beacons, etc., without having to run a foreground service and register a callback. They can instead register a PendingIntent which will be fired when scan results are available or when an error occurs. Bug: 37254611 Test: WIP Change-Id: I1793eee67ff0211370ed6fc38be4d95a4c5853f5 --- .../android/bluetooth/IBluetoothGatt.aidl | 4 + .../bluetooth/le/BluetoothLeScanner.java | 124 ++++++++++++++---- .../android/bluetooth/le/ScanCallback.java | 2 + 3 files changed, 105 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 334e88b69fd..582709c3ba4 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -16,6 +16,7 @@ package android.bluetooth; +import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattService; import android.bluetooth.le.AdvertiseSettings; @@ -47,6 +48,9 @@ interface IBluetoothGatt { void unregisterScanner(in int scannerId); void startScan(in int scannerId, in ScanSettings settings, in List filters, in WorkSource workSource, in List scanStorages, in String callingPackage); + void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List filters, + in String callingPackage); + void stopScanForIntent(in PendingIntent intent, in String callingPackage); void stopScan(in int scannerId); void flushPendingBatchResults(in int scannerId); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index b63c614711e..b65a7ad0c01 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -17,17 +17,18 @@ package android.bluetooth.le; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityThread; +import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; -import android.bluetooth.le.IScannerCallback; import android.os.Handler; import android.os.Looper; -import android.os.ParcelUuid; import android.os.RemoteException; import android.os.WorkSource; import android.util.Log; @@ -36,7 +37,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An @@ -57,6 +57,27 @@ public final class BluetoothLeScanner { private static final boolean DBG = true; private static final boolean VDBG = false; + /** + * Extra containing a list of ScanResults. It can have one or more results if there was no + * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this + * extra will not be available. + */ + public static final String EXTRA_LIST_SCAN_RESULT + = "android.bluetooth.le.extra.LIST_SCAN_RESULT"; + + /** + * Optional extra indicating the error code, if any. The error code will be one of the + * SCAN_FAILED_* codes in {@link ScanCallback}. + */ + public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE"; + + /** + * Optional extra indicating the callback type, which will be one of + * ScanSettings.CALLBACK_TYPE_*. + * @see ScanCallback#onScanResult(int, ScanResult) + */ + public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; + private final IBluetoothManager mBluetoothManager; private final Handler mHandler; private BluetoothAdapter mBluetoothAdapter; @@ -110,7 +131,27 @@ public final class BluetoothLeScanner { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { - startScan(filters, settings, null, callback, null); + startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); + } + + /** + * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via + * the PendingIntent. Use this method of scanning if your process is not always running and it + * should be started when scan results are available. + * + * @param filters Optional list of ScanFilters for finding exact BLE devices. + * @param settings Optional settings for the scan. + * @param callbackIntent The PendingIntent to deliver the result to. + * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request + * could not be sent. + * @see #stopScan(PendingIntent) + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public int startScan(@Nullable List filters, @Nullable ScanSettings settings, + @NonNull PendingIntent callbackIntent) { + return startScan(filters, + settings != null ? settings : new ScanSettings.Builder().build(), + null, null, callbackIntent, null); } /** @@ -145,23 +186,23 @@ public final class BluetoothLeScanner { Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) public void startScanFromSource(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback) { - startScan(filters, settings, workSource, callback, null); + startScan(filters, settings, workSource, callback, null, null); } - private void startScan(List filters, ScanSettings settings, + private int startScan(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, + final PendingIntent callbackIntent, List> resultStorages) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { + if (callback == null && callbackIntent == null) { throw new IllegalArgumentException("callback is null"); } if (settings == null) { throw new IllegalArgumentException("settings is null"); } synchronized (mLeScanClients) { - if (mLeScanClients.containsKey(callback)) { + if (callback != null && mLeScanClients.containsKey(callback)) { postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); - return; } IBluetoothGatt gatt; try { @@ -170,28 +211,34 @@ public final class BluetoothLeScanner { gatt = null; } if (gatt == null) { - postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); - return; + return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); } if (!isSettingsConfigAllowedForScan(settings)) { - postCallbackError(callback, - ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); - return; + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); } if (!isHardwareResourcesAvailableForScan(settings)) { - postCallbackError(callback, - ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); - return; + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); } if (!isSettingsAndFilterComboAllowed(settings, filters)) { - postCallbackError(callback, + return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); - return; } - BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, workSource, callback, resultStorages); - wrapper.startRegisteration(); + if (callback != null) { + BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, + settings, workSource, callback, resultStorages); + wrapper.startRegistration(); + } else { + try { + gatt.startScanForIntent(callbackIntent, settings, filters, + ActivityThread.currentOpPackageName()); + } catch (RemoteException e) { + return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; + } + } } + return ScanCallback.NO_ERROR; } /** @@ -214,6 +261,25 @@ public final class BluetoothLeScanner { } } + /** + * Stops an ongoing Bluetooth LE scan started using a PendingIntent. + *

        + * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param callbackIntent The PendingIntent that was used to start the scan. + * @see #startScan(List, ScanSettings, PendingIntent) + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public void stopScan(PendingIntent callbackIntent) { + BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName()); + } catch (RemoteException e) { + } + } + /** * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data @@ -252,7 +318,7 @@ public final class BluetoothLeScanner { scanFilters.add(filter.getFilter()); scanStorages.add(filter.getStorageDescriptors()); } - startScan(scanFilters, settings, null, callback, scanStorages); + startScan(scanFilters, settings, null, callback, null, scanStorages); } /** @@ -295,7 +361,7 @@ public final class BluetoothLeScanner { mResultStorages = resultStorages; } - public void startRegisteration() { + public void startRegistration() { synchronized (this) { // Scan stopped. if (mScannerId == -1) return; @@ -399,7 +465,6 @@ public final class BluetoothLeScanner { mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); } }); - } @Override @@ -453,6 +518,15 @@ public final class BluetoothLeScanner { } } + private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) { + if (callback == null) { + return errorCode; + } else { + postCallbackError(callback, errorCode); + return ScanCallback.NO_ERROR; + } + } + private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { @Override diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index 61b2e787c86..aff2e909502 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -50,6 +50,8 @@ public abstract class ScanCallback { */ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; + static final int NO_ERROR = 0; + /** * Callback when a BLE advertisement has been found. * -- GitLab From d5411a839ae615775bce51b4fc95092663bfc930 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 14 Apr 2017 07:21:20 -0700 Subject: [PATCH 0745/1408] Read by UUID for PTS tests (1/5) Add a hidden api for reading characteristic by UUID for PTS. Bug: 35150313 Test: sl4a GattReadTest.byUuid Change-Id: Ice4076d99e4694d20374ba0fdcae74d5ae841147 --- .../java/android/bluetooth/BluetoothGatt.java | 35 +++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 2 ++ 2 files changed, 37 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1a969b9ca72..9144ae72291 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -946,6 +946,41 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Reads the characteristic using its UUID from the associated remote device. + * + *

        This is an asynchronous operation. The result of the read operation + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * callback. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid UUID of characteristic to read from the remote device + * @return true, if the read operation was initiated successfully + * @hide + */ + public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { + if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); + if (mService == null || mClientIf == 0) return false; + + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + + try { + mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), + new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + mDeviceBusy = false; + return false; + } + + return true; + } + + /** * Writes a given characteristic and its values to the associated remote device. * diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 334e88b69fd..38ba9adafe6 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -77,6 +77,8 @@ interface IBluetoothGatt { void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); + void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, + in int startHandle, in int endHandle, in int authReq); void writeCharacteristic(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); -- GitLab From d424d09f116f3c248196a3354a5fd04cf92d9c03 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 19 Apr 2017 06:52:08 -0700 Subject: [PATCH 0746/1408] Expose LE advertiser address for easier PTS tests (1/6) This patchset adds a hidden method getOwnAddress, that lets app with BLUETOOTH_PRIVILEGED permission to lear their own addreess. This is done exclusively for PTS tests. Bug: 35147497 Test: manual Change-Id: Iaf0f2fe0613de44b8430ac25e691d66a4ad44f8d --- .../java/android/bluetooth/IBluetoothGatt.aidl | 1 + .../android/bluetooth/le/AdvertisingSet.java | 18 +++++++++++++++++- .../bluetooth/le/AdvertisingSetCallback.java | 11 +++++++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 11 +++++++++++ .../bluetooth/le/IAdvertisingSetCallback.aidl | 1 + 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 38ba9adafe6..a2066cb4991 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -56,6 +56,7 @@ interface IBluetoothGatt { in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); + void getOwnAddress(in int advertiserId); void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 51571b2746a..3021be1f8c0 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -181,7 +181,23 @@ public final class AdvertisingSet { } /** - * Returns advertiserId associated with thsi advertising set. + * Returns address associated with this advertising set. + * This method is exposed only for Bluetooth PTS tests, no app or system service + * should ever use it. + * + * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. + * @hide + */ + public void getOwnAddress(){ + try { + gatt.getOwnAddress(this.advertiserId); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Returns advertiserId associated with this advertising set. * * @hide */ diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index fe3b1cdd63a..2c46e856db4 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -143,4 +143,15 @@ public abstract class AdvertisingSetCallback { */ public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param addressType type of address. + * @param address advertising set bluetooth address. + * @hide + */ + public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) {} } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ea3031b2017..21e9497daa6 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -544,6 +544,17 @@ public final class BluetoothLeAdvertiser { }); } + @Override + public void onOwnAddressRead(int advertiserId, int addressType, String address) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); + callback.onOwnAddressRead(advertisingSet, addressType, address); + } + }); + } + @Override public void onAdvertisingSetStopped(int advertiserId) { handler.post(new Runnable() { diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl index 2c9f4baad52..3628c775b79 100644 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -21,6 +21,7 @@ package android.bluetooth.le; */ oneway interface IAdvertisingSetCallback { void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); + void onOwnAddressRead(in int advertiserId, in int addressType, in String address); void onAdvertisingSetStopped(in int advertiserId); void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); void onAdvertisingDataSet(in int advertiserId, in int status); -- GitLab From 723ee54c608a9ed16738de6382a06ee281bd5f83 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 17 Apr 2017 22:35:45 -0700 Subject: [PATCH 0747/1408] Allow the Bluetooth MAC address to be updated asynchronously (2/3) There are intermittent issues where either the returned Bluetooth MAC address to Java framework is uninitialized or this address update arrives too late. This fix will do 2 things: (1) Returns error when MAC address is unavailable in the native code. (2) Updates the MAC address later by adding a new broadcast event. Test: Check address for these cases: factory reset, system reboot, and Bluetooth re-enable. Bug: 36709382 Change-Id: I09720193e38fdf9139e1bb146f8e1847e2b65b1a --- .../android/bluetooth/BluetoothAdapter.java | 24 +++++++++++++++++++ .../bluetooth/BluetoothManagerService.java | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 845a47d9984..85636033d81 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -465,6 +465,30 @@ public final class BluetoothAdapter { public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; + /** + * Intent used to broadcast the change in the Bluetooth address + * of the local Bluetooth adapter. + *

        Always contains the extra field {@link + * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address. + * + * Note: only system level processes are allowed to send this + * defined broadcast. + * + * @hide + */ + public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = + "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; + + /** + * Used as a String extra field in {@link + * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local + * Bluetooth address. + * + * @hide + */ + public static final String EXTRA_BLUETOOTH_ADDRESS = + "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; + /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be * by BLE Always on enabled application to know the ACL_CONNECTED event diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index de4734f02d0..5eb4db408a6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -307,6 +307,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } + } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) { + String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS); + if (newAddress != null) { + if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); + storeNameAndAddress(null, newAddress); + } else { + if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); + } } } }; @@ -343,6 +351,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); + filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); -- GitLab From 0e00a22d944036df3200df1afc86e9c3e8cac424 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 04:59:55 -0700 Subject: [PATCH 0748/1408] Bluetooth: improve getLeMaximumAdvertisingDataLength comment Bug: 37534792 Test: none Change-Id: Ieff71356aceb0e2a4c6e81d9053f854448e0c927 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 85636033d81..735d84e72b6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1508,8 +1508,8 @@ public final class BluetoothAdapter { } /** - * Return the maximum LE advertising data length, - * if LE Extended Advertising feature is supported. + * Return the maximum LE advertising data length in bytes, + * if LE Extended Advertising feature is supported, 0 otherwise. * * @return the maximum LE advertising data length. */ -- GitLab From dc3abd6c94df6896f5247e3951be4b67ac629996 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 04:59:55 -0700 Subject: [PATCH 0749/1408] Bluetooth: improve getLeMaximumAdvertisingDataLength comment Bug: 37534792 Test: none Change-Id: Ieff71356aceb0e2a4c6e81d9053f854448e0c927 (cherry picked from commit 4634b5cd27b274105e550f58f9d5fc51c79eb3fb) --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 845a47d9984..1aca1d14e28 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1484,8 +1484,8 @@ public final class BluetoothAdapter { } /** - * Return the maximum LE advertising data length, - * if LE Extended Advertising feature is supported. + * Return the maximum LE advertising data length in bytes, + * if LE Extended Advertising feature is supported, 0 otherwise. * * @return the maximum LE advertising data length. */ -- GitLab From cf3818675f65d8c1901b3d33eab5db1cabb74ea7 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 17 Apr 2017 22:35:45 -0700 Subject: [PATCH 0750/1408] Allow the Bluetooth MAC address to be updated asynchronously (2/3) There are intermittent issues where either the returned Bluetooth MAC address to Java framework is uninitialized or this address update arrives too late. This fix will do 2 things: (1) Returns error when MAC address is unavailable in the native code. (2) Updates the MAC address later by adding a new broadcast event. Test: Check address for these cases: factory reset, system reboot, and Bluetooth re-enable. Bug: 36709382 Change-Id: I09720193e38fdf9139e1bb146f8e1847e2b65b1a (cherry picked from commit ad4d1d8e28618546953e75d4983335631feb6f2a) --- .../android/bluetooth/BluetoothAdapter.java | 24 +++++++++++++++++++ .../bluetooth/BluetoothManagerService.java | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 845a47d9984..85636033d81 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -465,6 +465,30 @@ public final class BluetoothAdapter { public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; + /** + * Intent used to broadcast the change in the Bluetooth address + * of the local Bluetooth adapter. + *

        Always contains the extra field {@link + * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address. + * + * Note: only system level processes are allowed to send this + * defined broadcast. + * + * @hide + */ + public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = + "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; + + /** + * Used as a String extra field in {@link + * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local + * Bluetooth address. + * + * @hide + */ + public static final String EXTRA_BLUETOOTH_ADDRESS = + "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; + /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be * by BLE Always on enabled application to know the ACL_CONNECTED event diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6c4895c91f2..6c18b260e18 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -308,6 +308,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } + } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) { + String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS); + if (newAddress != null) { + if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); + storeNameAndAddress(null, newAddress); + } else { + if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); + } } } }; @@ -343,6 +351,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); + filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); -- GitLab From fbe8b2299f543aee40d44794f7e68a0abbea112c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 21 Apr 2017 16:29:27 -0600 Subject: [PATCH 0751/1408] More auto-doc work. Add support for AnyThread, CallSuper, and UiThread. Another related CL started documenting @RequiresPermission, so remove duplicated information in existing APIs. Suppress auto-doc on a handful of classes that are already well-documented. Test: make -j32 offline-sdk-docs Bug: 37526420 Change-Id: I791437dccec0f11d5349a23b982ba098cb551af8 --- .../java/android/bluetooth/BluetoothA2dp.java | 2 -- .../android/bluetooth/BluetoothAdapter.java | 24 ------------------- .../android/bluetooth/BluetoothDevice.java | 11 --------- .../android/bluetooth/BluetoothManager.java | 6 ----- .../android/bluetooth/BluetoothProfile.java | 6 ----- .../bluetooth/le/BluetoothLeScanner.java | 6 ----- 6 files changed, 55 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1ca2be5b2a1..d1ad8de0b11 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -422,8 +422,6 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth device * @return priority of the device * @hide diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 845a47d9984..8bd568e7fb0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -673,7 +673,6 @@ public final class BluetoothAdapter { * Return true if Bluetooth is currently enabled and ready for use. *

        Equivalent to: * getBluetoothState() == STATE_ON - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return true if the local adapter is turned on */ @@ -811,7 +810,6 @@ public final class BluetoothAdapter { * {@link #STATE_TURNING_ON}, * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return current state of Bluetooth adapter */ @@ -854,7 +852,6 @@ public final class BluetoothAdapter { * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, * {@link #STATE_BLE_TURNING_OFF}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return current state of Bluetooth adapter * @hide @@ -910,8 +907,6 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned on - * such as Airplane mode, or the adapter is already turned on. - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission * * @return true to indicate adapter startup has begun, or false on * immediate error @@ -946,8 +941,6 @@ public final class BluetoothAdapter { * #STATE_ON}. If this call returns false then there was an * immediate problem that will prevent the adapter from being turned off - * such as the adapter already being turned off. - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission * * @return true to indicate adapter shutdown has begun, or false on * immediate error @@ -981,7 +974,6 @@ public final class BluetoothAdapter { /** * Returns the hardware address of the local Bluetooth adapter. *

        For example, "00:11:22:AA:BB:CC". - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Bluetooth hardware address as string */ @@ -1085,7 +1077,6 @@ public final class BluetoothAdapter { * will return false. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param name a valid Bluetooth name * @return true if the name was set, false otherwise @@ -1116,7 +1107,6 @@ public final class BluetoothAdapter { * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return scan mode */ @@ -1255,7 +1245,6 @@ public final class BluetoothAdapter { * will return false. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error */ @@ -1275,7 +1264,6 @@ public final class BluetoothAdapter { /** * Cancel the current device discovery process. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. *

        Because discovery is a heavyweight procedure for the Bluetooth * adapter, this method should always be called before attempting to connect * to a remote device with {@link @@ -1319,7 +1307,6 @@ public final class BluetoothAdapter { * will return false. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return true if discovering */ @@ -1586,7 +1573,6 @@ public final class BluetoothAdapter { * will return an empty set. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ @@ -1671,8 +1657,6 @@ public final class BluetoothAdapter { * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. - * *

        Return value can be one of * {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_CONNECTING}, @@ -1762,7 +1746,6 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. *

        Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM BluetoothServerSocket @@ -1794,7 +1777,6 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. *

        Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM BluetoothServerSocket @@ -2386,8 +2368,6 @@ public final class BluetoothAdapter { *

        Results of the scan are reported using the * {@link LeScanCallback#onLeScan} callback. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} @@ -2406,8 +2386,6 @@ public final class BluetoothAdapter { *

        Devices which advertise all specified services are reported using the * {@link LeScanCallback#onLeScan} callback. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param serviceUuids Array of services to look for * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully @@ -2495,8 +2473,6 @@ public final class BluetoothAdapter { /** * Stops an ongoing Bluetooth LE device scan. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param callback used to identify which scan to stop * must be the same handle used to start the scan * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e6cebc0819f..639e0562233 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -762,7 +762,6 @@ public final class BluetoothDevice implements Parcelable { *

        The local adapter will automatically retrieve remote names when * performing a device scan, and will cache them. This method just returns * the name for this device from the cache. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return the Bluetooth name, or null if there was a problem. */ @@ -781,8 +780,6 @@ public final class BluetoothDevice implements Parcelable { /** * Get the Bluetooth device type of the remote device. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} - * * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} * {@link #DEVICE_TYPE_DUAL}. * {@link #DEVICE_TYPE_UNKNOWN} if it's not available @@ -862,7 +859,6 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

        Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return false on immediate error, true if bonding will begin */ @@ -1022,7 +1018,6 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}, * {@link #BOND_BONDING}, * {@link #BOND_BONDED}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return the bond state */ @@ -1089,7 +1084,6 @@ public final class BluetoothDevice implements Parcelable { /** * Get the Bluetooth class of the remote device. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return Bluetooth class object, or null on error */ @@ -1114,7 +1108,6 @@ public final class BluetoothDevice implements Parcelable { * from the remote device. Instead, the local cached copy of the service * UUIDs are returned. *

        Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return the supported features (UUIDs) of the remote device, * or null on error @@ -1140,7 +1133,6 @@ public final class BluetoothDevice implements Parcelable { * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently * present in the cache. Clients should use the {@link #getUuids} to get UUIDs * if service discovery is not to be performed. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return False if the sanity check fails, True if the process * of initiating an ACL connection to the remote device @@ -1221,7 +1213,6 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error @@ -1502,7 +1493,6 @@ public final class BluetoothDevice implements Parcelable { * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. * However if you are connecting to an Android peer then please generate * your own unique UUID. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection @@ -1541,7 +1531,6 @@ public final class BluetoothDevice implements Parcelable { * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. * However if you are connecting to an Android peer then please generate * your own unique UUID. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 29283e793ce..c7191ba2638 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -85,8 +85,6 @@ public final class BluetoothManager { * This can be used by applications like status bar which would just like * to know the state of Bluetooth. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Remote bluetooth device. * @param profile GATT or GATT_SERVER * @return State of the profile connection. One of @@ -118,8 +116,6 @@ public final class BluetoothManager { * This can be used by applications like status bar which would just like * to know the state of Bluetooth. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param profile GATT or GATT_SERVER * @return List of devices. The list will be empty on error. */ @@ -159,8 +155,6 @@ public final class BluetoothManager { * This can be used by applications like status bar which would just like * to know the state of the local adapter. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param profile GATT or GATT_SERVER * @param states Array of states. States can be one of * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 2f64c719ec1..c5b58e97e52 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -187,8 +187,6 @@ public interface BluetoothProfile { * *

        Return the set of devices which are in state {@link #STATE_CONNECTED} * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return List of devices. The list will be empty on error. */ @RequiresPermission(Manifest.permission.BLUETOOTH) @@ -201,8 +199,6 @@ public interface BluetoothProfile { *

        If none of the devices match any of the given states, * an empty list will be returned. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param states Array of states. States can be one of * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, @@ -214,8 +210,6 @@ public interface BluetoothProfile { /** * Get the current connection state of the profile * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Remote bluetooth device. * @return State of the profile connection. One of * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index b65a7ad0c01..8d2c3ec92c0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -100,7 +100,6 @@ public final class BluetoothLeScanner { * Start Bluetooth LE scan with default parameters and no filters. The scan results will be * delivered through {@code callback}. *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * An app must hold * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission @@ -117,7 +116,6 @@ public final class BluetoothLeScanner { /** * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * An app must hold * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission @@ -243,8 +241,6 @@ public final class BluetoothLeScanner { /** * Stops an ongoing Bluetooth LE scan. - *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param callback */ @@ -263,8 +259,6 @@ public final class BluetoothLeScanner { /** * Stops an ongoing Bluetooth LE scan started using a PendingIntent. - *

        - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param callbackIntent The PendingIntent that was used to start the scan. * @see #startScan(List, ScanSettings, PendingIntent) -- GitLab From 122ce544809f632e57eb61ae4db5a918b9afb930 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 19 Apr 2017 05:44:34 -0700 Subject: [PATCH 0752/1408] Get rid of the IAdvertiserCallabck Bug: 30622771 Test: none Change-Id: I2c55f96e71cd3be67f88201ab07c91730cd85971 --- .../android/bluetooth/IBluetoothGatt.aidl | 1 - .../bluetooth/le/BluetoothLeAdvertiser.java | 1 - .../bluetooth/le/IAdvertiserCallback.aidl | 29 ------------------- 3 files changed, 31 deletions(-) delete mode 100644 framework/java/android/bluetooth/le/IAdvertiserCallback.aidl diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index a2066cb4991..fb6b89341db 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -31,7 +31,6 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; -import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 21e9497daa6..67d56d5060e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -22,7 +22,6 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; -import android.bluetooth.le.IAdvertiserCallback; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl b/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl deleted file mode 100644 index c58b1dfec96..00000000000 --- a/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.le; - -import android.bluetooth.le.AdvertiseSettings; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IAdvertiserCallback { - void onAdvertiserRegistered(in int status, in int advertiserId); - - void onMultiAdvertiseCallback(in int status, boolean isStart, - in AdvertiseSettings advertiseSettings); -} -- GitLab From 7e2e61ee2e3f46fa0e4bee82028b36a0f2aa22d5 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 17 Apr 2017 20:42:22 -0700 Subject: [PATCH 0753/1408] Limit btsnoop file size (3/8) Limit btsnoop file size by rotating between snoop files. The rotation occurrs when a fixed number of packets have been logged and will start overwriting the older file. Bug: 35998031 Test: Enable snoop logs from developer options and let logs get large Change-Id: I40d5da4c1b1c9b45908e5790d130f1c5e804c773 --- .../android/bluetooth/BluetoothAdapter.java | 22 ------------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - .../bluetooth/BluetoothInstrumentation.java | 8 ------- .../bluetooth/BluetoothManagerService.java | 10 --------- 4 files changed, 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 735d84e72b6..ff52f27447c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1031,28 +1031,6 @@ public final class BluetoothAdapter { return null; } - /** - * enable or disable Bluetooth HCI snoop log. - * - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * - * @return true to indicate configure HCI log successfully, or false on - * immediate error - * @hide - */ - public boolean configHciSnoopLog(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.configHciSnoopLog(enable); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - /** * Factory reset bluetooth settings. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index b33781729b6..43c5ae4407c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -97,7 +97,6 @@ interface IBluetooth ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - boolean configHciSnoopLog(boolean enable); boolean factoryReset(); boolean isMultiAdvertisementSupported(); diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 411a3f89496..37b2a50ed67 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -72,8 +72,6 @@ public class BluetoothInstrumentation extends Instrumentation { getAddress(); } else if ("getBondedDevices".equals(command)) { getBondedDevices(); - } else if ("enableBtSnoop".equals(command)) { - enableBtSnoop(); } else { finish(null); } @@ -116,12 +114,6 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } - public void enableBtSnoop() { - Assert.assertTrue("failed to enable snoop log", - getBluetoothAdapter().configHciSnoopLog(true)); - finish(mSuccessResult); - } - public void finish(Bundle result) { if (result == null) { result = new Bundle(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 18b4571ca3e..c785fb91e8f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1494,16 +1494,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mGetNameAddressOnly) return; } - try { - boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); - if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) { - Slog.e(TAG,"IBluetooth.configHciSnoopLog return false"); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call configHciSnoopLog", e); - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From b03eae11345c243bca765ccc315c18d78d27e006 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Mon, 24 Apr 2017 11:00:05 -0700 Subject: [PATCH 0754/1408] Add link to class with constants Addresses api-council feedback Change-Id: I2dcd526a266a2320b34c714fd91fb4a83d382d9c Fixes: 37536012 Test: make update-api --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 8d2c3ec92c0..52465137dfd 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -73,7 +73,7 @@ public final class BluetoothLeScanner { /** * Optional extra indicating the callback type, which will be one of - * ScanSettings.CALLBACK_TYPE_*. + * CALLBACK_TYPE_* constants in {@link ScanSettings}. * @see ScanCallback#onScanResult(int, ScanResult) */ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; -- GitLab From f2b84e7d60c8eee3ef2cff0f8bdecabdd7209234 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 03:49:00 -0700 Subject: [PATCH 0755/1408] Bluetooth: Add handler parameter to connectGatt Bug: 37544152 Test: sl4a GattReadTest GattWriteTest Change-Id: I043dfefaafe9f3700418f2c855c52aac3644310f --- .../android/bluetooth/BluetoothDevice.java | 43 ++++- .../java/android/bluetooth/BluetoothGatt.java | 168 ++++++++++-------- 2 files changed, 136 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e6cebc0819f..eaba3352ac0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -22,6 +22,8 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelUuid; @@ -1679,12 +1681,45 @@ public final class BluetoothDevice implements Parcelable { * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if - * {@code autoConnect} is set to true. - * @throws IllegalArgumentException if callback is null + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy) { + return connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK, null); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen + * on the service's main thread. + * @throws NullPointerException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport, int phy, + Handler handler) { + if (callback == null) + throw new NullPointerException("callback is null"); + + if (handler == null) + handler = new Handler(Looper.getMainLooper()); + // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1696,7 +1731,7 @@ public final class BluetoothDevice implements Parcelable { return null; } BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); - gatt.connect(autoConnect, callback); + gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 9144ae72291..7806e31c418 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.content.Context; +import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -43,6 +44,7 @@ public final class BluetoothGatt implements BluetoothProfile { private IBluetoothGatt mService; private BluetoothGattCallback mCallback; + private Handler mHandler; private int mClientIf; private BluetoothDevice mDevice; private boolean mAutoConnect; @@ -154,8 +156,14 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + } + }); + synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; } @@ -181,11 +189,12 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - try { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } + }); } /** @@ -200,11 +209,12 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - try { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } + }); } /** @@ -221,11 +231,13 @@ public final class BluetoothGatt implements BluetoothProfile { } int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; - try { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); + } + }); synchronized(mStateLock) { if (connected) { @@ -279,11 +291,12 @@ public final class BluetoothGatt implements BluetoothProfile { } } - try { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onServicesDiscovered(BluetoothGatt.this, status); + } + }); } /** @@ -328,11 +341,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); - try { - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); + } + }); } /** @@ -373,11 +387,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); + } + }); } /** @@ -398,11 +413,12 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); - try { - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + } + }); } /** @@ -442,11 +458,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + } + }); } /** @@ -485,11 +502,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + } + }); } /** @@ -508,11 +526,12 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - try { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + } + }); } /** @@ -526,11 +545,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + } + }); } /** @@ -544,11 +564,13 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + } + }); } /** @@ -564,12 +586,14 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } + }); } }; @@ -659,11 +683,12 @@ public final class BluetoothGatt implements BluetoothProfile { * @return If true, the callback will be called to notify success or failure, * false on immediate error */ - private boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallback callback, Handler handler) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; mCallback = callback; + mHandler = handler; UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); @@ -716,7 +741,8 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, + Handler handler) { if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); synchronized(mStateLock) { if (mConnState != CONN_STATE_IDLE) { @@ -727,7 +753,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAutoConnect = autoConnect; - if (!registerApp(callback)) { + if (!registerApp(callback, handler)) { synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; } -- GitLab From 23ec4998d1a16c6f27fb3e5fb8b88cdc7ce8157c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 25 Apr 2017 08:07:11 -0700 Subject: [PATCH 0756/1408] Bluetooth: document status value in PHY read/update Bug: 37544152 Test: none Change-Id: I2bac468f833c73cd282b1dac80b6553917996604 --- framework/java/android/bluetooth/BluetoothGattCallback.java | 6 ++++-- .../java/android/bluetooth/BluetoothGattServerCallback.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 11a15c66385..c6f82ffb7ae 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -30,7 +30,8 @@ public abstract class BluetoothGattCallback{ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation + * @param status Status of the PHY update operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } @@ -43,7 +44,8 @@ public abstract class BluetoothGattCallback{ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation + * @param status Status of the PHY read operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 3b8f962bf73..02307bd9ef9 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -167,7 +167,8 @@ public abstract class BluetoothGattServerCallback { * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation + * @param status Status of the PHY update operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } @@ -180,7 +181,8 @@ public abstract class BluetoothGattServerCallback { * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation + * @param status Status of the PHY read operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } -- GitLab From e76d69c1b9b7cf97a9d72609145a4299deeaf315 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 04:26:12 -0700 Subject: [PATCH 0757/1408] Bluetooth: expand comments on new PHY constants Bug: 37544152 Test: the all-knowing eyes of the reviewers Change-Id: I8f5a756a204fe90960700e1dc42f64824d3b844a --- .../android/bluetooth/BluetoothDevice.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index eaba3352ac0..e0b03d22abe 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -595,32 +595,38 @@ public final class BluetoothDevice implements Parcelable { public static final int TRANSPORT_LE = 2; /** - * Bluetooth LE 1M PHY. + * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or + * connection. */ public static final int PHY_LE_1M = 1; /** - * Bluetooth LE 2M PHY. + * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or + * connection. */ public static final int PHY_LE_2M = 2; /** - * Bluetooth LE Coded PHY. + * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning + * or connection. */ public static final int PHY_LE_CODED = 3; /** - * Bluetooth LE 1M PHY mask. + * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available + * options in a bitmask. */ public static final int PHY_LE_1M_MASK = 1; /** - * Bluetooth LE 2M PHY mask. + * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available + * options in a bitmask. */ public static final int PHY_LE_2M_MASK = 2; /** - * Bluetooth LE Coded PHY mask. + * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many + * available options in a bitmask. */ public static final int PHY_LE_CODED_MASK = 4; -- GitLab From d14556208be7fef556ad80470755002897eaf97c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 19 Apr 2017 05:44:34 -0700 Subject: [PATCH 0758/1408] Get rid of the IAdvertiserCallabck Bug: 30622771 Test: none Change-Id: I2c55f96e71cd3be67f88201ab07c91730cd85971 (cherry picked from commit 89e10d3eb4a4728ffdb5a5eba76d772d28bcb855) --- .../android/bluetooth/IBluetoothGatt.aidl | 1 - .../bluetooth/le/BluetoothLeAdvertiser.java | 1 - .../bluetooth/le/IAdvertiserCallback.aidl | 29 ------------------- 3 files changed, 31 deletions(-) delete mode 100644 framework/java/android/bluetooth/le/IAdvertiserCallback.aidl diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 582709c3ba4..12e9baaded9 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -32,7 +32,6 @@ import android.os.WorkSource; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; -import android.bluetooth.le.IAdvertiserCallback; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.IPeriodicAdvertisingCallback; import android.bluetooth.le.IScannerCallback; diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ea3031b2017..0c7958dc9a3 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -22,7 +22,6 @@ import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; -import android.bluetooth.le.IAdvertiserCallback; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl b/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl deleted file mode 100644 index c58b1dfec96..00000000000 --- a/framework/java/android/bluetooth/le/IAdvertiserCallback.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.le; - -import android.bluetooth.le.AdvertiseSettings; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IAdvertiserCallback { - void onAdvertiserRegistered(in int status, in int advertiserId); - - void onMultiAdvertiseCallback(in int status, boolean isStart, - in AdvertiseSettings advertiseSettings); -} -- GitLab From e87b3b361e4627d5dff2525adc2c3befa03c6c02 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 03:49:00 -0700 Subject: [PATCH 0759/1408] Bluetooth: Add handler parameter to connectGatt Bug: 37544152 Test: sl4a GattReadTest GattWriteTest Change-Id: I043dfefaafe9f3700418f2c855c52aac3644310f (cherry picked from commit 4eab49652e48159fd37f827d30b822f2f187551e) --- .../android/bluetooth/BluetoothDevice.java | 43 ++++- .../java/android/bluetooth/BluetoothGatt.java | 168 ++++++++++-------- 2 files changed, 136 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 639e0562233..c8d6b190d02 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -22,6 +22,8 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelUuid; @@ -1668,12 +1670,45 @@ public final class BluetoothDevice implements Parcelable { * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if - * {@code autoConnect} is set to true. - * @throws IllegalArgumentException if callback is null + * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy) { + return connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK, null); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen + * on the service's main thread. + * @throws NullPointerException if callback is null + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport, int phy, + Handler handler) { + if (callback == null) + throw new NullPointerException("callback is null"); + + if (handler == null) + handler = new Handler(Looper.getMainLooper()); + // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1685,7 +1720,7 @@ public final class BluetoothDevice implements Parcelable { return null; } BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); - gatt.connect(autoConnect, callback); + gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 4aaf6bd3eb1..713dbf43d33 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.content.Context; +import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -43,6 +44,7 @@ public final class BluetoothGatt implements BluetoothProfile { private IBluetoothGatt mService; private BluetoothGattCallback mCallback; + private Handler mHandler; private int mClientIf; private BluetoothDevice mDevice; private boolean mAutoConnect; @@ -154,8 +156,14 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + } + }); + synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; } @@ -181,11 +189,12 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - try { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } + }); } /** @@ -200,11 +209,12 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - try { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } + }); } /** @@ -221,11 +231,13 @@ public final class BluetoothGatt implements BluetoothProfile { } int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; - try { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); + } + }); synchronized(mStateLock) { if (connected) { @@ -279,11 +291,12 @@ public final class BluetoothGatt implements BluetoothProfile { } } - try { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onServicesDiscovered(BluetoothGatt.this, status); + } + }); } /** @@ -328,11 +341,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); - try { - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); + } + }); } /** @@ -373,11 +387,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); + } + }); } /** @@ -398,11 +413,12 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); - try { - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + } + }); } /** @@ -442,11 +458,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + } + }); } /** @@ -485,11 +502,12 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - try { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + } + }); } /** @@ -508,11 +526,12 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - try { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + } + }); } /** @@ -526,11 +545,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + } + }); } /** @@ -544,11 +564,13 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + } + }); } /** @@ -564,12 +586,14 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - try { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } + }); } }; @@ -659,11 +683,12 @@ public final class BluetoothGatt implements BluetoothProfile { * @return If true, the callback will be called to notify success or failure, * false on immediate error */ - private boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallback callback, Handler handler) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; mCallback = callback; + mHandler = handler; UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); @@ -716,7 +741,8 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, + Handler handler) { if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); synchronized(mStateLock) { if (mConnState != CONN_STATE_IDLE) { @@ -727,7 +753,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAutoConnect = autoConnect; - if (!registerApp(callback)) { + if (!registerApp(callback, handler)) { synchronized(mStateLock) { mConnState = CONN_STATE_IDLE; } -- GitLab From 919323475ad3acbd77d1cecd03a96d4fc2bb995c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 25 Apr 2017 08:07:11 -0700 Subject: [PATCH 0760/1408] Bluetooth: document status value in PHY read/update Bug: 37544152 Test: none Change-Id: I2bac468f833c73cd282b1dac80b6553917996604 (cherry picked from commit e55c4469771f9f88626b362aa7b7226c4f424ed7) --- framework/java/android/bluetooth/BluetoothGattCallback.java | 6 ++++-- .../java/android/bluetooth/BluetoothGattServerCallback.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 11a15c66385..c6f82ffb7ae 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -30,7 +30,8 @@ public abstract class BluetoothGattCallback{ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation + * @param status Status of the PHY update operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } @@ -43,7 +44,8 @@ public abstract class BluetoothGattCallback{ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status status of the operation + * @param status Status of the PHY read operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 3b8f962bf73..02307bd9ef9 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -167,7 +167,8 @@ public abstract class BluetoothGattServerCallback { * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation + * @param status Status of the PHY update operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } @@ -180,7 +181,8 @@ public abstract class BluetoothGattServerCallback { * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status status of the operation + * @param status Status of the PHY read operation. + * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. */ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } -- GitLab From bcdc18c64b9221c22cca0be1a2ee6cfb01d3ed5a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 21 Apr 2017 04:26:12 -0700 Subject: [PATCH 0761/1408] Bluetooth: expand comments on new PHY constants Bug: 37544152 Test: the all-knowing eyes of the reviewers Change-Id: I8f5a756a204fe90960700e1dc42f64824d3b844a (cherry picked from commit 60c7dac77533163ed951dee09ec7b42a49bdf54a) --- .../android/bluetooth/BluetoothDevice.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c8d6b190d02..c4272688cb4 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -595,32 +595,38 @@ public final class BluetoothDevice implements Parcelable { public static final int TRANSPORT_LE = 2; /** - * Bluetooth LE 1M PHY. + * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or + * connection. */ public static final int PHY_LE_1M = 1; /** - * Bluetooth LE 2M PHY. + * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or + * connection. */ public static final int PHY_LE_2M = 2; /** - * Bluetooth LE Coded PHY. + * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning + * or connection. */ public static final int PHY_LE_CODED = 3; /** - * Bluetooth LE 1M PHY mask. + * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available + * options in a bitmask. */ public static final int PHY_LE_1M_MASK = 1; /** - * Bluetooth LE 2M PHY mask. + * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available + * options in a bitmask. */ public static final int PHY_LE_2M_MASK = 2; /** - * Bluetooth LE Coded PHY mask. + * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many + * available options in a bitmask. */ public static final int PHY_LE_CODED_MASK = 4; -- GitLab From 210515a2790b88e3587ea39241c80ca73fbd5ffb Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 17 Apr 2017 20:42:22 -0700 Subject: [PATCH 0762/1408] Limit btsnoop file size (3/8) Limit btsnoop file size by rotating between snoop files. The rotation occurrs when a fixed number of packets have been logged and will start overwriting the older file. Bug: 35998031 Test: Enable snoop logs from developer options and let logs get large Merged-In: I40d5da4c1b1c9b45908e5790d130f1c5e804c773 Change-Id: I40d5da4c1b1c9b45908e5790d130f1c5e804c773 --- .../android/bluetooth/BluetoothAdapter.java | 22 ------------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - .../bluetooth/BluetoothInstrumentation.java | 8 ------- .../bluetooth/BluetoothManagerService.java | 10 --------- 4 files changed, 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 735d84e72b6..ff52f27447c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1031,28 +1031,6 @@ public final class BluetoothAdapter { return null; } - /** - * enable or disable Bluetooth HCI snoop log. - * - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * - * @return true to indicate configure HCI log successfully, or false on - * immediate error - * @hide - */ - public boolean configHciSnoopLog(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.configHciSnoopLog(enable); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - /** * Factory reset bluetooth settings. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index b33781729b6..43c5ae4407c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -97,7 +97,6 @@ interface IBluetooth ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - boolean configHciSnoopLog(boolean enable); boolean factoryReset(); boolean isMultiAdvertisementSupported(); diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 411a3f89496..37b2a50ed67 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -72,8 +72,6 @@ public class BluetoothInstrumentation extends Instrumentation { getAddress(); } else if ("getBondedDevices".equals(command)) { getBondedDevices(); - } else if ("enableBtSnoop".equals(command)) { - enableBtSnoop(); } else { finish(null); } @@ -116,12 +114,6 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } - public void enableBtSnoop() { - Assert.assertTrue("failed to enable snoop log", - getBluetoothAdapter().configHciSnoopLog(true)); - finish(mSuccessResult); - } - public void finish(Bundle result) { if (result == null) { result = new Bundle(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5eb4db408a6..1262d88be8e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1496,16 +1496,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mGetNameAddressOnly) return; } - try { - boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); - if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) { - Slog.e(TAG,"IBluetooth.configHciSnoopLog return false"); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call configHciSnoopLog", e); - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From 78488ad15a35a9333cbb156eecce9557cba52a01 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Tue, 28 Mar 2017 13:44:04 +0100 Subject: [PATCH 0763/1408] Introduce DISALLOW_BLUETOOTH_SHARING. When this restriction is enforced Bluetooth sharing option should not be present when the user tries to share something. Previously this was handled by explicitly disabling bluetooth sharing activity during managed provisioning, now this code is to be removed (see topic CLs) and the same behavior should be achieved by setting this restriction for profile owners by default. In Bluetooth: 1) Don't check restrictions on boot, it is invoked anyway through the listener during boot. 2) Ignore when the restriction is "changed" from true to true - i think it was the initial intent in that condition. 3) Disable the component for a particular user and not always the system user. This is something that has to be fixed in O I think since currently in secondary user the bluetooth itself gets disabled but the sharing thing still shows up. In DPMS: 1) Now ActiveAdmin for PO also contains a set of restrictions applied by default. 2) Now all ActiveAdmins for POs are loaded quite early. That shouldn't have huge impact though. Bug: 36249732 Test: run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.ManagedProfileTest#testBluetoothSharingRestriction Test: run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Test: runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java Change-Id: I78c4ffbd503c4a10138e8c0862a9f206f24c5631 Merged-in: I78c4ffbd503c4a10138e8c0862a9f206f24c5631 (cherry picked from commit 7f4ad75218bdd3f1bdf9022a146147eae032cc0c) --- .../bluetooth/BluetoothManagerService.java | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6c18b260e18..18b4571ca3e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import android.Manifest; import android.app.ActivityManager; +import android.app.AppGlobals; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; @@ -37,11 +38,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -50,7 +51,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -61,15 +61,15 @@ import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import com.android.internal.util.DumpUtils; -import com.android.server.pm.PackageManagerService; +import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; class BluetoothManagerService extends IBluetoothManager.Stub { @@ -120,7 +120,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; - private static final int MAX_SAVE_RETRIES = 3; private static final int MAX_ERROR_RESTART_RETRIES = 6; // Bluetooth persisted setting is off @@ -223,22 +222,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { - if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) - && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { - // The relevant restriction has not changed - do nothing. - return; + if (!UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, + UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH_SHARING)) { + return; // No relevant changes, nothing to do. } - final boolean bluetoothDisallowed = - newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); - if ((mEnable || mEnableExternal) && bluetoothDisallowed) { + + final boolean disallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + + // DISALLOW_BLUETOOTH is a global restriction that can only be set by DO or PO on the + // system user, so we only look at the system user. + if (userId == UserHandle.USER_SYSTEM && disallowed && (mEnable || mEnableExternal)) { try { - disable(null, true); + disable(null /* packageName */, true /* persist */); } catch (RemoteException e) { - Slog.w(TAG, "Exception when disabling Bluetooth from UserRestrictionsListener", - e); + Slog.w(TAG, "Exception when disabling Bluetooth", e); } } - updateOppLauncherComponentState(bluetoothDisallowed); + final boolean sharingDisallowed = disallowed + || newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING); + updateOppLauncherComponentState(userId, sharingDisallowed); } }; @@ -994,11 +996,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); final boolean isBluetoothDisallowed = isBluetoothDisallowed(); - PackageManagerService packageManagerService = - (PackageManagerService) ServiceManager.getService("package"); - if (packageManagerService != null && !packageManagerService.isOnlyCoreApps()) { - updateOppLauncherComponentState(isBluetoothDisallowed); - } if (isBluetoothDisallowed) { return; } @@ -2074,21 +2071,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not - * offered to the user if Bluetooth is disallowed. Puts the component to its default state if - * Bluetooth is not disallowed. + * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default + * state if Bluetooth is not disallowed. * - * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user - * restriction was set. + * @param userId user to disable bluetooth sharing for. + * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed. */ - private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) { final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); - final int newState = bluetoothDisallowed + final int newState = bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; try { - mContext.getPackageManager() - .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + final IPackageManager imp = AppGlobals.getPackageManager(); + imp.setComponentEnabledSetting(oppLauncherComponent, newState, 0 /* flags */, userId); } catch (Exception e) { // The component was not found, do nothing. } -- GitLab From b6212a3def2e91e8964009b1a373b2053640ff37 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 27 Apr 2017 07:04:15 -0700 Subject: [PATCH 0764/1408] Fix NPE when accessing mCallback in BluetoothGatt This issue was introduced in commit f2b84e7d60c8eee3ef2cff0f8bdecabdd7209234. Bug: 37710354 Test: none Change-Id: I2d985ce97c44d4e096713e156e57437f44ea3ddb --- .../java/android/bluetooth/BluetoothGatt.java | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 7806e31c418..40f10a8edd2 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -159,8 +159,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + } } }); @@ -192,7 +194,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + if (mCallback != null) { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } } }); } @@ -212,7 +216,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + if (mCallback != null) { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } } }); } @@ -235,7 +241,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, status, + profileState); + } } }); @@ -294,7 +303,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); + if (mCallback != null) { + mCallback.onServicesDiscovered(BluetoothGatt.this, status); + } } }); } @@ -344,7 +355,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); + if (mCallback != null) { + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, + status); + } } }); } @@ -390,7 +404,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); + if (mCallback != null) { + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, + status); + } } }); } @@ -416,7 +433,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + if (mCallback != null) { + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + } } }); } @@ -461,7 +480,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + if (mCallback != null) { + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + } } }); } @@ -505,7 +526,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + if (mCallback != null) { + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + } } }); } @@ -529,7 +552,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + if (mCallback != null) { + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + } } }); } @@ -548,7 +573,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + if (mCallback != null) { + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + } } }); } @@ -568,7 +595,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + if (mCallback != null) { + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + } } }); } @@ -590,8 +619,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); + if (mCallback != null) { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } } }); } -- GitLab From 40185b0b78d2fc990e69d1121fded74b2dd81d5f Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 14 Apr 2017 07:21:20 -0700 Subject: [PATCH 0765/1408] Read by UUID for PTS tests (1/5) Add a hidden api for reading characteristic by UUID for PTS. Bug: 35150313 Test: sl4a GattReadTest.byUuid Change-Id: Ice4076d99e4694d20374ba0fdcae74d5ae841147 (cherry picked from commit de74891d1c603326691678ec301c80b4525bc4ef) --- .../java/android/bluetooth/BluetoothGatt.java | 35 +++++++++++++++++++ .../android/bluetooth/IBluetoothGatt.aidl | 2 ++ 2 files changed, 37 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 713dbf43d33..e77ab2e61a0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -972,6 +972,41 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Reads the characteristic using its UUID from the associated remote device. + * + *

        This is an asynchronous operation. The result of the read operation + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} + * callback. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid UUID of characteristic to read from the remote device + * @return true, if the read operation was initiated successfully + * @hide + */ + public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { + if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); + if (mService == null || mClientIf == 0) return false; + + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + + try { + mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), + new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); + } catch (RemoteException e) { + Log.e(TAG,"",e); + mDeviceBusy = false; + return false; + } + + return true; + } + + /** * Writes a given characteristic and its values to the associated remote device. * diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 12e9baaded9..9e6d7007eb2 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -80,6 +80,8 @@ interface IBluetoothGatt { void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); + void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, + in int startHandle, in int endHandle, in int authReq); void writeCharacteristic(in int clientIf, in String address, in int handle, in int writeType, in int authReq, in byte[] value); void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); -- GitLab From ce4012c670a732970f89f6d806487b02ce8345d5 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 19 Apr 2017 06:52:08 -0700 Subject: [PATCH 0766/1408] Expose LE advertiser address for easier PTS tests (1/6) This patchset adds a hidden method getOwnAddress, that lets app with BLUETOOTH_PRIVILEGED permission to lear their own addreess. This is done exclusively for PTS tests. Bug: 35147497 Test: manual Change-Id: Iaf0f2fe0613de44b8430ac25e691d66a4ad44f8d (cherry picked from commit 4bc4a441007e0a9ef6ccd1816c831ffd2dfa4c18) --- .../java/android/bluetooth/IBluetoothGatt.aidl | 1 + .../android/bluetooth/le/AdvertisingSet.java | 18 +++++++++++++++++- .../bluetooth/le/AdvertisingSetCallback.java | 11 +++++++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 11 +++++++++++ .../bluetooth/le/IAdvertisingSetCallback.aidl | 1 + 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 9e6d7007eb2..e2d4f5ba4fd 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -59,6 +59,7 @@ interface IBluetoothGatt { in IAdvertisingSetCallback callback); void stopAdvertisingSet(in IAdvertisingSetCallback callback); + void getOwnAddress(in int advertiserId); void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); void setAdvertisingData(in int advertiserId, in AdvertiseData data); void setScanResponseData(in int advertiserId, in AdvertiseData data); diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 51571b2746a..3021be1f8c0 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -181,7 +181,23 @@ public final class AdvertisingSet { } /** - * Returns advertiserId associated with thsi advertising set. + * Returns address associated with this advertising set. + * This method is exposed only for Bluetooth PTS tests, no app or system service + * should ever use it. + * + * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. + * @hide + */ + public void getOwnAddress(){ + try { + gatt.getOwnAddress(this.advertiserId); + } catch (RemoteException e) { + Log.e(TAG, "remote exception - ", e); + } + } + + /** + * Returns advertiserId associated with this advertising set. * * @hide */ diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index fe3b1cdd63a..2c46e856db4 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -143,4 +143,15 @@ public abstract class AdvertisingSetCallback { */ public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} + + /** + * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()} + * indicating result of the operation. + * + * @param advertisingSet The advertising set. + * @param addressType type of address. + * @param address advertising set bluetooth address. + * @hide + */ + public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) {} } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 0c7958dc9a3..67d56d5060e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -543,6 +543,17 @@ public final class BluetoothLeAdvertiser { }); } + @Override + public void onOwnAddressRead(int advertiserId, int addressType, String address) { + handler.post(new Runnable() { + @Override + public void run() { + AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); + callback.onOwnAddressRead(advertisingSet, addressType, address); + } + }); + } + @Override public void onAdvertisingSetStopped(int advertiserId) { handler.post(new Runnable() { diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl index 2c9f4baad52..3628c775b79 100644 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl @@ -21,6 +21,7 @@ package android.bluetooth.le; */ oneway interface IAdvertisingSetCallback { void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); + void onOwnAddressRead(in int advertiserId, in int addressType, in String address); void onAdvertisingSetStopped(in int advertiserId); void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); void onAdvertisingDataSet(in int advertiserId, in int status); -- GitLab From d8375f532869c1e7130d9acae4fa95ba20e3cdda Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 27 Apr 2017 07:04:15 -0700 Subject: [PATCH 0767/1408] Fix NPE when accessing mCallback in BluetoothGatt This issue was introduced in commit 4eab49652e48159fd37f827d30b822f2f187551e. Bug: 37710354 Test: none Change-Id: I2d985ce97c44d4e096713e156e57437f44ea3ddb (cherry picked from commit 0e4ac75f2e291305a493796186d44a081926f3a8) --- .../java/android/bluetooth/BluetoothGatt.java | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index e77ab2e61a0..0f01d62f8b6 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -159,8 +159,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + } } }); @@ -192,7 +194,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + if (mCallback != null) { + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + } } }); } @@ -212,7 +216,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + if (mCallback != null) { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } } }); } @@ -235,7 +241,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, status, + profileState); + } } }); @@ -294,7 +303,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); + if (mCallback != null) { + mCallback.onServicesDiscovered(BluetoothGatt.this, status); + } } }); } @@ -344,7 +355,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); + if (mCallback != null) { + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, + status); + } } }); } @@ -390,7 +404,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); + if (mCallback != null) { + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, + status); + } } }); } @@ -416,7 +433,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + if (mCallback != null) { + mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + } } }); } @@ -461,7 +480,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + if (mCallback != null) { + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + } } }); } @@ -505,7 +526,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + if (mCallback != null) { + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + } } }); } @@ -529,7 +552,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + if (mCallback != null) { + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + } } }); } @@ -548,7 +573,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + if (mCallback != null) { + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + } } }); } @@ -568,7 +595,9 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + if (mCallback != null) { + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + } } }); } @@ -590,8 +619,10 @@ public final class BluetoothGatt implements BluetoothProfile { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); + if (mCallback != null) { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } } }); } -- GitLab From ace5976b6838eae37a160fdef9841248df6257ca Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 27 Apr 2017 08:37:04 -0700 Subject: [PATCH 0768/1408] Bluetooth: Add constants to ScanResult Add TX_POWER_NOT_PRESENT and PERIODIC_INTERVAL_NOT_PRESENT Test: none Bug: 37536707 Change-Id: Ia48a30f44a961c6e0babd17ecaed0eb93c98ecad --- .../java/android/bluetooth/le/ScanResult.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 5b2fa406cd3..e552398e9e2 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -51,6 +51,16 @@ public final class ScanResult implements Parcelable { */ public static final int SID_NOT_PRESENT = 0xFF; + /** + * TX power is not present in the packet. + */ + public static final int TX_POWER_NOT_PRESENT = 0x7F; + + /** + * Periodic advertising interval is not present in the packet. + */ + public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00; + /** * Mask for checking whether event type represents legacy advertisement. */ @@ -265,15 +275,16 @@ public final class ScanResult implements Parcelable { /** * Returns the transmit power in dBm. - * Valid range is [-127, 126]. A value of 127 indicates that the - * advertisement did not indicate TX power. + * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT} + * indicates that the TX power is not present. */ public int getTxPower() { return mTxPower; } /** * Returns the periodic advertising interval in units of 1.25ms. - * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means - * periodic advertising is not used for this scan result. + * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of + * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic + * advertising interval is not present. */ public int getPeriodicAdvertisingInterval() { return mPeriodicAdvertisingInterval; -- GitLab From 98fbaf1b0e8bfb9f7a62dc80e1599d59c7db22ce Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 27 Apr 2017 08:37:04 -0700 Subject: [PATCH 0769/1408] Bluetooth: Add constants to ScanResult Add TX_POWER_NOT_PRESENT and PERIODIC_INTERVAL_NOT_PRESENT Test: none Bug: 37536707 Change-Id: Ia48a30f44a961c6e0babd17ecaed0eb93c98ecad (cherry-picked from commit c7c01a28774e431a408744b443a674f2622fde9f) --- .../java/android/bluetooth/le/ScanResult.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 5b2fa406cd3..e552398e9e2 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -51,6 +51,16 @@ public final class ScanResult implements Parcelable { */ public static final int SID_NOT_PRESENT = 0xFF; + /** + * TX power is not present in the packet. + */ + public static final int TX_POWER_NOT_PRESENT = 0x7F; + + /** + * Periodic advertising interval is not present in the packet. + */ + public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00; + /** * Mask for checking whether event type represents legacy advertisement. */ @@ -265,15 +275,16 @@ public final class ScanResult implements Parcelable { /** * Returns the transmit power in dBm. - * Valid range is [-127, 126]. A value of 127 indicates that the - * advertisement did not indicate TX power. + * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT} + * indicates that the TX power is not present. */ public int getTxPower() { return mTxPower; } /** * Returns the periodic advertising interval in units of 1.25ms. - * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means - * periodic advertising is not used for this scan result. + * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of + * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic + * advertising interval is not present. */ public int getPeriodicAdvertisingInterval() { return mPeriodicAdvertisingInterval; -- GitLab From 7a5b0a7efab9e41113a4527c3f8f61d20ff955ec Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 28 Apr 2017 04:11:26 -0700 Subject: [PATCH 0770/1408] Bluetooth: bluetooth.le API fixes Fix minor spelling problems. Throw IllegalStateException instead of IllegalArgumentException in build(). Bug: 37532634 Test: none Change-Id: I73b6f04aec98f5baffb06a363e2a3f71e8cec3c4 --- .../java/android/bluetooth/le/AdvertisingSet.java | 2 +- .../bluetooth/le/AdvertisingSetCallback.java | 2 +- .../bluetooth/le/AdvertisingSetParameters.java | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 3021be1f8c0..1bc211cb83a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -172,7 +172,7 @@ public final class AdvertisingSet { * * @param enable whether the periodic advertising should be enabled (true), or disabled (false). */ - public void setPeriodicAdvertisingEnable(boolean enable) { + public void setPeriodicAdvertisingEnabled(boolean enable) { try { gatt.setPeriodicAdvertisingEnable(this.advertiserId, enable); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index 2c46e856db4..c3c16a479e7 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -135,7 +135,7 @@ public abstract class AdvertisingSetCallback { int status) {} /** - * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnable} + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled} * indicating result of the operation. * * @param advertisingSet The advertising set. diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 31d8f482090..71c4484a947 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -242,7 +242,7 @@ public final class AdvertisingSetParameters implements Parcelable { * non-connectable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param connectable Controls whether the advertisment type will be + * @param connectable Controls whether the advertisement type will be * connectable (true) or non-connectable (false). */ public Builder setConnectable(boolean connectable) { @@ -254,7 +254,7 @@ public final class AdvertisingSetParameters implements Parcelable { * Set whether the advertisement type should be scannable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param scannable Controls whether the advertisment type will be + * @param scannable Controls whether the advertisement type will be * scannable (true) or non-scannable (false). */ public Builder setScannable(boolean scannable) { @@ -386,6 +386,7 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Build the {@link AdvertisingSetParameters} object. + * @throws IllegalStateException if invalid combination of parameters is used. */ public AdvertisingSetParameters build() { if (isLegacy) { @@ -394,22 +395,22 @@ public final class AdvertisingSetParameters implements Parcelable { } if (connectable == true && scannable == false) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Legacy advertisement can't be connectable and non-scannable"); } if (includeTxPower) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Legacy advertising can't include TX power level in header"); } } else { if (connectable && scannable) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Advertising can't be both connectable and scannable"); } if (isAnonymous && connectable) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Advertising can't be both connectable and anonymous"); } } -- GitLab From 0992d6b5a9c39cfeeffe067ed1942fbd0349c8c2 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 28 Apr 2017 04:11:26 -0700 Subject: [PATCH 0771/1408] Bluetooth: bluetooth.le API fixes Fix minor spelling problems. Throw IllegalStateException instead of IllegalArgumentException in build(). Bug: 37532634 Test: none Change-Id: I73b6f04aec98f5baffb06a363e2a3f71e8cec3c4 (cherry picked from commit 9d4abb5631c7719de8d919e0c37c9aee54354266) --- .../java/android/bluetooth/le/AdvertisingSet.java | 2 +- .../bluetooth/le/AdvertisingSetCallback.java | 2 +- .../bluetooth/le/AdvertisingSetParameters.java | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 3021be1f8c0..1bc211cb83a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -172,7 +172,7 @@ public final class AdvertisingSet { * * @param enable whether the periodic advertising should be enabled (true), or disabled (false). */ - public void setPeriodicAdvertisingEnable(boolean enable) { + public void setPeriodicAdvertisingEnabled(boolean enable) { try { gatt.setPeriodicAdvertisingEnable(this.advertiserId, enable); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index 2c46e856db4..c3c16a479e7 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -135,7 +135,7 @@ public abstract class AdvertisingSetCallback { int status) {} /** - * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnable} + * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled} * indicating result of the operation. * * @param advertisingSet The advertising set. diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 31d8f482090..71c4484a947 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -242,7 +242,7 @@ public final class AdvertisingSetParameters implements Parcelable { * non-connectable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param connectable Controls whether the advertisment type will be + * @param connectable Controls whether the advertisement type will be * connectable (true) or non-connectable (false). */ public Builder setConnectable(boolean connectable) { @@ -254,7 +254,7 @@ public final class AdvertisingSetParameters implements Parcelable { * Set whether the advertisement type should be scannable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param scannable Controls whether the advertisment type will be + * @param scannable Controls whether the advertisement type will be * scannable (true) or non-scannable (false). */ public Builder setScannable(boolean scannable) { @@ -386,6 +386,7 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Build the {@link AdvertisingSetParameters} object. + * @throws IllegalStateException if invalid combination of parameters is used. */ public AdvertisingSetParameters build() { if (isLegacy) { @@ -394,22 +395,22 @@ public final class AdvertisingSetParameters implements Parcelable { } if (connectable == true && scannable == false) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Legacy advertisement can't be connectable and non-scannable"); } if (includeTxPower) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Legacy advertising can't include TX power level in header"); } } else { if (connectable && scannable) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Advertising can't be both connectable and scannable"); } if (isAnonymous && connectable) { - throw new IllegalArgumentException( + throw new IllegalStateException( "Advertising can't be both connectable and anonymous"); } } -- GitLab From dbbc5ff3db35c9554d9a85a907d28a95354281e6 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 17 Apr 2017 20:42:22 -0700 Subject: [PATCH 0772/1408] Limit btsnoop file size (3/8) Limit btsnoop file size by rotating between snoop files. The rotation occurrs when a fixed number of packets have been logged and will start overwriting the older file. Bug: 35998031 Test: Enable snoop logs from developer options and let logs get large Change-Id: I40d5da4c1b1c9b45908e5790d130f1c5e804c773 (cherry picked from commit 7e2e61ee2e3f46fa0e4bee82028b36a0f2aa22d5) --- .../android/bluetooth/BluetoothAdapter.java | 22 ------------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - .../bluetooth/BluetoothInstrumentation.java | 8 ------- .../bluetooth/BluetoothManagerService.java | 10 --------- 4 files changed, 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d60d4db1cab..64c0f31de72 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1023,28 +1023,6 @@ public final class BluetoothAdapter { return null; } - /** - * enable or disable Bluetooth HCI snoop log. - * - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * - * @return true to indicate configure HCI log successfully, or false on - * immediate error - * @hide - */ - public boolean configHciSnoopLog(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.configHciSnoopLog(enable); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - /** * Factory reset bluetooth settings. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index b33781729b6..43c5ae4407c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -97,7 +97,6 @@ interface IBluetooth ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - boolean configHciSnoopLog(boolean enable); boolean factoryReset(); boolean isMultiAdvertisementSupported(); diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 411a3f89496..37b2a50ed67 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -72,8 +72,6 @@ public class BluetoothInstrumentation extends Instrumentation { getAddress(); } else if ("getBondedDevices".equals(command)) { getBondedDevices(); - } else if ("enableBtSnoop".equals(command)) { - enableBtSnoop(); } else { finish(null); } @@ -116,12 +114,6 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } - public void enableBtSnoop() { - Assert.assertTrue("failed to enable snoop log", - getBluetoothAdapter().configHciSnoopLog(true)); - finish(mSuccessResult); - } - public void finish(Bundle result) { if (result == null) { result = new Bundle(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 18b4571ca3e..c785fb91e8f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1494,16 +1494,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mGetNameAddressOnly) return; } - try { - boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); - if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) { - Slog.e(TAG,"IBluetooth.configHciSnoopLog return false"); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call configHciSnoopLog", e); - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From 98024a83d50f8cfcc02701403de5f99bcf8e50d6 Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Mon, 1 May 2017 16:57:31 -0700 Subject: [PATCH 0773/1408] Fix Bluetooth GATT API default handler assignment Restores previous behaviour where GATT callbacks are invoked on the binder thread and not the calling process main looper thread. This fixes performance regressions as well as some NetworkOnMainThreadException's for some applications. Bug: 37544152 Bug: 37871717 Test: Covered by prior API tests. Change-Id: Id8ab705dd4d7f00030e6ac29e056dde5180670e9 --- .../android/bluetooth/BluetoothDevice.java | 5 +-- .../java/android/bluetooth/BluetoothGatt.java | 44 +++++++++++++------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c4272688cb4..e8ad69d2f52 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1703,7 +1703,7 @@ public final class BluetoothDevice implements Parcelable { * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect * if {@code autoConnect} is set to true. * @param handler The handler to use for the callback. If {@code null}, callbacks will happen - * on the service's main thread. + * on an un-specified background thread. * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, @@ -1712,9 +1712,6 @@ public final class BluetoothDevice implements Parcelable { if (callback == null) throw new NullPointerException("callback is null"); - if (handler == null) - handler = new Handler(Looper.getMainLooper()); - // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 0f01d62f8b6..5fabbb68df8 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -156,7 +156,7 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -191,7 +191,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -213,7 +213,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -238,7 +238,7 @@ public final class BluetoothGatt implements BluetoothProfile { int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -300,7 +300,7 @@ public final class BluetoothGatt implements BluetoothProfile { } } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -352,7 +352,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -401,7 +401,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -430,7 +430,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -477,7 +477,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -523,7 +523,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -549,7 +549,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -570,7 +570,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -592,7 +592,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -616,7 +616,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -702,6 +702,22 @@ public final class BluetoothGatt implements BluetoothProfile { return null; } + /** + * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable + * immediately if no Handler was provided. + */ + private void runOrQueueCallback(final Runnable cb) { + if (mHandler == null) { + try { + cb.run(); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } else { + mHandler.post(cb); + } + } + /** * Register an application callback to start using GATT. * -- GitLab From c5cd102ca122d0e0c59d5aea29c21b1ea15261ce Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Wed, 26 Apr 2017 16:37:53 -0700 Subject: [PATCH 0774/1408] Add persistent state for Bluetooth high quality audio support Defines the flags used in code and the Settings.Global key names for persisting the following state for Bluetooth A2DP Sink devices: -Whether the device supports optional codecs or not -Whether optional codecs should be turned on for the device For each of these two properties we model the state in the code as yes/no/unknown, so that we can tailor the UI and behavior accordingly. Bug=37441685 Test: manually Change-Id: I6bcd02fd7c95bef989575f3b13d4788dab61971a --- .../java/android/bluetooth/BluetoothA2dp.java | 114 ++++++++++++++++++ .../android/bluetooth/IBluetoothA2dp.aidl | 3 + 2 files changed, 117 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d1ad8de0b11..c58eaa14ed8 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -136,6 +136,38 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public static final int STATE_NOT_PLAYING = 11; + /** + * We don't have a stored preference for whether or not the given A2DP sink device supports + * optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; + + /** + * The given A2DP sink device does not support optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; + + /** + * The given A2DP sink device does support optional codecs. + * @hide */ + public static final int OPTIONAL_CODECS_SUPPORTED = 1; + + /** + * We don't have a stored preference for whether optional codecs should be enabled or disabled + * for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; + + /** + * Optional codecs should be disabled for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; + + /** + * Optional codecs should be enabled for the given A2DP device. + * @hide */ + public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; + private Context mContext; private ServiceListener mServiceListener; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -654,6 +686,88 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /** + * Returns whether this device supports optional codecs. + * + * @param device The device to check + * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or + * OPTIONAL_CODECS_SUPPORTED. + * + * @hide + */ + public int supportsOptionalCodecs(BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.supportsOptionalCodecs(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return OPTIONAL_CODECS_SUPPORT_UNKNOWN; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + return OPTIONAL_CODECS_SUPPORT_UNKNOWN; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Returns whether this device should have optional codecs enabled. + * + * @param device The device in question. + * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or + * OPTIONAL_CODECS_PREF_DISABLED. + * + * @hide + */ + public int getOptionalCodecsEnabled(BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.getOptionalCodecsEnabled(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return OPTIONAL_CODECS_PREF_UNKNOWN; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + return OPTIONAL_CODECS_PREF_UNKNOWN; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Sets a persistent preference for whether a given device should have optional codecs enabled. + * + * @param device The device to set this preference for. + * @param value Whether the optional codecs should be enabled for this device. This should be + * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or + * OPTIONAL_CODECS_PREF_DISABLED. + * @hide + */ + public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { + try { + if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && + value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED && + value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { + Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); + return; + } + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + mService.setOptionalCodecsEnabled(device, value); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl index a775a1f90b8..1b533cba3d2 100644 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/framework/java/android/bluetooth/IBluetoothA2dp.aidl @@ -42,4 +42,7 @@ interface IBluetoothA2dp { oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); oneway void enableOptionalCodecs(); oneway void disableOptionalCodecs(); + int supportsOptionalCodecs(in BluetoothDevice device); + int getOptionalCodecsEnabled(in BluetoothDevice device); + oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value); } -- GitLab From 777463136a672ee1bd56332204317f8732a9160c Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Mon, 17 Apr 2017 20:42:22 -0700 Subject: [PATCH 0775/1408] Limit btsnoop file size (3/9) Limit btsnoop file size by rotating between snoop files. The rotation occurrs when a fixed number of packets have been logged and will start overwriting the older file. Bug: 35998031 Test: Enable snoop logs from developer options and let logs get large Change-Id: I40d5da4c1b1c9b45908e5790d130f1c5e804c773 --- .../android/bluetooth/BluetoothAdapter.java | 22 ------------------- .../java/android/bluetooth/IBluetooth.aidl | 1 - .../bluetooth/BluetoothInstrumentation.java | 8 ------- .../bluetooth/BluetoothManagerService.java | 10 --------- 4 files changed, 41 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d60d4db1cab..64c0f31de72 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1023,28 +1023,6 @@ public final class BluetoothAdapter { return null; } - /** - * enable or disable Bluetooth HCI snoop log. - * - *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * - * @return true to indicate configure HCI log successfully, or false on - * immediate error - * @hide - */ - public boolean configHciSnoopLog(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.configHciSnoopLog(enable); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - /** * Factory reset bluetooth settings. * diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index b33781729b6..43c5ae4407c 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -97,7 +97,6 @@ interface IBluetooth ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - boolean configHciSnoopLog(boolean enable); boolean factoryReset(); boolean isMultiAdvertisementSupported(); diff --git a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java index 411a3f89496..37b2a50ed67 100644 --- a/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java +++ b/framework/tests/src/android/bluetooth/BluetoothInstrumentation.java @@ -72,8 +72,6 @@ public class BluetoothInstrumentation extends Instrumentation { getAddress(); } else if ("getBondedDevices".equals(command)) { getBondedDevices(); - } else if ("enableBtSnoop".equals(command)) { - enableBtSnoop(); } else { finish(null); } @@ -116,12 +114,6 @@ public class BluetoothInstrumentation extends Instrumentation { finish(mSuccessResult); } - public void enableBtSnoop() { - Assert.assertTrue("failed to enable snoop log", - getBluetoothAdapter().configHciSnoopLog(true)); - finish(mSuccessResult); - } - public void finish(Bundle result) { if (result == null) { result = new Bundle(); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 18b4571ca3e..c785fb91e8f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1494,16 +1494,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mGetNameAddressOnly) return; } - try { - boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1); - if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) { - Slog.e(TAG,"IBluetooth.configHciSnoopLog return false"); - } - } catch (RemoteException e) { - Slog.e(TAG,"Unable to call configHciSnoopLog", e); - } - //Register callback object try { mBluetooth.registerCallback(mBluetoothCallback); -- GitLab From 6aeb76d4e43bf5d886651dcf57070edceee565b3 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Tue, 2 May 2017 16:28:03 -0700 Subject: [PATCH 0776/1408] Bluetooth: Move Bluetooth battery stats tracking (1/2) Move Bluetooth battery stats tracking to AppScanStats for a more unified tracking system. Bug: 37720787 Test: cts-tradefed run cts-dev -m CtsIncidentHostTestCases -t com.android.server.cts.BatteryStatsValidationTest#testBleScans Perform BLE scan and check battery stats Change-Id: Ie6c682374e6e258c291d3a11eede649c7747ef40 --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 4 ++-- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index e2d4f5ba4fd..dc5c7b6b620 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -43,10 +43,10 @@ import android.bluetooth.le.IScannerCallback; interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); - void registerScanner(in IScannerCallback callback); + void registerScanner(in IScannerCallback callback, in WorkSource workSource); void unregisterScanner(in int scannerId); void startScan(in int scannerId, in ScanSettings settings, in List filters, - in WorkSource workSource, in List scanStorages, in String callingPackage); + in List scanStorages, in String callingPackage); void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List filters, in String callingPackage); void stopScanForIntent(in PendingIntent intent, in String callingPackage); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 52465137dfd..f3f0ae5cd95 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -360,7 +360,7 @@ public final class BluetoothLeScanner { // Scan stopped. if (mScannerId == -1) return; try { - mBluetoothGatt.registerScanner(this); + mBluetoothGatt.registerScanner(this, mWorkSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "application registeration exception", e); @@ -424,7 +424,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mWorkSource, mResultStorages, + mResultStorages, ActivityThread.currentOpPackageName()); } } catch (RemoteException e) { -- GitLab From bfde0feb1b9ceb056b38a66985614fa2fa1a4390 Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Mon, 1 May 2017 16:57:31 -0700 Subject: [PATCH 0777/1408] Fix Bluetooth GATT API default handler assignment Restores previous behaviour where GATT callbacks are invoked on the binder thread and not the calling process main looper thread. This fixes performance regressions as well as some NetworkOnMainThreadException's for some applications. Bug: 37544152 Bug: 37871717 Test: Covered by prior API tests. Change-Id: Id8ab705dd4d7f00030e6ac29e056dde5180670e9 (cherry picked from commit 98024a83d50f8cfcc02701403de5f99bcf8e50d6) --- .../android/bluetooth/BluetoothDevice.java | 5 +-- .../java/android/bluetooth/BluetoothGatt.java | 44 +++++++++++++------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e0b03d22abe..f158f5f29ec 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1714,7 +1714,7 @@ public final class BluetoothDevice implements Parcelable { * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect * if {@code autoConnect} is set to true. * @param handler The handler to use for the callback. If {@code null}, callbacks will happen - * on the service's main thread. + * on an un-specified background thread. * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, @@ -1723,9 +1723,6 @@ public final class BluetoothDevice implements Parcelable { if (callback == null) throw new NullPointerException("callback is null"); - if (handler == null) - handler = new Handler(Looper.getMainLooper()); - // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 40f10a8edd2..3135b30ebcc 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -156,7 +156,7 @@ public final class BluetoothGatt implements BluetoothProfile { } mClientIf = clientIf; if (status != GATT_SUCCESS) { - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -191,7 +191,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -213,7 +213,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -238,7 +238,7 @@ public final class BluetoothGatt implements BluetoothProfile { int profileState = connected ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -300,7 +300,7 @@ public final class BluetoothGatt implements BluetoothProfile { } } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -352,7 +352,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (status == 0) characteristic.setValue(value); - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -401,7 +401,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -430,7 +430,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.setValue(value); - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -477,7 +477,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -523,7 +523,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetryState = AUTH_RETRY_STATE_IDLE; - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -549,7 +549,7 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -570,7 +570,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -592,7 +592,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -616,7 +616,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - mHandler.post(new Runnable() { + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { @@ -702,6 +702,22 @@ public final class BluetoothGatt implements BluetoothProfile { return null; } + /** + * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable + * immediately if no Handler was provided. + */ + private void runOrQueueCallback(final Runnable cb) { + if (mHandler == null) { + try { + cb.run(); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } else { + mHandler.post(cb); + } + } + /** * Register an application callback to start using GATT. * -- GitLab From c9b7e7cc97a49e9fbb4742250ac788ea001caf14 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Tue, 2 May 2017 16:28:03 -0700 Subject: [PATCH 0778/1408] Bluetooth: Move Bluetooth battery stats tracking (1/2) Move Bluetooth battery stats tracking to AppScanStats for a more unified tracking system. Bug: 37720787 Test: cts-tradefed run cts-dev -m CtsIncidentHostTestCases -t com.android.server.cts.BatteryStatsValidationTest#testBleScans Perform BLE scan and check battery stats Change-Id: Ie6c682374e6e258c291d3a11eede649c7747ef40 --- framework/java/android/bluetooth/IBluetoothGatt.aidl | 4 ++-- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index fb6b89341db..63bd9428464 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -42,10 +42,10 @@ import android.bluetooth.le.IScannerCallback; interface IBluetoothGatt { List getDevicesMatchingConnectionStates(in int[] states); - void registerScanner(in IScannerCallback callback); + void registerScanner(in IScannerCallback callback, in WorkSource workSource); void unregisterScanner(in int scannerId); void startScan(in int scannerId, in ScanSettings settings, in List filters, - in WorkSource workSource, in List scanStorages, in String callingPackage); + in List scanStorages, in String callingPackage); void stopScan(in int scannerId); void flushPendingBatchResults(in int scannerId); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index b63c614711e..35c526f9ae0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -300,7 +300,7 @@ public final class BluetoothLeScanner { // Scan stopped. if (mScannerId == -1) return; try { - mBluetoothGatt.registerScanner(this); + mBluetoothGatt.registerScanner(this, mWorkSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "application registeration exception", e); @@ -364,7 +364,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mWorkSource, mResultStorages, + mResultStorages, ActivityThread.currentOpPackageName()); } } catch (RemoteException e) { -- GitLab From 1a4f14b8664c4477eff3e5771bcddd7b44f93839 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 9 May 2017 14:28:21 -0700 Subject: [PATCH 0779/1408] Bluetooth: Fix interval low/min values Bug: 38175945 Test: none Change-Id: Idf487c7aafc518f2c57be78a1fa979b2c4a383d6 --- .../java/android/bluetooth/le/AdvertisingSetParameters.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 71c4484a947..e9747d8205b 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -34,7 +34,7 @@ public final class AdvertisingSetParameters implements Parcelable { * Advertise on low frequency, around every 1000ms. This is the default and * preferred advertising mode as it consumes the least power. */ - public static final int INTERVAL_LOW = 1600; + public static final int INTERVAL_HIGH = 1600; /** * Advertise on medium frequency, around every 250ms. This is balanced @@ -47,7 +47,7 @@ public final class AdvertisingSetParameters implements Parcelable { * has the highest power consumption and should not be used for continuous * background advertising. */ - public static final int INTERVAL_HIGH = 160; + public static final int INTERVAL_LOW = 160; /** * Minimum value for advertising interval. -- GitLab From 61b53b5deb4a7454aa2445bcf004c7dac187c318 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 9 May 2017 14:28:21 -0700 Subject: [PATCH 0780/1408] Bluetooth: Fix interval low/min values Bug: 38175945 Test: none Change-Id: Idf487c7aafc518f2c57be78a1fa979b2c4a383d6 (cherry picked from commit 6b1f39502c7d5f71b42d82ac6d511ee0afd1191e) --- .../java/android/bluetooth/le/AdvertisingSetParameters.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 71c4484a947..e9747d8205b 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -34,7 +34,7 @@ public final class AdvertisingSetParameters implements Parcelable { * Advertise on low frequency, around every 1000ms. This is the default and * preferred advertising mode as it consumes the least power. */ - public static final int INTERVAL_LOW = 1600; + public static final int INTERVAL_HIGH = 1600; /** * Advertise on medium frequency, around every 250ms. This is balanced @@ -47,7 +47,7 @@ public final class AdvertisingSetParameters implements Parcelable { * has the highest power consumption and should not be used for continuous * background advertising. */ - public static final int INTERVAL_HIGH = 160; + public static final int INTERVAL_LOW = 160; /** * Minimum value for advertising interval. -- GitLab From 319e3d3b01ac1829d34e5a1d82ce7ec35603a4f7 Mon Sep 17 00:00:00 2001 From: Sunny Kapdi Date: Fri, 5 May 2017 15:27:33 -0700 Subject: [PATCH 0781/1408] Bluetooth: Fix Periodic Adv interval check The MIN and MAX values need to be swapped else the parameter check in setInterval would always fail Bug: 37579882 Change-Id: I48538c2a61be88caaf04abd94074b3d9eb6dde96 --- .../android/bluetooth/le/PeriodicAdvertisingParameters.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index 8891d2e842d..cf8f08fddd9 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -26,8 +26,8 @@ import android.os.Parcelable; */ public final class PeriodicAdvertisingParameters implements Parcelable { - private static final int INTERVAL_MAX = 80; - private static final int INTERVAL_MIN = 65519; + private static final int INTERVAL_MIN = 80; + private static final int INTERVAL_MAX = 65519; private final boolean includeTxPower; private final int interval; -- GitLab From 8a7717cd606a4048c556c69e2892caa3ecde4fc1 Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Tue, 30 Jul 2013 16:09:20 +0530 Subject: [PATCH 0782/1408] Bluetooth: HID: Add support for Set Idle and Get Idle Commands (3/4) Provides an interface for application to send Set Idle and Get Idle commands to remote HID Device. Support for these two commands was missing from existing code, so existing code design is reused to add support for these two commands. Without this support following mandatory PTS test cases for HID 1.0 cannot be passed, TC_HOS_HID_BV_05/06. Test: Executed PTS tests TC_HOS_HID_BV_05/06 and confirmed if they can pass Bug: 34344715 Change-Id: I548362cc328498920b2dae740f1a76b2cc2d6a67 --- .../bluetooth/BluetoothInputDevice.java | 61 +++++++++++++++++++ .../bluetooth/IBluetoothInputDevice.aidl | 8 +++ 2 files changed, 69 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index 252e3d28a25..fccdf14bb9c 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -96,6 +96,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_TIME_CHANGED = + "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. @@ -199,6 +205,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -658,6 +669,56 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } + + /** + * Send Get_Idle_Time command to the connected HID input device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean getIdleTime(BluetoothDevice device) { + if (DBG) log("getIdletime(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getIdleTime(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Idle_Time command to the connected HID input device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param idleTime Idle time to be set on HID Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setIdleTime(BluetoothDevice device, byte idleTime) { + if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setIdleTime(device, idleTime); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl index 1ebb9ca6eb1..5bd3f781932 100644 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -56,4 +56,12 @@ interface IBluetoothInputDevice { * @hide */ boolean sendData(in BluetoothDevice device, String report); + /** + * @hide + */ + boolean getIdleTime(in BluetoothDevice device); + /** + * @hide + */ + boolean setIdleTime(in BluetoothDevice device, byte idleTime); } -- GitLab From 6bb0e9017983f5d62edf61eda8aa6e4a93bf0da7 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 1 May 2017 21:27:31 -0700 Subject: [PATCH 0783/1408] Add Save and Restore of BluetoothOn setting This change will automatically save the Bluetooth On setting when the user chooses to backup the phone settings into the cloud. This setting is restored by the Setup Wizard (SUW) when configuring the phone and this change will enable or disable the Bluetooth based on this restored setting. Bug: 35657817 Test: Manual test with Sailfish Change-Id: Ie4518593af63f96f8c363f98941ca5260a3ec4bb --- .../bluetooth/BluetoothManagerService.java | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1262d88be8e..5a3c7d0aef3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -90,6 +90,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String REASON_SYSTEM_BOOT = "system boot"; private static final String REASON_UNEXPECTED = "unexpected crash"; private static final String REASON_USER_SWITCH = "user switch"; + private static final String REASON_RESTORE_USER_SETTING = "restore user setting"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind //Maximum msec to wait for service restart @@ -118,6 +119,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; + private static final int MESSAGE_RESTORE_USER_SETTING = 500; + + private static final int RESTORE_SETTING_TO_ON = 1; + private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_SAVE_RETRIES = 3; private static final int MAX_ERROR_RESTART_RETRIES = 6; @@ -315,6 +320,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); } + } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { + final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); + if (Settings.Global.BLUETOOTH_ON.equals(name)) { + // The Bluetooth On state may be changed during system restore. + final String prevValue = intent.getStringExtra( + Intent.EXTRA_SETTING_PREVIOUS_VALUE); + final String newValue = intent.getStringExtra( + Intent.EXTRA_SETTING_NEW_VALUE); + + if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + + prevValue + ", newValue=" + newValue); + + if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) { + Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING, + newValue.equals("0") ? + RESTORE_SETTING_TO_OFF : + RESTORE_SETTING_TO_ON, 0); + mHandler.sendMessage(msg); + } + } } } }; @@ -348,12 +373,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); - IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mReceiver, filter); - filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + filter.addAction(Intent.ACTION_SETTING_RESTORED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); + loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); @@ -1424,6 +1451,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; + case MESSAGE_RESTORE_USER_SETTING: + try { + if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { + if (DBG) Slog.d(TAG, "Restore Bluetooth state to disabled"); + disable(REASON_RESTORE_USER_SETTING, true); + } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { + if (DBG) Slog.d(TAG, "Restore Bluetooth state to enabled"); + enable(REASON_RESTORE_USER_SETTING); + } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to change Bluetooth On setting", e); + } + break; + case MESSAGE_REGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; -- GitLab From ed46cbc070f20aef76931b8481a389eb2b1548dc Mon Sep 17 00:00:00 2001 From: Hemant Gupta Date: Tue, 30 Jul 2013 16:09:20 +0530 Subject: [PATCH 0784/1408] Bluetooth: HID: Add support for Set Idle and Get Idle Commands (3/4) Provides an interface for application to send Set Idle and Get Idle commands to remote HID Device. Support for these two commands was missing from existing code, so existing code design is reused to add support for these two commands. Without this support following mandatory PTS test cases for HID 1.0 cannot be passed, TC_HOS_HID_BV_05/06. Test: Executed PTS tests TC_HOS_HID_BV_05/06 and confirmed if they can pass Bug: 34344715 Change-Id: I21f37d7638c80da99a69c38d55d741b5fb107961 (cherry picked from commit cef9ce368c5f3d50913c37ea73d5589371faa11f) --- .../bluetooth/BluetoothInputDevice.java | 61 +++++++++++++++++++ .../bluetooth/IBluetoothInputDevice.aidl | 8 +++ 2 files changed, 69 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index e3288f3c7c8..a5a02435e37 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -97,6 +97,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_TIME_CHANGED = + "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. @@ -200,6 +206,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -659,6 +670,56 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } + + /** + * Send Get_Idle_Time command to the connected HID input device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean getIdleTime(BluetoothDevice device) { + if (DBG) log("getIdletime(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getIdleTime(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Idle_Time command to the connected HID input device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param idleTime Idle time to be set on HID Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setIdleTime(BluetoothDevice device, byte idleTime) { + if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setIdleTime(device, idleTime); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl index 1ebb9ca6eb1..5bd3f781932 100644 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -56,4 +56,12 @@ interface IBluetoothInputDevice { * @hide */ boolean sendData(in BluetoothDevice device, String report); + /** + * @hide + */ + boolean getIdleTime(in BluetoothDevice device); + /** + * @hide + */ + boolean setIdleTime(in BluetoothDevice device, byte idleTime); } -- GitLab From a87b1d276beacf1b05b17a6d0be2f0e330fb0157 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 1 May 2017 21:27:31 -0700 Subject: [PATCH 0785/1408] Add Save and Restore of BluetoothOn setting This change will automatically save the Bluetooth On setting when the user chooses to backup the phone settings into the cloud. This setting is restored by the Setup Wizard (SUW) when configuring the phone and this change will enable or disable the Bluetooth based on this restored setting. Bug: 35657817 Test: Manual test with Sailfish Change-Id: Ie4518593af63f96f8c363f98941ca5260a3ec4bb (cherry picked from commit 767f05feea67e642a76bd3e2e7633a8f5273f077) --- .../bluetooth/BluetoothManagerService.java | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c785fb91e8f..b65f54ecbda 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -91,6 +91,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String REASON_SYSTEM_BOOT = "system boot"; private static final String REASON_UNEXPECTED = "unexpected crash"; private static final String REASON_USER_SWITCH = "user switch"; + private static final String REASON_RESTORE_USER_SETTING = "restore user setting"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind //Maximum msec to wait for service restart @@ -119,6 +120,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_USER_UNLOCKED = 301; private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; + private static final int MESSAGE_RESTORE_USER_SETTING = 500; + + private static final int RESTORE_SETTING_TO_ON = 1; + private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; @@ -318,6 +323,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); } + } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { + final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); + if (Settings.Global.BLUETOOTH_ON.equals(name)) { + // The Bluetooth On state may be changed during system restore. + final String prevValue = intent.getStringExtra( + Intent.EXTRA_SETTING_PREVIOUS_VALUE); + final String newValue = intent.getStringExtra( + Intent.EXTRA_SETTING_NEW_VALUE); + + if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + + prevValue + ", newValue=" + newValue); + + if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) { + Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING, + newValue.equals("0") ? + RESTORE_SETTING_TO_OFF : + RESTORE_SETTING_TO_ON, 0); + mHandler.sendMessage(msg); + } + } } } }; @@ -350,12 +375,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); - IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mReceiver, filter); - filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + filter.addAction(Intent.ACTION_SETTING_RESTORED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); + loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); @@ -1421,6 +1448,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; + case MESSAGE_RESTORE_USER_SETTING: + try { + if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { + if (DBG) Slog.d(TAG, "Restore Bluetooth state to disabled"); + disable(REASON_RESTORE_USER_SETTING, true); + } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { + if (DBG) Slog.d(TAG, "Restore Bluetooth state to enabled"); + enable(REASON_RESTORE_USER_SETTING); + } + } catch (RemoteException e) { + Slog.e(TAG,"Unable to change Bluetooth On setting", e); + } + break; + case MESSAGE_REGISTER_ADAPTER: { IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; -- GitLab From b722a1f11344499cc226a82b525fd4636cb1b676 Mon Sep 17 00:00:00 2001 From: Ram Periathiruvadi Date: Wed, 3 May 2017 19:11:20 -0700 Subject: [PATCH 0786/1408] Fix for Bluetooth not restarting on User Switch The flag that keeps track of the Bluetooth adapter status(mEnable) is not guarded correctly and could go out of sync with the adapter status. This causes User Switch handling to not function correctly since it checks for the status of the mEnable flag before restarting Bluetooth. This fix queries the Bluetooth Adapter for the status instead of using the mEnable flag. Bug: b/37648701 Test: Tested switching users multiple times in both phone and carkit. Also tested pairing and connection for sanity. Change-Id: I17facecf8e1de90df11c11d5b0a184f48a0356e4 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5a3c7d0aef3..ba1befdf817 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1699,7 +1699,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_USER_SWITCHED); /* disable and enable BT when detect a user switch */ - if (mEnable && mBluetooth != null) { + if (mBluetooth != null && isEnabled()) { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { @@ -1768,6 +1768,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_OFF; // enable addActiveLog(REASON_USER_SWITCH, true); + // mEnable flag could have been reset on disableBLE. Reenable it. + mEnable = true; handleEnable(mQuietEnable); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); -- GitLab From ef23f2bc22c17992afe327a5a40ef86194e83086 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 16 May 2017 10:56:35 -0700 Subject: [PATCH 0787/1408] Discover primary service by UUID for PTS tests (4/4) Bug: 38123054 Test: manual Change-Id: I89e088e60f4325f1ece59d22efda0c907a3b716a --- .../java/android/bluetooth/BluetoothGatt.java | 26 ++++++++++++++++++- .../android/bluetooth/IBluetoothGatt.aidl | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 3135b30ebcc..b12ff72a50c 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.content.Context; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -926,6 +925,31 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Discovers a service by UUID. This is exposed only for passing PTS tests. + * It should never be used by real applications. The service is not searched + * for characteristics and descriptors, or returned in any callback. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the remote service discovery has been started + * @hide + */ + public boolean discoverServiceByUuid(UUID uuid) { + if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress()); + if (mService == null || mClientIf == 0) return false; + + mServices.clear(); + + try { + mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid)); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + return true; + } + /** * Returns a list of GATT services offered by the remote device. * diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 63bd9428464..4ff59765576 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -76,6 +76,7 @@ interface IBluetoothGatt { void clientReadPhy(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); + void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, in int startHandle, in int endHandle, in int authReq); -- GitLab From c7ac6cd061c8d8ef35b2611d8240de6be3bd3bc7 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 16 May 2017 10:56:35 -0700 Subject: [PATCH 0788/1408] Discover primary service by UUID for PTS tests (4/4) Bug: 38123054 Test: manual Change-Id: I89e088e60f4325f1ece59d22efda0c907a3b716a (cherry picked from commit fe2bf16a2b287c3c748cd6fa7c14026becfe83ff) --- .../java/android/bluetooth/BluetoothGatt.java | 26 ++++++++++++++++++- .../android/bluetooth/IBluetoothGatt.aidl | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 5fabbb68df8..a3d6e9f1e49 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.content.Context; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -926,6 +925,31 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Discovers a service by UUID. This is exposed only for passing PTS tests. + * It should never be used by real applications. The service is not searched + * for characteristics and descriptors, or returned in any callback. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the remote service discovery has been started + * @hide + */ + public boolean discoverServiceByUuid(UUID uuid) { + if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress()); + if (mService == null || mClientIf == 0) return false; + + mServices.clear(); + + try { + mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid)); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + return true; + } + /** * Returns a list of GATT services offered by the remote device. * diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index dc5c7b6b620..167f5e9e108 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -80,6 +80,7 @@ interface IBluetoothGatt { void clientReadPhy(in int clientIf, in String address); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); + void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid); void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, in int startHandle, in int endHandle, in int authReq); -- GitLab From bfadc5c8040cc7b08a196ebaf02c1700d252476b Mon Sep 17 00:00:00 2001 From: Ram Periathiruvadi Date: Wed, 3 May 2017 19:11:20 -0700 Subject: [PATCH 0789/1408] Fix for Bluetooth not restarting on User Switch The flag that keeps track of the Bluetooth adapter status(mEnable) is not guarded correctly and could go out of sync with the adapter status. This causes User Switch handling to not function correctly since it checks for the status of the mEnable flag before restarting Bluetooth. This fix queries the Bluetooth Adapter for the status instead of using the mEnable flag. Bug: b/37648701 Test: Tested switching users multiple times in both phone and carkit. Also tested pairing and connection for sanity. Change-Id: I17facecf8e1de90df11c11d5b0a184f48a0356e4 (cherry picked from commit 7d1203a2f4fdbafe26d33421c3cdd83f07a5fba8) --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b65f54ecbda..61057dd2544 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1697,7 +1697,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_USER_SWITCHED); /* disable and enable BT when detect a user switch */ - if (mEnable && mBluetooth != null) { + if (mBluetooth != null && isEnabled()) { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { @@ -1766,6 +1766,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_OFF; // enable addActiveLog(REASON_USER_SWITCH, true); + // mEnable flag could have been reset on disableBLE. Reenable it. + mEnable = true; handleEnable(mQuietEnable); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); -- GitLab From 9d4d34eae33c9d0a9b4c68a850015fba40ab9e02 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 26 May 2017 13:59:55 -0700 Subject: [PATCH 0790/1408] Add 32 and 128 bit Service Data parsing. Bug: 62078132 Change-Id: I79ff75cd5ccbe346dca79693b074ff3cd09112dd --- .../java/android/bluetooth/le/ScanRecord.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index f802e8d1295..914e8fd9e07 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -47,7 +47,9 @@ public final class ScanRecord { private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA = 0x16; + private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16; + private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20; + private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21; private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; // Flags of the advertising data. @@ -224,10 +226,16 @@ public final class ScanRecord { case DATA_TYPE_TX_POWER_LEVEL: txPowerLevel = scanRecord[currentPos]; break; - case DATA_TYPE_SERVICE_DATA: - // The first two bytes of the service data are service data UUID in little - // endian. The rest bytes are service data. + case DATA_TYPE_SERVICE_DATA_16_BIT: + case DATA_TYPE_SERVICE_DATA_32_BIT: + case DATA_TYPE_SERVICE_DATA_128_BIT: int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; + if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) { + serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; + } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) { + serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; + } + byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( -- GitLab From 71f6d4fea0db0c0666919885f493ff1c5939eaca Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 26 May 2017 13:59:55 -0700 Subject: [PATCH 0791/1408] Add 32 and 128 bit Service Data parsing. Bug: 62078132 Change-Id: I79ff75cd5ccbe346dca79693b074ff3cd09112dd (cherry picked from commit 45033a7e5799f2d957c7dad22eec0bfef94b3e16) --- .../java/android/bluetooth/le/ScanRecord.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index f802e8d1295..914e8fd9e07 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -47,7 +47,9 @@ public final class ScanRecord { private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA = 0x16; + private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16; + private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20; + private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21; private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; // Flags of the advertising data. @@ -224,10 +226,16 @@ public final class ScanRecord { case DATA_TYPE_TX_POWER_LEVEL: txPowerLevel = scanRecord[currentPos]; break; - case DATA_TYPE_SERVICE_DATA: - // The first two bytes of the service data are service data UUID in little - // endian. The rest bytes are service data. + case DATA_TYPE_SERVICE_DATA_16_BIT: + case DATA_TYPE_SERVICE_DATA_32_BIT: + case DATA_TYPE_SERVICE_DATA_128_BIT: int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; + if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) { + serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; + } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) { + serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; + } + byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, serviceUuidLength); ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( -- GitLab From 798d72888a442c6e3fdd52f8a8c2219be012af48 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 9 May 2017 17:16:01 -0700 Subject: [PATCH 0792/1408] HFP: Allow SCO audio to be forcibly connected * Normally, Android only allows SCO audio to be connected in several allowed cases (e.g. in call, in ringing, in virtual call state, etc) * Sometimes, it is necessary to force a SCO audio connection (e.g. during PTS test) * This change adds setForceScoAudio(boolean) hidden, system only method to allow such behaviour Bug: 38040125 Test: PTS HFP tests Change-Id: I28f295c98da5bebb837c2a5423c7acde81af73f7 --- .../android/bluetooth/BluetoothHeadset.java | 22 +++++++++++++++++++ .../android/bluetooth/IBluetoothHeadset.aidl | 1 + 2 files changed, 23 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 2d25659bd77..c31a9b20e28 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -766,6 +766,28 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Force SCO audio to be opened regardless any other restrictions + * + * @param forced Whether or not SCO audio connection should be forced: + * True to force SCO audio + * False to use SCO audio in normal manner + * @hide + */ + public void setForceScoAudio(boolean forced) { + if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); + if (mService != null && isEnabled()) { + try { + mService.setForceScoAudio(forced); + } 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())); + } + } + /** * Check if Bluetooth SCO audio is connected. * diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl index 6ad442b6138..6bd0d7fb43b 100755 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/framework/java/android/bluetooth/IBluetoothHeadset.aidl @@ -52,6 +52,7 @@ interface IBluetoothHeadset { boolean disconnectAudio(); void setAudioRouteAllowed(boolean allowed); boolean getAudioRouteAllowed(); + void setForceScoAudio(boolean forced); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); -- GitLab From a9fdcc96dcbcba072896a8440d1558677911dabd Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 2 Jun 2017 17:36:26 -0600 Subject: [PATCH 0793/1408] Annotate @SystemApi with required permissions. Most @SystemApi methods should be protected with system (or higher) permissions, so annotate common methods with @RequiresPermission to make automatic verification easier. Verification is really only relevant when calling into system services (where permissions checking can happen on the other side of a Binder call), so annotate managers with the new @SystemService annotation, which is now automatically documented. This is purely a docs change; no logic changes are being made. Test: make -j32 update-api && make -j32 offline-sdk-docs Bug: 62263906 Change-Id: I2554227202d84465676aa4ab0dd336b5c45fc651 --- framework/java/android/bluetooth/BluetoothManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index c7191ba2638..e2fa38a9309 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.util.Log; @@ -48,6 +49,7 @@ import java.util.List; * @see Context#getSystemService * @see BluetoothAdapter#getDefaultAdapter() */ +@SystemService(Context.BLUETOOTH_SERVICE) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = true; -- GitLab From 9b6692e60c696c499287f6c6476064c10836b315 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 7 Jun 2017 13:45:33 -0700 Subject: [PATCH 0794/1408] Bluetooth: fix connectGatt overload invocations Test: sl4a GattOverBrEdrTest Bug: 62387078 Change-Id: I5bfb87683e25f8862587e8000eb4c9bb6ff14f99 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f158f5f29ec..6bc88b03a12 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1669,7 +1669,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK)); + return (connectGatt(context, autoConnect,callback, transport, PHY_LE_1M_MASK)); } /** @@ -1693,7 +1693,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy) { - return connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK, null); + return connectGatt(context, autoConnect,callback, transport, phy, null); } /** -- GitLab From 6f696471c3f9324c9f0ffef484a9b742ce3e28a3 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 7 Jun 2017 13:45:33 -0700 Subject: [PATCH 0795/1408] Bluetooth: fix connectGatt overload invocations Merged-In: I726252e85e78bff2c9585332d2748a0d92a9f6d3 Test: sl4a GattOverBrEdrTest Bug: 62387078 Change-Id: I5bfb87683e25f8862587e8000eb4c9bb6ff14f99 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index e8ad69d2f52..7ff37d29323 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1658,7 +1658,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK)); + return (connectGatt(context, autoConnect,callback, transport, PHY_LE_1M_MASK)); } /** @@ -1682,7 +1682,7 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy) { - return connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M_MASK, null); + return connectGatt(context, autoConnect,callback, transport, phy, null); } /** -- GitLab From f88dbc1eb4a6f1afe8453f4c45c198fbc1ef5d34 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 13 Jun 2017 18:59:07 -0700 Subject: [PATCH 0796/1408] Remove legacy advertiser from map after it's no longer used Test: manual Bug: 62597369 Change-Id: Ief1e1b054f29b59b1ed72514b138d4f209f9847a --- framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 67d56d5060e..dfd5996c6a0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -208,6 +208,8 @@ public final class BluetoothLeAdvertiser { if (wrapper == null) return; stopAdvertisingSet(wrapper); + + mLegacyAdvertisers.remove(callback); } } -- GitLab From 8e858ecae3cec8f673d119b9c96b6e9b32c30b80 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 13 Jun 2017 18:59:07 -0700 Subject: [PATCH 0797/1408] Remove legacy advertiser from map after it's no longer used Test: manual Bug: 62597369 Change-Id: Ief1e1b054f29b59b1ed72514b138d4f209f9847a --- framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 67d56d5060e..dfd5996c6a0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -208,6 +208,8 @@ public final class BluetoothLeAdvertiser { if (wrapper == null) return; stopAdvertisingSet(wrapper); + + mLegacyAdvertisers.remove(callback); } } -- GitLab From cb75a93a765813ba3a0b87204a37944592c352ab Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Wed, 21 Jun 2017 14:06:44 -0700 Subject: [PATCH 0798/1408] Clarify startScan API on how to get results Make it clear in BluetoothLeScanner on how to get results when starting a scan for a PendingIntent. Bug: 38365430 Test: make offline-sdk-docs Change-Id: I0bf88d751c89a8a478db985713357e153ac08595 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index f3f0ae5cd95..1eac395bd06 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -136,6 +136,11 @@ public final class BluetoothLeScanner { * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via * the PendingIntent. Use this method of scanning if your process is not always running and it * should be started when scan results are available. + *

        + * When the PendingIntent is delivered, the Intent passed to the receiver or activity + * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, + * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of + * the scan. * * @param filters Optional list of ScanFilters for finding exact BLE devices. * @param settings Optional settings for the scan. -- GitLab From 36354fb05c282dcb0e4b27bd4422fa424b7bc032 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Thu, 12 Jan 2017 16:00:30 -0800 Subject: [PATCH 0799/1408] Bluetooth: minor documentation fix to de-confuse It was somewhat unclear which is the preferred method of retrieving a BluetoothAdapter. Make it clear that using BluetoothManager is preferred, and remove references to the old method in BluetoothManager docs since it is only avaialable in API 18 or higher. Test: recompile, check that documentation is updated Fix: 33355430 Change-Id: Ia20b4e45dca03bc2f13c2ab477799b89c5e14f45 --- .../java/android/bluetooth/BluetoothAdapter.java | 15 ++++++++------- .../java/android/bluetooth/BluetoothManager.java | 5 +---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ff52f27447c..838fb72ea3a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -70,9 +70,10 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * devices, and start a scan for Bluetooth LE devices. * *

        To get a {@link BluetoothAdapter} representing the local Bluetooth - * adapter, when running on JELLY_BEAN_MR1 and below, call the - * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and - * higher, call {@link BluetoothManager#getAdapter}. + * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}. + * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter} + * method instead. + *

        * Fundamentally, this is your starting point for all * Bluetooth actions. Once you have the local adapter, you can get a set of * {@link BluetoothDevice} objects representing all paired devices with @@ -81,14 +82,13 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * listen for incoming connection requests with * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. - * - *

        This class is thread safe. - * + *

        + *

        This class is thread safe.

        *

        Note: * Most methods require the {@link android.Manifest.permission#BLUETOOTH} * permission and some also require the * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * + *

        *
        *

        Developer Guides

        *

        @@ -565,6 +565,7 @@ public final class BluetoothAdapter { *

        Currently Android only supports one Bluetooth adapter, but the API * could be extended to support more. This will always return the default * adapter. + *

        * @return the default local adapter, or null if Bluetooth is not supported * on this hardware platform */ diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 29283e793ce..4c21aae13ed 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -32,10 +32,7 @@ import java.util.List; * Use {@link android.content.Context#getSystemService(java.lang.String)} * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager}, * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}. - *

        - * Alternately, you can just call the static helper - * {@link BluetoothAdapter#getDefaultAdapter()}. - * + *

        *
        *

        Developer Guides

        *

        -- GitLab From c725b95b750fbdb6cd481561ecc6ba9d7214a051 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 16 Jun 2017 19:43:58 -0700 Subject: [PATCH 0800/1408] Add APIs to get remote device's battery level (1/2) * Add BluetoothDevice.getBatteryLevel() API to retreive battery level information of remote device * Add BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED intent to notify user that remote device's battery level has changed Bug: 35874078 Test: make, pair with devices and use them Change-Id: I41051ee25383f5f3a1e505aef6f8c526385f58bd --- .../android/bluetooth/BluetoothDevice.java | 49 +++++++++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 11 ++--- .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6bc88b03a12..a72ae847736 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -202,6 +202,34 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED"; + /** + * Broadcast Action: Indicates the battery level of a remote device has + * been retrieved for the first time, or changed since the last retrieval + *

        Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_BATTERY_LEVEL}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BATTERY_LEVEL_CHANGED = + "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"; + + /** + * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED} + * intent. It contains the most recently retrieved battery level information + * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN} + * when the valid is unknown or there is an error + * @hide + */ + public static final String EXTRA_BATTERY_LEVEL = + "android.bluetooth.device.extra.BATTERY_LEVEL"; + + /** + * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()} + * @hide + */ + public static final int BATTERY_LEVEL_UNKNOWN = -1; + /** * Used as a Parcelable {@link BluetoothDevice} extra field in every intent * broadcast by this class. It contains the {@link BluetoothDevice} that @@ -863,6 +891,27 @@ public final class BluetoothDevice implements Parcelable { return name; } + /** + * Get the most recent identified battery level of this Bluetooth device + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if + * Bluetooth is disabled, or device is disconnected, or does not have any battery + * reporting service, or return value is invalid + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getBatteryLevel() { + if (sService == null) { + Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); + return BATTERY_LEVEL_UNKNOWN; + } + try { + return sService.getBatteryLevel(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BATTERY_LEVEL_UNKNOWN; + } + /** * Start the bonding (pairing) process with the remote device. *

        This is an asynchronous call, it will return immediately. Register diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c31a9b20e28..da9b50a5669 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -226,14 +226,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by - the headset ( as indicated by AT+BIND - command in the SLC sequence).or whose value - is changed (indicated by AT+BIEV command)
        • - *
        • {@link #EXTRA_IND_VALUE}- The updated value of headset indicator.
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which + * is supported by the headset ( as indicated by AT+BIND command in the SLC + * sequence).or whose value is changed (indicated by AT+BIEV command)
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE}- The updated value of headset indicator.
        • *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        - *

        {@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are + *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators are * given an assigned number. Below shows the assigned number of Indicator added so far * - Enhanced Safety - 1 *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 43c5ae4407c..1d7cfc900e4 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -75,6 +75,7 @@ interface IBluetooth ParcelUuid[] getRemoteUuids(in BluetoothDevice device); boolean fetchRemoteUuids(in BluetoothDevice device); boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); + int getBatteryLevel(in BluetoothDevice device); boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] -- GitLab From 48f6a8a5c8b3cc9ab3e1a6e638b314c187e8e299 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 22 Jun 2017 12:56:54 -0700 Subject: [PATCH 0801/1408] HFP: Modify comments for ACTION_HF_INDICATORS_VALUE_CHANGED (1/2) * Per HFP 1.7.1 spec page 102 of 144 EXTRA_HF_INDICATORS_IND_ID should be int EXTRA_HF_INDICATORS_IND_VALUE should be int Bug: 35874078 Test: make, PTS test, unit tests runtest -c com.android.bluetooth.btservice.RemoteDevicesTest bluetooth Change-Id: Idd316a82bab164b6ea1701bb261b1cc3dee196f1 --- .../android/bluetooth/BluetoothHeadset.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index da9b50a5669..9414a8d349f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -228,31 +228,31 @@ public final class BluetoothHeadset implements BluetoothProfile { *

          *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which * is supported by the headset ( as indicated by AT+BIND command in the SLC - * sequence).or whose value is changed (indicated by AT+BIEV command)
        • - *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE}- The updated value of headset indicator.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + * sequence) or whose value is changed (indicated by AT+BIEV command) + *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - Remote device.
        • *
        - *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators are - * given an assigned number. Below shows the assigned number of Indicator added so far - * - Enhanced Safety - 1 - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. + *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators + * are given an assigned number. Below shows the assigned number of Indicator added so far + * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled + * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. * @hide */ public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; /** - * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} - * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG) - * that is being sent. + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the assigned number of the headset indicator as defined by + * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7 * @hide */ public static final String EXTRA_HF_INDICATORS_IND_ID = "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; /** - * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} * intents that contains the value of the Headset indicator that is being sent. * @hide */ -- GitLab From 7f9c70bc2299cdddae91a2d1a17f7d2e119a7f29 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 20 Jun 2017 17:07:40 -0700 Subject: [PATCH 0802/1408] Support battery level reporting via Plantronics XEVENT (1/2) * Add related constants to BluetoothHeadset Bug: 35874078 Test: make, test with supporting headsets Change-Id: Ied089a065bf9caa0d03f681950fefa2ea990bb3e --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 9414a8d349f..82e76bdc823 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -198,6 +198,18 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT"; + + /** + * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT} + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY"; + /** * Headset state when SCO audio is not connected. * This state can be one of -- GitLab From 0dfd69bb2b63964a1fccaeef1c7db4e5182c99cb Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 20 Jun 2017 17:09:47 -0700 Subject: [PATCH 0803/1408] Support battery level reporting via Apple VSC (1/2) * Add related constants to BluetoothHeadset Bug: 35874078 Test: make, test with supporting headsets Change-Id: I9f49ea28efdf6de0e751a377d879fe13a47655d5 --- .../android/bluetooth/BluetoothHeadset.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 82e76bdc823..336f330b163 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -198,6 +198,25 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL"; + + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV"; + + /** + * Battery level indicator associated with + * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV} + * @hide + */ + public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1; + /** * A vendor-specific AT command * @hide -- GitLab From 3ffc5f1ea83be3f643558e71af7bd7acd881a183 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 16 Jun 2017 19:43:58 -0700 Subject: [PATCH 0804/1408] Add APIs to get remote device's battery level (1/2) * Add BluetoothDevice.getBatteryLevel() API to retreive battery level information of remote device * Add BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED intent to notify user that remote device's battery level has changed Bug: 35874078 Test: make, pair with devices and use them Change-Id: I41051ee25383f5f3a1e505aef6f8c526385f58bd Merged-In: I41051ee25383f5f3a1e505aef6f8c526385f58bd (cherry picked from commit 1d312bfa78c25e0e1d6ea25b2c027e2efdd5a418) --- .../android/bluetooth/BluetoothDevice.java | 49 +++++++++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 11 ++--- .../java/android/bluetooth/IBluetooth.aidl | 1 + 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7ff37d29323..27b802e59c5 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -202,6 +202,34 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED"; + /** + * Broadcast Action: Indicates the battery level of a remote device has + * been retrieved for the first time, or changed since the last retrieval + *

        Always contains the extra fields {@link #EXTRA_DEVICE} and {@link + * #EXTRA_BATTERY_LEVEL}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BATTERY_LEVEL_CHANGED = + "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"; + + /** + * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED} + * intent. It contains the most recently retrieved battery level information + * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN} + * when the valid is unknown or there is an error + * @hide + */ + public static final String EXTRA_BATTERY_LEVEL = + "android.bluetooth.device.extra.BATTERY_LEVEL"; + + /** + * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()} + * @hide + */ + public static final int BATTERY_LEVEL_UNKNOWN = -1; + /** * Used as a Parcelable {@link BluetoothDevice} extra field in every intent * broadcast by this class. It contains the {@link BluetoothDevice} that @@ -860,6 +888,27 @@ public final class BluetoothDevice implements Parcelable { return name; } + /** + * Get the most recent identified battery level of this Bluetooth device + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if + * Bluetooth is disabled, or device is disconnected, or does not have any battery + * reporting service, or return value is invalid + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getBatteryLevel() { + if (sService == null) { + Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); + return BATTERY_LEVEL_UNKNOWN; + } + try { + return sService.getBatteryLevel(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return BATTERY_LEVEL_UNKNOWN; + } + /** * Start the bonding (pairing) process with the remote device. *

        This is an asynchronous call, it will return immediately. Register diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 57b954f40c6..5b8d81d81ef 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -227,14 +227,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_IND_ID} - The Assigned number of headset Indicator which is supported by - the headset ( as indicated by AT+BIND - command in the SLC sequence).or whose value - is changed (indicated by AT+BIEV command)
        • - *
        • {@link #EXTRA_IND_VALUE}- The updated value of headset indicator.
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which + * is supported by the headset ( as indicated by AT+BIND command in the SLC + * sequence).or whose value is changed (indicated by AT+BIEV command)
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE}- The updated value of headset indicator.
        • *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        - *

        {@link #EXTRA_IND_ID} is defined by Bluetooth SIG and each of the indicators are + *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators are * given an assigned number. Below shows the assigned number of Indicator added so far * - Enhanced Safety - 1 *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl index 43c5ae4407c..1d7cfc900e4 100644 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ b/framework/java/android/bluetooth/IBluetooth.aidl @@ -75,6 +75,7 @@ interface IBluetooth ParcelUuid[] getRemoteUuids(in BluetoothDevice device); boolean fetchRemoteUuids(in BluetoothDevice device); boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); + int getBatteryLevel(in BluetoothDevice device); boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] -- GitLab From 61a1de5cc7223101b6e4c09f755f2db7055dc486 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 22 Jun 2017 12:56:54 -0700 Subject: [PATCH 0805/1408] HFP: Modify comments for ACTION_HF_INDICATORS_VALUE_CHANGED (1/2) * Per HFP 1.7.1 spec page 102 of 144 EXTRA_HF_INDICATORS_IND_ID should be int EXTRA_HF_INDICATORS_IND_VALUE should be int Bug: 35874078 Test: make, PTS test, unit tests runtest -c com.android.bluetooth.btservice.RemoteDevicesTest bluetooth Change-Id: Idd316a82bab164b6ea1701bb261b1cc3dee196f1 Merged-In: Idd316a82bab164b6ea1701bb261b1cc3dee196f1 (cherry picked from commit 73795440a54a5407284331ac11f8c0f75acd98d6) --- .../android/bluetooth/BluetoothHeadset.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 5b8d81d81ef..788732c49e5 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -229,31 +229,31 @@ public final class BluetoothHeadset implements BluetoothProfile { *

          *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which * is supported by the headset ( as indicated by AT+BIND command in the SLC - * sequence).or whose value is changed (indicated by AT+BIEV command)
        • - *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE}- The updated value of headset indicator.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + * sequence) or whose value is changed (indicated by AT+BIEV command) + *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - Remote device.
        • *
        - *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators are - * given an assigned number. Below shows the assigned number of Indicator added so far - * - Enhanced Safety - 1 - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. + *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators + * are given an assigned number. Below shows the assigned number of Indicator added so far + * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled + * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. * @hide */ public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; /** - * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} - * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG) - * that is being sent. + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * intents that contains the assigned number of the headset indicator as defined by + * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7 * @hide */ public static final String EXTRA_HF_INDICATORS_IND_ID = "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; /** - * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} + * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} * intents that contains the value of the Headset indicator that is being sent. * @hide */ -- GitLab From 9dbc30d0ba1d448e8c3f74583cf437173026094a Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 20 Jun 2017 17:07:40 -0700 Subject: [PATCH 0806/1408] Support battery level reporting via Plantronics XEVENT (1/2) * Add related constants to BluetoothHeadset Bug: 35874078 Test: make, test with supporting headsets Change-Id: Ied089a065bf9caa0d03f681950fefa2ea990bb3e Merged-In: Ied089a065bf9caa0d03f681950fefa2ea990bb3e (cherry picked from commit 0fcbba25b7ac7fd59367c8c9ba3080a3e6ade934) --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 788732c49e5..900f005273c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -199,6 +199,18 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT"; + + /** + * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT} + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY"; + /** * Headset state when SCO audio is not connected. * This state can be one of -- GitLab From d35a189f013f491461e250d60f992d884ab0c5eb Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 20 Jun 2017 17:09:47 -0700 Subject: [PATCH 0807/1408] Support battery level reporting via Apple VSC (1/2) * Add related constants to BluetoothHeadset Bug: 35874078 Test: make, test with supporting headsets Change-Id: I9f49ea28efdf6de0e751a377d879fe13a47655d5 Merged-In: I9f49ea28efdf6de0e751a377d879fe13a47655d5 (cherry picked from commit 679d0bc8f99145d70af0c8006d807b55110fe3b5) --- .../android/bluetooth/BluetoothHeadset.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 900f005273c..c84643fc46b 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -199,6 +199,25 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL"; + + /** + * A vendor-specific AT command + * @hide + */ + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV"; + + /** + * Battery level indicator associated with + * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV} + * @hide + */ + public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1; + /** * A vendor-specific AT command * @hide -- GitLab From bcac61c17129d70e26354655e56c9bca5a1a3c75 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Thu, 29 Jun 2017 03:12:02 -0700 Subject: [PATCH 0808/1408] Send disable messages when Bluetooth is disallowed Bug: 63021124 Test: cts-tradefed run commandAndExit cts \ -m CtsDevicePolicyManagerTestCases \ -t com.android.cts.devicepolicy.DeviceOwnerTest#testBluetoothRestriction Change-Id: I0ef538a4d0f0632ce303b29595ac1cab3b7d29ce --- .../bluetooth/BluetoothManagerService.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 61057dd2544..75206e48aa8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -86,6 +86,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ACTIVE_LOG_MAX_SIZE = 20; private static final int CRASH_LOG_MAX_SIZE = 100; private static final String REASON_AIRPLANE_MODE = "airplane mode"; + private static final String REASON_DISALLOWED = "disallowed by system"; + private static final String REASON_SHARING_DISALLOWED = "sharing disallowed by system"; private static final String REASON_RESTARTED = "automatic restart"; private static final String REASON_START_CRASH = "turn-on crash"; private static final String REASON_SYSTEM_BOOT = "system boot"; @@ -227,25 +229,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { - if (!UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, - UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH_SHARING)) { - return; // No relevant changes, nothing to do. - } - final boolean disallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); + if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, + UserManager.DISALLOW_BLUETOOTH_SHARING)) { + updateOppLauncherComponentState(userId, newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH_SHARING)); + } - // DISALLOW_BLUETOOTH is a global restriction that can only be set by DO or PO on the - // system user, so we only look at the system user. - if (userId == UserHandle.USER_SYSTEM && disallowed && (mEnable || mEnableExternal)) { - try { - disable(null /* packageName */, true /* persist */); - } catch (RemoteException e) { - Slog.w(TAG, "Exception when disabling Bluetooth", e); + // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. + if (userId == UserHandle.USER_SYSTEM && + UserRestrictionsUtils.restrictionsChanged( + prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { + if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH)) { + updateOppLauncherComponentState(userId, true); // Sharing disallowed + sendDisableMsg(REASON_DISALLOWED); + } else { + updateOppLauncherComponentState(userId, newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH_SHARING)); } } - final boolean sharingDisallowed = disallowed - || newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING); - updateOppLauncherComponentState(userId, sharingDisallowed); } }; @@ -2118,7 +2121,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; try { final IPackageManager imp = AppGlobals.getPackageManager(); - imp.setComponentEnabledSetting(oppLauncherComponent, newState, 0 /* flags */, userId); + imp.setComponentEnabledSetting(oppLauncherComponent, newState, + PackageManager.DONT_KILL_APP, userId); } catch (Exception e) { // The component was not found, do nothing. } -- GitLab From dafdf2656b11d8600fbfcc20cbee1966cc5c6d5a Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 5 Jul 2017 14:55:35 -0700 Subject: [PATCH 0809/1408] GATT: Expose opportunistic client API to Java * Allow Java based programs to create an opportunistic GATT client * Such client does not hold a GATT connection. It automatically disconnects when no other GATT connections are active for the remote device. Bug: 63347806 Test: make, run battery service Change-Id: Ib9333817d7f17a1fa3ddacfa51c680890bac19ec --- .../android/bluetooth/BluetoothDevice.java | 34 ++++++++++++++++++- .../java/android/bluetooth/BluetoothGatt.java | 10 +++--- .../android/bluetooth/IBluetoothGatt.aidl | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a72ae847736..78d7c54787e 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1769,6 +1769,38 @@ public final class BluetoothDevice implements Parcelable { public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy, Handler handler) { + return connectGatt(context, autoConnect, callback, transport, false, phy, handler); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client + * does not hold a GATT connection. It automatically disconnects when no + * other GATT connections are active for the remote device. + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen + * on an un-specified background thread. + * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client + * operations. + * @hide + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport, + boolean opportunistic, int phy, Handler handler) { if (callback == null) throw new NullPointerException("callback is null"); @@ -1782,7 +1814,7 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); + BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy); gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index b12ff72a50c..1307f076b8b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -53,6 +53,7 @@ public final class BluetoothGatt implements BluetoothProfile { private Boolean mDeviceBusy = false; private int mTransport; private int mPhy; + private boolean mOpportunistic; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -172,7 +173,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport, mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -628,11 +629,12 @@ public final class BluetoothGatt implements BluetoothProfile { }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport, int phy) { + int transport, boolean opportunistic, int phy) { mService = iGatt; mDevice = device; mTransport = transport; mPhy = phy; + mOpportunistic = opportunistic; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -839,8 +841,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean connect() { try { - mService.clientConnect(mClientIf, mDevice.getAddress(), - false, mTransport, mPhy); // autoConnect is inverse of "isDirect" + mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, + mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 4ff59765576..05cef600c42 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -70,7 +70,7 @@ interface IBluetoothGatt { void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy); void clientDisconnect(in int clientIf, in String address); void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); void clientReadPhy(in int clientIf, in String address); -- GitLab From f92d0272d03a5367f243943d8ebd6d51582b6442 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 5 Jul 2017 14:55:35 -0700 Subject: [PATCH 0810/1408] GATT: Expose opportunistic client API to Java * Allow Java based programs to create an opportunistic GATT client * Such client does not hold a GATT connection. It automatically disconnects when no other GATT connections are active for the remote device. Bug: 63347806 Test: make, run battery service Change-Id: Ib9333817d7f17a1fa3ddacfa51c680890bac19ec Merged-In: Ib9333817d7f17a1fa3ddacfa51c680890bac19ec (cherry picked from commit dafdf2656b11d8600fbfcc20cbee1966cc5c6d5a) --- .../android/bluetooth/BluetoothDevice.java | 34 ++++++++++++++++++- .../java/android/bluetooth/BluetoothGatt.java | 10 +++--- .../android/bluetooth/IBluetoothGatt.aidl | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 27b802e59c5..a206b53b536 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1758,6 +1758,38 @@ public final class BluetoothDevice implements Parcelable { public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy, Handler handler) { + return connectGatt(context, autoConnect, callback, transport, false, phy, handler); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client + * does not hold a GATT connection. It automatically disconnects when no + * other GATT connections are active for the remote device. + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of + * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, + * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect + * if {@code autoConnect} is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen + * on an un-specified background thread. + * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client + * operations. + * @hide + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport, + boolean opportunistic, int phy, Handler handler) { if (callback == null) throw new NullPointerException("callback is null"); @@ -1771,7 +1803,7 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy); + BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy); gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a3d6e9f1e49..678159b7129 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -53,6 +53,7 @@ public final class BluetoothGatt implements BluetoothProfile { private Boolean mDeviceBusy = false; private int mTransport; private int mPhy; + private boolean mOpportunistic; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -172,7 +173,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport, mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -628,11 +629,12 @@ public final class BluetoothGatt implements BluetoothProfile { }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport, int phy) { + int transport, boolean opportunistic, int phy) { mService = iGatt; mDevice = device; mTransport = transport; mPhy = phy; + mOpportunistic = opportunistic; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -839,8 +841,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean connect() { try { - mService.clientConnect(mClientIf, mDevice.getAddress(), - false, mTransport, mPhy); // autoConnect is inverse of "isDirect" + mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, + mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl index 167f5e9e108..e87f0700722 100644 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ b/framework/java/android/bluetooth/IBluetoothGatt.aidl @@ -74,7 +74,7 @@ interface IBluetoothGatt { void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy); void clientDisconnect(in int clientIf, in String address); void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); void clientReadPhy(in int clientIf, in String address); -- GitLab From a9d404ec387fa0c605c78eca21ee097739e1ff51 Mon Sep 17 00:00:00 2001 From: xutianguo Date: Mon, 22 May 2017 14:03:33 +0800 Subject: [PATCH 0811/1408] Make BluetoothStateChangeCallback oneway to prevent waiting on response. Test: Connected bluetooth devices(included Headset/HID devices), and made bluetooth enabling/disabling over 1000 times, no other side effect observed. Bug: 38485770 Change-Id: Ia3959d2441aece39a79ab2d662b57790a78df674 Signed-off-by: xutianguo (cherry picked from commit 95e1e21a639318bb3399c394707624393cfa2300) --- .../java/android/bluetooth/IBluetoothStateChangeCallback.aidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl index feccdce57b9..0da4e884328 100644 --- a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -21,7 +21,7 @@ package android.bluetooth; * * {@hide} */ -interface IBluetoothStateChangeCallback +oneway interface IBluetoothStateChangeCallback { void onBluetoothStateChange(boolean on); } -- GitLab From 36b4f3ed3cf22486e558d1b1ee4f297218a9a4d7 Mon Sep 17 00:00:00 2001 From: Vinay Kalia Date: Fri, 28 Jul 2017 15:09:57 -0700 Subject: [PATCH 0812/1408] Fix BLE scan leaks If an app starts multiple scans with same callback then there is a collision in mLeScanClients hash map which results in leaking first scan. This change fixes it by not allowing the second scan with same callback. BUG: 62389939 BUG: 38198694 Test: Tested applications which started multiple scans with same callback. Change-Id: I569069a40b6f8b4b8bb070731225e732c6b23ec8 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 1eac395bd06..e3bc78e5a2b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -205,7 +205,8 @@ public final class BluetoothLeScanner { } synchronized (mLeScanClients) { if (callback != null && mLeScanClients.containsKey(callback)) { - postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_ALREADY_STARTED); } IBluetoothGatt gatt; try { -- GitLab From 49cdd0df0697809ddde41f72a232ea8922824123 Mon Sep 17 00:00:00 2001 From: Vinay Kalia Date: Fri, 28 Jul 2017 15:09:57 -0700 Subject: [PATCH 0813/1408] Fix BLE scan leaks If an app starts multiple scans with same callback then there is a collision in mLeScanClients hash map which results in leaking first scan. This change fixes it by not allowing the second scan with same callback. BUG: 62389939 BUG: 38198694 Test: Tested applications which started multiple scans with same callback. Change-Id: I569069a40b6f8b4b8bb070731225e732c6b23ec8 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 1eac395bd06..e3bc78e5a2b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -205,7 +205,8 @@ public final class BluetoothLeScanner { } synchronized (mLeScanClients) { if (callback != null && mLeScanClients.containsKey(callback)) { - postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_ALREADY_STARTED); } IBluetoothGatt gatt; try { -- GitLab From 574b1e693b9d0916003b046f77e7ca722bc08f2f Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 17 Apr 2017 22:35:45 -0700 Subject: [PATCH 0814/1408] DO NOT MERGE ANYWHERE Allow the Bluetooth MAC address to be updated asynchronously (2/3) There are intermittent issues where either the returned Bluetooth MAC address to Java framework is uninitialized or this address update arrives too late. This fix will do 2 things: (1) Returns error when MAC address is unavailable in the native code. (2) Updates the MAC address later by adding a new broadcast event. Test: Check address for these cases: factory reset, system reboot, and Bluetooth re-enable. Bug: 36709382 Merged-In: I09720193e38fdf9139e1bb146f8e1847e2b65b1a (cherry picked from commit 723ee54c608a9ed16738de6382a06ee281bd5f83) Change-Id: Ifae3adf6e2aad1f0811c03d3114d1bd0452e7c23 --- .../android/bluetooth/BluetoothAdapter.java | 24 +++++++++++++++++++ .../bluetooth/BluetoothManagerService.java | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 542b06b1360..e1a2a2f45a8 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -447,6 +447,30 @@ public final class BluetoothAdapter { public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; + /** + * Intent used to broadcast the change in the Bluetooth address + * of the local Bluetooth adapter. + *

        Always contains the extra field {@link + * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address. + * + * Note: only system level processes are allowed to send this + * defined broadcast. + * + * @hide + */ + public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = + "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; + + /** + * Used as a String extra field in {@link + * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local + * Bluetooth address. + * + * @hide + */ + public static final String EXTRA_BLUETOOTH_ADDRESS = + "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; + /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be * by BLE Always on enabled application to know the ACL_CONNECTED event diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d767c462c6d..97437c9579d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -187,6 +187,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (newName != null) { storeNameAndAddress(newName, null); } + } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) { + String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS); + if (newAddress != null) { + if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); + storeNameAndAddress(null, newAddress); + } else { + if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); + } } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { synchronized(mReceiver) { if (isBluetoothPersistedStateOn()) { @@ -275,6 +283,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { registerForAirplaneMode(filter); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); + filter = new IntentFilter(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(mReceiver, filter); loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); -- GitLab From 4d55372269e32a6f7a4df136ea994978d1f348a8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 8 Aug 2017 04:27:20 -0700 Subject: [PATCH 0815/1408] Bluetooth: move AIDL files related to bluetooth into system/bt (1/2) This patch moves *.aidl files from frameworks/base/core/java/android/bluetooth into system/bt/binder. This is in preparation to convert the Bluetooth deamon into native implementation piece by piece. In order to do that, one must have C++ header files, and paths to them with AIDL files, and */java/* folder didn't seem as proper place for that. Additionally, keeping AIDL files out of framework/base will not require creating dependency on this huge project, which should help keeping the compilation fast. Test: compilation test Change-Id: I9a6db8832c9ec3215c648e325d67278832ef22cc --- .../BluetoothActivityEnergyInfo.aidl | 19 --- .../bluetooth/BluetoothAudioConfig.aidl | 19 --- .../BluetoothAvrcpPlayerSettings.aidl | 19 --- .../bluetooth/BluetoothCodecConfig.aidl | 19 --- .../bluetooth/BluetoothCodecStatus.aidl | 19 --- .../android/bluetooth/BluetoothDevice.aidl | 19 --- .../BluetoothGattCharacteristic.aidl | 19 --- .../bluetooth/BluetoothGattDescriptor.aidl | 19 --- .../BluetoothGattIncludedService.aidl | 19 --- .../bluetooth/BluetoothGattService.aidl | 19 --- .../bluetooth/BluetoothHeadsetClientCall.aidl | 18 --- .../BluetoothHealthAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppQosSettings.aidl | 19 --- .../BluetoothHidDeviceAppSdpSettings.aidl | 19 --- .../java/android/bluetooth/IBluetooth.aidl | 125 ------------------ .../android/bluetooth/IBluetoothA2dp.aidl | 48 ------- .../android/bluetooth/IBluetoothA2dpSink.aidl | 37 ------ .../bluetooth/IBluetoothAvrcpController.aidl | 36 ----- .../android/bluetooth/IBluetoothCallback.aidl | 28 ---- .../android/bluetooth/IBluetoothGatt.aidl | 115 ---------------- .../bluetooth/IBluetoothGattCallback.aidl | 42 ------ .../IBluetoothGattServerCallback.aidl | 47 ------- .../android/bluetooth/IBluetoothHeadset.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetClient.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetPhone.aidl | 39 ------ .../android/bluetooth/IBluetoothHealth.aidl | 42 ------ .../bluetooth/IBluetoothHealthCallback.aidl | 32 ----- .../IBluetoothHidDeviceCallback.aidl | 31 ----- .../bluetooth/IBluetoothInputDevice.aidl | 67 ---------- .../bluetooth/IBluetoothInputHost.aidl | 40 ------ .../android/bluetooth/IBluetoothManager.aidl | 53 -------- .../bluetooth/IBluetoothManagerCallback.aidl | 30 ----- .../java/android/bluetooth/IBluetoothMap.aidl | 37 ------ .../bluetooth/IBluetoothMapClient.aidl | 40 ------ .../java/android/bluetooth/IBluetoothPan.aidl | 35 ----- .../android/bluetooth/IBluetoothPbap.aidl | 32 ----- .../bluetooth/IBluetoothPbapClient.aidl | 34 ----- .../IBluetoothProfileServiceConnection.aidl | 30 ----- .../java/android/bluetooth/IBluetoothSap.aidl | 37 ------ .../IBluetoothStateChangeCallback.aidl | 27 ---- framework/java/android/bluetooth/OobData.aidl | 19 --- .../android/bluetooth/le/AdvertiseData.aidl | 19 --- .../bluetooth/le/AdvertiseSettings.aidl | 19 --- .../le/AdvertisingSetParameters.aidl | 19 --- .../bluetooth/le/IAdvertisingSetCallback.aidl | 33 ----- .../le/IPeriodicAdvertisingCallback.aidl | 31 ----- .../bluetooth/le/IScannerCallback.aidl | 31 ----- .../le/PeriodicAdvertisingParameters.aidl | 19 --- .../le/PeriodicAdvertisingReport.aidl | 19 --- .../bluetooth/le/ResultStorageDescriptor.aidl | 23 ---- .../java/android/bluetooth/le/ScanFilter.aidl | 19 --- .../java/android/bluetooth/le/ScanResult.aidl | 19 --- .../android/bluetooth/le/ScanSettings.aidl | 19 --- 54 files changed, 1785 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAudioConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecStatus.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothDevice.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl delete mode 100644 framework/java/android/bluetooth/IBluetooth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothAvrcpController.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGatt.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothHeadset.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealthCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputDevice.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputHost.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManager.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManagerCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMapClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPan.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbapClient.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothSap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl delete mode 100644 framework/java/android/bluetooth/OobData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseSettings.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IScannerCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl delete mode 100644 framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanFilter.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanResult.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanSettings.aidl diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl deleted file mode 100644 index 60cbf9f3133..00000000000 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothActivityEnergyInfo; diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl b/framework/java/android/bluetooth/BluetoothAudioConfig.aidl deleted file mode 100644 index 63be5cff87d..00000000000 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothAudioConfig; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl deleted file mode 100644 index 590fd63eda8..00000000000 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2015 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; - -parcelable BluetoothAvrcpPlayerSettings; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl b/framework/java/android/bluetooth/BluetoothCodecConfig.aidl deleted file mode 100644 index 553e66e1dac..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothCodecConfig; diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl b/framework/java/android/bluetooth/BluetoothCodecStatus.aidl deleted file mode 100644 index f9c3a3de2f4..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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; - -parcelable BluetoothCodecStatus; diff --git a/framework/java/android/bluetooth/BluetoothDevice.aidl b/framework/java/android/bluetooth/BluetoothDevice.aidl deleted file mode 100644 index daae74d52c4..00000000000 --- a/framework/java/android/bluetooth/BluetoothDevice.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothDevice; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl deleted file mode 100644 index bbb8623e217..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattCharacteristic; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl deleted file mode 100644 index 43932733167..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattDescriptor; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl deleted file mode 100644 index 1ef427edc3a..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattIncludedService; diff --git a/framework/java/android/bluetooth/BluetoothGattService.aidl b/framework/java/android/bluetooth/BluetoothGattService.aidl deleted file mode 100644 index 84314d2072d..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl deleted file mode 100644 index 35f792387ec..00000000000 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothHeadsetClientCall; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl deleted file mode 100644 index bc9e54f5787..00000000000 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2011, 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; - -parcelable BluetoothHealthAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl deleted file mode 100644 index 283a71790d3..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl deleted file mode 100644 index 14f91140af2..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppQosSettings; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl deleted file mode 100644 index 87dd10ee15f..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl deleted file mode 100644 index 1d7cfc900e4..00000000000 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008, 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.bluetooth.IBluetoothCallback; -import android.bluetooth.IBluetoothStateChangeCallback; -import android.bluetooth.BluetoothActivityEnergyInfo; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.OobData; -import android.os.ParcelUuid; -import android.os.ParcelFileDescriptor; -import android.os.ResultReceiver; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetooth -{ - boolean isEnabled(); - int getState(); - boolean enable(); - boolean enableNoAutoConnect(); - boolean disable(); - - String getAddress(); - ParcelUuid[] getUuids(); - boolean setName(in String name); - String getName(); - - int getScanMode(); - boolean setScanMode(int mode, int duration); - - int getDiscoverableTimeout(); - boolean setDiscoverableTimeout(int timeout); - - boolean startDiscovery(); - boolean cancelDiscovery(); - boolean isDiscovering(); - long getDiscoveryEndMillis(); - - int getAdapterConnectionState(); - int getProfileConnectionState(int profile); - - BluetoothDevice[] getBondedDevices(); - boolean createBond(in BluetoothDevice device, in int transport); - boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData); - boolean cancelBondProcess(in BluetoothDevice device); - boolean removeBond(in BluetoothDevice device); - int getBondState(in BluetoothDevice device); - boolean isBondingInitiatedLocally(in BluetoothDevice device); - long getSupportedProfiles(); - int getConnectionState(in BluetoothDevice device); - - String getRemoteName(in BluetoothDevice device); - int getRemoteType(in BluetoothDevice device); - String getRemoteAlias(in BluetoothDevice device); - boolean setRemoteAlias(in BluetoothDevice device, in String name); - int getRemoteClass(in BluetoothDevice device); - ParcelUuid[] getRemoteUuids(in BluetoothDevice device); - boolean fetchRemoteUuids(in BluetoothDevice device); - boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); - int getBatteryLevel(in BluetoothDevice device); - - boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); - boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] - passkey); - boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); - - int getPhonebookAccessPermission(in BluetoothDevice device); - boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); - int getMessageAccessPermission(in BluetoothDevice device); - boolean setMessageAccessPermission(in BluetoothDevice device, int value); - int getSimAccessPermission(in BluetoothDevice device); - boolean setSimAccessPermission(in BluetoothDevice device, int value); - - void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); - - void registerCallback(in IBluetoothCallback callback); - void unregisterCallback(in IBluetoothCallback callback); - - // For Socket - ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); - ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - - boolean factoryReset(); - - boolean isMultiAdvertisementSupported(); - boolean isOffloadedFilteringSupported(); - boolean isOffloadedScanBatchingSupported(); - boolean isActivityAndEnergyReportingSupported(); - boolean isLe2MPhySupported(); - boolean isLeCodedPhySupported(); - boolean isLeExtendedAdvertisingSupported(); - boolean isLePeriodicAdvertisingSupported(); - int getLeMaximumAdvertisingDataLength(); - BluetoothActivityEnergyInfo reportActivityInfo(); - - /** - * Requests the controller activity info asynchronously. - * The implementor is expected to reply with the - * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the - * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. - * The result code is ignored. - */ - oneway void requestActivityInfo(in ResultReceiver result); - - void onLeServiceUp(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl deleted file mode 100644 index 1b533cba3d2..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothCodecConfig; -import android.bluetooth.BluetoothCodecStatus; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP service - * - * @hide - */ -interface IBluetoothA2dp { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isAvrcpAbsoluteVolumeSupported(); - oneway void adjustAvrcpAbsoluteVolume(int direction); - oneway void setAvrcpAbsoluteVolume(int volume); - boolean isA2dpPlaying(in BluetoothDevice device); - BluetoothCodecStatus getCodecStatus(); - oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); - oneway void enableOptionalCodecs(); - oneway void disableOptionalCodecs(); - int supportsOptionalCodecs(in BluetoothDevice device); - int getOptionalCodecsEnabled(in BluetoothDevice device); - oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl deleted file mode 100755 index d1458246dfe..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAudioConfig; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP sink service - * - * @hide - */ -interface IBluetoothA2dpSink { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isA2dpPlaying(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl deleted file mode 100644 index cfa11cac4a8..00000000000 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAvrcpPlayerSettings; -import android.bluetooth.BluetoothDevice; -import android.media.MediaMetadata; -import android.media.session.PlaybackState; - -/** - * APIs for Bluetooth AVRCP controller service - * - * @hide - */ -interface IBluetoothAvrcpController { - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); - void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); -} diff --git a/framework/java/android/bluetooth/IBluetoothCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl deleted file mode 100644 index e2809788d67..00000000000 --- a/framework/java/android/bluetooth/IBluetoothCallback.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009, 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; - -/** - * System private API for Bluetooth service callbacks. - * - * {@hide} - */ -interface IBluetoothCallback -{ - //void onRfcommChannelFound(int channel); - void onBluetoothStateChange(int prevState, int newState); -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl deleted file mode 100644 index e87f0700722..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.app.PendingIntent; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertisingSetParameters; -import android.bluetooth.le.PeriodicAdvertisingParameters; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; -import android.bluetooth.le.ResultStorageDescriptor; -import android.os.ParcelUuid; -import android.os.WorkSource; - -import android.bluetooth.IBluetoothGattCallback; -import android.bluetooth.IBluetoothGattServerCallback; -import android.bluetooth.le.IAdvertisingSetCallback; -import android.bluetooth.le.IPeriodicAdvertisingCallback; -import android.bluetooth.le.IScannerCallback; - -/** - * API for interacting with BLE / GATT - * @hide - */ -interface IBluetoothGatt { - List getDevicesMatchingConnectionStates(in int[] states); - - void registerScanner(in IScannerCallback callback, in WorkSource workSource); - void unregisterScanner(in int scannerId); - void startScan(in int scannerId, in ScanSettings settings, in List filters, - in List scanStorages, in String callingPackage); - void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List filters, - in String callingPackage); - void stopScanForIntent(in PendingIntent intent, in String callingPackage); - void stopScan(in int scannerId); - void flushPendingBatchResults(in int scannerId); - - void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, - in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, - in IAdvertisingSetCallback callback); - void stopAdvertisingSet(in IAdvertisingSetCallback callback); - - void getOwnAddress(in int advertiserId); - void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); - void setAdvertisingData(in int advertiserId, in AdvertiseData data); - void setScanResponseData(in int advertiserId, in AdvertiseData data); - void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); - void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); - void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); - void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable); - - void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); - void unregisterSync(in IPeriodicAdvertisingCallback callback); - - void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); - - void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy); - void clientDisconnect(in int clientIf, in String address); - void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void clientReadPhy(in int clientIf, in String address); - void refreshDevice(in int clientIf, in String address); - void discoverServices(in int clientIf, in String address); - void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid); - void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); - void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, - in int startHandle, in int endHandle, in int authReq); - void writeCharacteristic(in int clientIf, in String address, in int handle, - in int writeType, in int authReq, in byte[] value); - void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); - void writeDescriptor(in int clientIf, in String address, in int handle, - in int authReq, in byte[] value); - void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); - void beginReliableWrite(in int clientIf, in String address); - void endReliableWrite(in int clientIf, in String address, in boolean execute); - void readRemoteRssi(in int clientIf, in String address); - void configureMTU(in int clientIf, in String address, in int mtu); - void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); - void unregisterServer(in int serverIf); - void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); - void serverDisconnect(in int serverIf, in String address); - void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void serverReadPhy(in int clientIf, in String address); - void addService(in int serverIf, in BluetoothGattService service); - void removeService(in int serverIf, in int handle); - void clearServices(in int serverIf); - void sendResponse(in int serverIf, in String address, in int requestId, - in int status, in int offset, in byte[] value); - void sendNotification(in int serverIf, in String address, in int handle, - in boolean confirm, in byte[] value); - void disconnectAll(); - void unregAll(); - int numHwTrackFiltersAvailable(); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl deleted file mode 100644 index 4f85cdda87f..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.os.ParcelUuid; -import android.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattCallback { - void onClientRegistered(in int status, in int clientIf); - void onClientConnectionState(in int status, in int clientIf, - in boolean connected, in String address); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onSearchComplete(in String address, in List services, in int status); - void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); - void onCharacteristicWrite(in String address, in int status, in int handle); - void onExecuteWrite(in String address, in int status); - void onDescriptorRead(in String address, in int status, in int handle, in byte[] value); - void onDescriptorWrite(in String address, in int status, in int handle); - void onNotify(in String address, in int handle, in byte[] value); - void onReadRemoteRssi(in String address, in int rssi, in int status); - void onConfigureMTU(in String address, in int mtu, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl deleted file mode 100644 index 74ee11fbd32..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2017 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.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattServerCallback { - void onServerRegistered(in int status, in int serverIf); - void onServerConnectionState(in int status, in int serverIf, - in boolean connected, in String address); - void onServiceAdded(in int status, in BluetoothGattService service); - void onCharacteristicReadRequest(in String address, in int transId, in int offset, - in boolean isLong, in int handle); - void onDescriptorReadRequest(in String address, in int transId, - in int offset, in boolean isLong, - in int handle); - void onCharacteristicWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onDescriptorWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onExecuteWrite(in String address, in int transId, in boolean execWrite); - void onNotificationSent(in String address, in int status); - void onMtuChanged(in String address, in int mtu); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl deleted file mode 100755 index 92ab8da7542..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Headset service - * - * {@hide} - */ -interface IBluetoothHeadset { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - boolean isAudioConnected(in BluetoothDevice device); - boolean sendVendorSpecificResultCode(in BluetoothDevice device, - in String command, - in String arg); - - // APIs that can be made public in future - int getBatteryUsageHint(in BluetoothDevice device); - - // Internal functions, not be made public - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - int getAudioState(in BluetoothDevice device); - - boolean isAudioOn(); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); - void setForceScoAudio(boolean forced); - boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); - boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - oneway void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); - void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type); - boolean enableWBS(); - boolean disableWBS(); - void bindResponse(int ind_id, boolean ind_status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl deleted file mode 100644 index e571b009f04..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadsetClientCall; -import android.os.Bundle; - -/** - * API for Bluetooth Headset Client service (HFP HF Role) - * - * {@hide} - */ -interface IBluetoothHeadsetClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - - List getCurrentCalls(in BluetoothDevice device); - Bundle getCurrentAgEvents(in BluetoothDevice device); - - boolean acceptCall(in BluetoothDevice device, int flag); - boolean holdCall(in BluetoothDevice device); - boolean rejectCall(in BluetoothDevice device); - boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call); - - boolean enterPrivateMode(in BluetoothDevice device, int index); - boolean explicitCallTransfer(in BluetoothDevice device); - - BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number); - - boolean sendDTMF(in BluetoothDevice device, byte code); - boolean getLastVoiceTagNumber(in BluetoothDevice device); - - int getAudioState(in BluetoothDevice device); - boolean connectAudio(in BluetoothDevice device); - boolean disconnectAudio(in BluetoothDevice device); - void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); - boolean getAudioRouteAllowed(in BluetoothDevice device); - - Bundle getCurrentAgFeatures(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl deleted file mode 100644 index d5e64f6faec..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 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; - -/** - * API for Bluetooth Headset Phone Service in phone app - * - * {@hide} - */ -interface IBluetoothHeadsetPhone { - // Internal functions, not be made public - boolean answerCall(); - boolean hangupCall(); - boolean sendDtmf(int dtmf); - boolean processChld(int chld); - String getNetworkOperator(); - String getSubscriberNumber(); - boolean listCurrentCalls(); - boolean queryPhoneState(); - - // Internal for phone app to call - void updateBtHandsfreeAfterRadioTechnologyChange(); - void cdmaSwapSecondCallState(); - void cdmaSetSecondCallState(boolean state); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealth.aidl b/framework/java/android/bluetooth/IBluetoothHealth.aidl deleted file mode 100644 index a84a42cb9ce..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealth.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.bluetooth.IBluetoothHealthCallback; -import android.os.ParcelFileDescriptor; - -/** - * API for Bluetooth Health service - * - * {@hide} - */ -interface IBluetoothHealth -{ - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, - in IBluetoothHealthCallback callback); - boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); - boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, - int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); - ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - List getConnectedHealthDevices(); - List getHealthDevicesMatchingConnectionStates(in int[] states); - int getHealthDeviceConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl deleted file mode 100644 index 0ace9fe1230..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011, 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.os.ParcelFileDescriptor; - -/** - *@hide - */ -interface IBluetoothHealthCallback -{ - void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); - void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, - in BluetoothDevice device, int prevState, int newState, in - ParcelFileDescriptor fd, int id); -} diff --git a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl deleted file mode 100644 index a737198ad95..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; - -/** @hide */ -interface IBluetoothHidDeviceCallback { - void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); - void onConnectionStateChanged(in BluetoothDevice device, in int state); - void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize); - void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - void onSetProtocol(in BluetoothDevice device, in byte protocol); - void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data); - void onVirtualCableUnplug(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl deleted file mode 100644 index 5bd3f781932..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth HID service - * - * {@hide} - */ -interface IBluetoothInputDevice { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - /** - * @hide - */ - boolean getProtocolMode(in BluetoothDevice device); - /** - * @hide - */ - boolean virtualUnplug(in BluetoothDevice device); - /** - * @hide - */ - boolean setProtocolMode(in BluetoothDevice device, int protocolMode); - /** - * @hide - */ - boolean getReport(in BluetoothDevice device, byte reportType, byte reportId, int bufferSize); - /** - * @hide - */ - boolean setReport(in BluetoothDevice device, byte reportType, String report); - /** - * @hide - */ - boolean sendData(in BluetoothDevice device, String report); - /** - * @hide - */ - boolean getIdleTime(in BluetoothDevice device); - /** - * @hide - */ - boolean setIdleTime(in BluetoothDevice device, byte idleTime); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputHost.aidl b/framework/java/android/bluetooth/IBluetoothInputHost.aidl deleted file mode 100644 index 6c4993f750e..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputHost.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; -import android.bluetooth.IBluetoothHidDeviceCallback; -import android.bluetooth.BluetoothHidDeviceAppSdpSettings; -import android.bluetooth.BluetoothHidDeviceAppQosSettings; - -/** @hide */ -interface IBluetoothInputHost { - boolean registerApp(in BluetoothHidDeviceAppConfiguration config, - in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, - in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); - boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); - boolean sendReport(in BluetoothDevice device, in int id, in byte[] data); - boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - boolean reportError(in BluetoothDevice device, byte error); - boolean unplug(in BluetoothDevice device); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl deleted file mode 100644 index 2d5fc98e5e5..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; -import android.bluetooth.IBluetoothStateChangeCallback; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetoothManager -{ - IBluetooth registerAdapter(in IBluetoothManagerCallback callback); - void unregisterAdapter(in IBluetoothManagerCallback callback); - void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); - void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); - boolean isEnabled(); - boolean enable(String packageName); - boolean enableNoAutoConnect(String packageName); - boolean disable(String packageName, boolean persist); - int getState(); - IBluetoothGatt getBluetoothGatt(); - - boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - - String getAddress(); - String getName(); - - boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable, String packageName); - boolean isBleAppPresent(); -} - diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl deleted file mode 100644 index 8104d219948..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; - -/** - * API for Communication between BluetoothAdapter and BluetoothManager - * - * {@hide} - */ -oneway interface IBluetoothManagerCallback { - void onBluetoothServiceUp(in IBluetooth bluetoothService); - void onBluetoothServiceDown(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothMap.aidl b/framework/java/android/bluetooth/IBluetoothMap.aidl deleted file mode 100644 index d4af63d1fd2..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth MAP service - * - * {@hide} - */ -interface IBluetoothMap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothMapClient.aidl b/framework/java/android/bluetooth/IBluetoothMapClient.aidl deleted file mode 100644 index df45af91c92..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMapClient.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.net.Uri; - -/** - * System private API for Bluetooth MAP MCE service - * - * {@hide} - */ -interface IBluetoothMapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device,in int priority); - int getPriority(in BluetoothDevice device); - boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message, - in PendingIntent sentIntent, in PendingIntent deliveryIntent); - boolean getUnreadMessages(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPan.aidl b/framework/java/android/bluetooth/IBluetoothPan.aidl deleted file mode 100644 index 5a323477704..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPan.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Pan service - * - * {@hide} - */ -interface IBluetoothPan { - // Public API - boolean isTetheringOn(); - void setBluetoothTethering(boolean value); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbap.aidl b/framework/java/android/bluetooth/IBluetoothPbap.aidl deleted file mode 100644 index 7cc77d110e9..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbap.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth pbap service - * - * {@hide} - */ -interface IBluetoothPbap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - void disconnect(); - boolean isConnected(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl deleted file mode 100644 index 6d4c5a6f90b..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Phone Book Access Provile Client Side - * - * {@hide} - */ -interface IBluetoothPbapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl deleted file mode 100755 index 541583ff553..00000000000 --- a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2014 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.content.ComponentName; -import android.os.IBinder; - -/** - * Callback for bluetooth profile connections. - * - * {@hide} - */ -oneway interface IBluetoothProfileServiceConnection { - void onServiceConnected(in ComponentName comp, in IBinder service); - void onServiceDisconnected(in ComponentName comp); -} diff --git a/framework/java/android/bluetooth/IBluetoothSap.aidl b/framework/java/android/bluetooth/IBluetoothSap.aidl deleted file mode 100644 index 8970639467c..00000000000 --- a/framework/java/android/bluetooth/IBluetoothSap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth SAP service - * - * {@hide} - */ -interface IBluetoothSap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl deleted file mode 100644 index 0da4e884328..00000000000 --- a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011, 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; - -/** - * System private API for Bluetooth state change callback. - * - * {@hide} - */ -oneway interface IBluetoothStateChangeCallback -{ - void onBluetoothStateChange(boolean on); -} diff --git a/framework/java/android/bluetooth/OobData.aidl b/framework/java/android/bluetooth/OobData.aidl deleted file mode 100644 index d831c647769..00000000000 --- a/framework/java/android/bluetooth/OobData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable OobData; diff --git a/framework/java/android/bluetooth/le/AdvertiseData.aidl b/framework/java/android/bluetooth/le/AdvertiseData.aidl deleted file mode 100644 index bcbf2243e18..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseData; diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl b/framework/java/android/bluetooth/le/AdvertiseSettings.aidl deleted file mode 100644 index 9f47d74ca53..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseSettings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl deleted file mode 100644 index 39034a001fa..00000000000 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable AdvertisingSetParameters; diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl deleted file mode 100644 index 3628c775b79..00000000000 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IAdvertisingSetCallback { - void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); - void onOwnAddressRead(in int advertiserId, in int addressType, in String address); - void onAdvertisingSetStopped(in int advertiserId); - void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); - void onAdvertisingDataSet(in int advertiserId, in int status); - void onScanResponseDataSet(in int advertiserId, in int status); - void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); - void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); - void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); - void onPeriodicAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); -} diff --git a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl deleted file mode 100644 index a76c54d4ab4..00000000000 --- a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.PeriodicAdvertisingReport; - -/** - * Callback definitions for interacting with Periodic Advertising - * @hide - */ -oneway interface IPeriodicAdvertisingCallback { - - void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, - in int skip, in int timeout, in int status); - void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); - void onSyncLost(in int syncHandle); -} diff --git a/framework/java/android/bluetooth/le/IScannerCallback.aidl b/framework/java/android/bluetooth/le/IScannerCallback.aidl deleted file mode 100644 index 8cbbaef41ae..00000000000 --- a/framework/java/android/bluetooth/le/IScannerCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.le; - -import android.bluetooth.le.ScanResult; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IScannerCallback { - void onScannerRegistered(in int status, in int scannerId); - - void onScanResult(in ScanResult scanResult); - void onBatchScanResults(in List batchResults); - void onFoundOrLost(in boolean onFound, in ScanResult scanResult); - void onScanManagerErrorCallback(in int errorCode); -} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl deleted file mode 100644 index f4bea22a12f..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingParameters; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl deleted file mode 100644 index 547d09611fd..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingReport; diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl b/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl deleted file mode 100644 index f218a01a5d1..00000000000 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -/** - * {@hide} - */ - -parcelable ResultStorageDescriptor; diff --git a/framework/java/android/bluetooth/le/ScanFilter.aidl b/framework/java/android/bluetooth/le/ScanFilter.aidl deleted file mode 100644 index 4cecfe62e11..00000000000 --- a/framework/java/android/bluetooth/le/ScanFilter.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanFilter; diff --git a/framework/java/android/bluetooth/le/ScanResult.aidl b/framework/java/android/bluetooth/le/ScanResult.aidl deleted file mode 100644 index 39430350dac..00000000000 --- a/framework/java/android/bluetooth/le/ScanResult.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanResult; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/ScanSettings.aidl b/framework/java/android/bluetooth/le/ScanSettings.aidl deleted file mode 100644 index eb169c1209f..00000000000 --- a/framework/java/android/bluetooth/le/ScanSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanSettings; -- GitLab From b2544a0aec73f01869f8c325f8d08955552d4230 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 8 Aug 2017 04:27:20 -0700 Subject: [PATCH 0816/1408] Bluetooth: move AIDL files related to bluetooth into system/bt (1/2) This patch moves *.aidl files from frameworks/base/core/java/android/bluetooth into system/bt/binder. This is in preparation to convert the Bluetooth deamon into native implementation piece by piece. In order to do that, one must have C++ header files, and paths to them with AIDL files, and */java/* folder didn't seem as proper place for that. Additionally, keeping AIDL files out of framework/base will not require creating dependency on this huge project, which should help keeping the compilation fast. Test: compilation test Change-Id: I9a6db8832c9ec3215c648e325d67278832ef22cc Merged-In: I9a6db8832c9ec3215c648e325d67278832ef22cc (cherry picked from commit 06ae9c665b6fce7a748c3d7175359bff8343768c) --- .../BluetoothActivityEnergyInfo.aidl | 19 --- .../bluetooth/BluetoothAudioConfig.aidl | 19 --- .../BluetoothAvrcpPlayerSettings.aidl | 19 --- .../bluetooth/BluetoothCodecConfig.aidl | 19 --- .../bluetooth/BluetoothCodecStatus.aidl | 19 --- .../android/bluetooth/BluetoothDevice.aidl | 19 --- .../BluetoothGattCharacteristic.aidl | 19 --- .../bluetooth/BluetoothGattDescriptor.aidl | 19 --- .../BluetoothGattIncludedService.aidl | 19 --- .../bluetooth/BluetoothGattService.aidl | 19 --- .../bluetooth/BluetoothHeadsetClientCall.aidl | 18 --- .../BluetoothHealthAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppQosSettings.aidl | 19 --- .../BluetoothHidDeviceAppSdpSettings.aidl | 19 --- .../java/android/bluetooth/IBluetooth.aidl | 125 ------------------ .../android/bluetooth/IBluetoothA2dp.aidl | 45 ------- .../android/bluetooth/IBluetoothA2dpSink.aidl | 37 ------ .../bluetooth/IBluetoothAvrcpController.aidl | 36 ----- .../android/bluetooth/IBluetoothCallback.aidl | 28 ---- .../android/bluetooth/IBluetoothGatt.aidl | 111 ---------------- .../bluetooth/IBluetoothGattCallback.aidl | 42 ------ .../IBluetoothGattServerCallback.aidl | 47 ------- .../android/bluetooth/IBluetoothHeadset.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetClient.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetPhone.aidl | 39 ------ .../android/bluetooth/IBluetoothHealth.aidl | 42 ------ .../bluetooth/IBluetoothHealthCallback.aidl | 32 ----- .../IBluetoothHidDeviceCallback.aidl | 31 ----- .../bluetooth/IBluetoothInputDevice.aidl | 67 ---------- .../bluetooth/IBluetoothInputHost.aidl | 40 ------ .../android/bluetooth/IBluetoothManager.aidl | 52 -------- .../bluetooth/IBluetoothManagerCallback.aidl | 30 ----- .../java/android/bluetooth/IBluetoothMap.aidl | 37 ------ .../bluetooth/IBluetoothMapClient.aidl | 40 ------ .../java/android/bluetooth/IBluetoothPan.aidl | 35 ----- .../android/bluetooth/IBluetoothPbap.aidl | 32 ----- .../bluetooth/IBluetoothPbapClient.aidl | 34 ----- .../IBluetoothProfileServiceConnection.aidl | 30 ----- .../java/android/bluetooth/IBluetoothSap.aidl | 37 ------ .../IBluetoothStateChangeCallback.aidl | 27 ---- framework/java/android/bluetooth/OobData.aidl | 19 --- .../android/bluetooth/le/AdvertiseData.aidl | 19 --- .../bluetooth/le/AdvertiseSettings.aidl | 19 --- .../le/AdvertisingSetParameters.aidl | 19 --- .../bluetooth/le/IAdvertisingSetCallback.aidl | 33 ----- .../le/IPeriodicAdvertisingCallback.aidl | 31 ----- .../bluetooth/le/IScannerCallback.aidl | 31 ----- .../le/PeriodicAdvertisingParameters.aidl | 19 --- .../le/PeriodicAdvertisingReport.aidl | 19 --- .../bluetooth/le/ResultStorageDescriptor.aidl | 23 ---- .../java/android/bluetooth/le/ScanFilter.aidl | 19 --- .../java/android/bluetooth/le/ScanResult.aidl | 19 --- .../android/bluetooth/le/ScanSettings.aidl | 19 --- 54 files changed, 1777 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAudioConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecStatus.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothDevice.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl delete mode 100644 framework/java/android/bluetooth/IBluetooth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothAvrcpController.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGatt.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothHeadset.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealthCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputDevice.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputHost.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManager.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManagerCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMapClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPan.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbapClient.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothSap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl delete mode 100644 framework/java/android/bluetooth/OobData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseSettings.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IScannerCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl delete mode 100644 framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanFilter.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanResult.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanSettings.aidl diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl deleted file mode 100644 index 60cbf9f3133..00000000000 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothActivityEnergyInfo; diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl b/framework/java/android/bluetooth/BluetoothAudioConfig.aidl deleted file mode 100644 index 63be5cff87d..00000000000 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothAudioConfig; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl deleted file mode 100644 index 590fd63eda8..00000000000 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2015 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; - -parcelable BluetoothAvrcpPlayerSettings; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl b/framework/java/android/bluetooth/BluetoothCodecConfig.aidl deleted file mode 100644 index 553e66e1dac..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothCodecConfig; diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl b/framework/java/android/bluetooth/BluetoothCodecStatus.aidl deleted file mode 100644 index f9c3a3de2f4..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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; - -parcelable BluetoothCodecStatus; diff --git a/framework/java/android/bluetooth/BluetoothDevice.aidl b/framework/java/android/bluetooth/BluetoothDevice.aidl deleted file mode 100644 index daae74d52c4..00000000000 --- a/framework/java/android/bluetooth/BluetoothDevice.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothDevice; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl deleted file mode 100644 index bbb8623e217..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattCharacteristic; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl deleted file mode 100644 index 43932733167..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattDescriptor; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl deleted file mode 100644 index 1ef427edc3a..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattIncludedService; diff --git a/framework/java/android/bluetooth/BluetoothGattService.aidl b/framework/java/android/bluetooth/BluetoothGattService.aidl deleted file mode 100644 index 84314d2072d..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl deleted file mode 100644 index 35f792387ec..00000000000 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothHeadsetClientCall; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl deleted file mode 100644 index bc9e54f5787..00000000000 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2011, 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; - -parcelable BluetoothHealthAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl deleted file mode 100644 index 283a71790d3..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl deleted file mode 100644 index 14f91140af2..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppQosSettings; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl deleted file mode 100644 index 87dd10ee15f..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl deleted file mode 100644 index 1d7cfc900e4..00000000000 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008, 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.bluetooth.IBluetoothCallback; -import android.bluetooth.IBluetoothStateChangeCallback; -import android.bluetooth.BluetoothActivityEnergyInfo; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.OobData; -import android.os.ParcelUuid; -import android.os.ParcelFileDescriptor; -import android.os.ResultReceiver; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetooth -{ - boolean isEnabled(); - int getState(); - boolean enable(); - boolean enableNoAutoConnect(); - boolean disable(); - - String getAddress(); - ParcelUuid[] getUuids(); - boolean setName(in String name); - String getName(); - - int getScanMode(); - boolean setScanMode(int mode, int duration); - - int getDiscoverableTimeout(); - boolean setDiscoverableTimeout(int timeout); - - boolean startDiscovery(); - boolean cancelDiscovery(); - boolean isDiscovering(); - long getDiscoveryEndMillis(); - - int getAdapterConnectionState(); - int getProfileConnectionState(int profile); - - BluetoothDevice[] getBondedDevices(); - boolean createBond(in BluetoothDevice device, in int transport); - boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData); - boolean cancelBondProcess(in BluetoothDevice device); - boolean removeBond(in BluetoothDevice device); - int getBondState(in BluetoothDevice device); - boolean isBondingInitiatedLocally(in BluetoothDevice device); - long getSupportedProfiles(); - int getConnectionState(in BluetoothDevice device); - - String getRemoteName(in BluetoothDevice device); - int getRemoteType(in BluetoothDevice device); - String getRemoteAlias(in BluetoothDevice device); - boolean setRemoteAlias(in BluetoothDevice device, in String name); - int getRemoteClass(in BluetoothDevice device); - ParcelUuid[] getRemoteUuids(in BluetoothDevice device); - boolean fetchRemoteUuids(in BluetoothDevice device); - boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); - int getBatteryLevel(in BluetoothDevice device); - - boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); - boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] - passkey); - boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); - - int getPhonebookAccessPermission(in BluetoothDevice device); - boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); - int getMessageAccessPermission(in BluetoothDevice device); - boolean setMessageAccessPermission(in BluetoothDevice device, int value); - int getSimAccessPermission(in BluetoothDevice device); - boolean setSimAccessPermission(in BluetoothDevice device, int value); - - void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); - - void registerCallback(in IBluetoothCallback callback); - void unregisterCallback(in IBluetoothCallback callback); - - // For Socket - ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); - ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - - boolean factoryReset(); - - boolean isMultiAdvertisementSupported(); - boolean isOffloadedFilteringSupported(); - boolean isOffloadedScanBatchingSupported(); - boolean isActivityAndEnergyReportingSupported(); - boolean isLe2MPhySupported(); - boolean isLeCodedPhySupported(); - boolean isLeExtendedAdvertisingSupported(); - boolean isLePeriodicAdvertisingSupported(); - int getLeMaximumAdvertisingDataLength(); - BluetoothActivityEnergyInfo reportActivityInfo(); - - /** - * Requests the controller activity info asynchronously. - * The implementor is expected to reply with the - * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the - * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. - * The result code is ignored. - */ - oneway void requestActivityInfo(in ResultReceiver result); - - void onLeServiceUp(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl deleted file mode 100644 index a775a1f90b8..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothCodecConfig; -import android.bluetooth.BluetoothCodecStatus; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP service - * - * @hide - */ -interface IBluetoothA2dp { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isAvrcpAbsoluteVolumeSupported(); - oneway void adjustAvrcpAbsoluteVolume(int direction); - oneway void setAvrcpAbsoluteVolume(int volume); - boolean isA2dpPlaying(in BluetoothDevice device); - BluetoothCodecStatus getCodecStatus(); - oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); - oneway void enableOptionalCodecs(); - oneway void disableOptionalCodecs(); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl deleted file mode 100755 index d1458246dfe..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAudioConfig; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP sink service - * - * @hide - */ -interface IBluetoothA2dpSink { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isA2dpPlaying(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl deleted file mode 100644 index cfa11cac4a8..00000000000 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAvrcpPlayerSettings; -import android.bluetooth.BluetoothDevice; -import android.media.MediaMetadata; -import android.media.session.PlaybackState; - -/** - * APIs for Bluetooth AVRCP controller service - * - * @hide - */ -interface IBluetoothAvrcpController { - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); - void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); -} diff --git a/framework/java/android/bluetooth/IBluetoothCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl deleted file mode 100644 index e2809788d67..00000000000 --- a/framework/java/android/bluetooth/IBluetoothCallback.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009, 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; - -/** - * System private API for Bluetooth service callbacks. - * - * {@hide} - */ -interface IBluetoothCallback -{ - //void onRfcommChannelFound(int channel); - void onBluetoothStateChange(int prevState, int newState); -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl deleted file mode 100644 index 05cef600c42..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertisingSetParameters; -import android.bluetooth.le.PeriodicAdvertisingParameters; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; -import android.bluetooth.le.ResultStorageDescriptor; -import android.os.ParcelUuid; -import android.os.WorkSource; - -import android.bluetooth.IBluetoothGattCallback; -import android.bluetooth.IBluetoothGattServerCallback; -import android.bluetooth.le.IAdvertisingSetCallback; -import android.bluetooth.le.IPeriodicAdvertisingCallback; -import android.bluetooth.le.IScannerCallback; - -/** - * API for interacting with BLE / GATT - * @hide - */ -interface IBluetoothGatt { - List getDevicesMatchingConnectionStates(in int[] states); - - void registerScanner(in IScannerCallback callback, in WorkSource workSource); - void unregisterScanner(in int scannerId); - void startScan(in int scannerId, in ScanSettings settings, in List filters, - in List scanStorages, in String callingPackage); - void stopScan(in int scannerId); - void flushPendingBatchResults(in int scannerId); - - void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, - in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, - in IAdvertisingSetCallback callback); - void stopAdvertisingSet(in IAdvertisingSetCallback callback); - - void getOwnAddress(in int advertiserId); - void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); - void setAdvertisingData(in int advertiserId, in AdvertiseData data); - void setScanResponseData(in int advertiserId, in AdvertiseData data); - void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); - void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); - void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); - void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable); - - void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); - void unregisterSync(in IPeriodicAdvertisingCallback callback); - - void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); - - void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy); - void clientDisconnect(in int clientIf, in String address); - void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void clientReadPhy(in int clientIf, in String address); - void refreshDevice(in int clientIf, in String address); - void discoverServices(in int clientIf, in String address); - void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid); - void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); - void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, - in int startHandle, in int endHandle, in int authReq); - void writeCharacteristic(in int clientIf, in String address, in int handle, - in int writeType, in int authReq, in byte[] value); - void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); - void writeDescriptor(in int clientIf, in String address, in int handle, - in int authReq, in byte[] value); - void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); - void beginReliableWrite(in int clientIf, in String address); - void endReliableWrite(in int clientIf, in String address, in boolean execute); - void readRemoteRssi(in int clientIf, in String address); - void configureMTU(in int clientIf, in String address, in int mtu); - void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); - void unregisterServer(in int serverIf); - void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); - void serverDisconnect(in int serverIf, in String address); - void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void serverReadPhy(in int clientIf, in String address); - void addService(in int serverIf, in BluetoothGattService service); - void removeService(in int serverIf, in int handle); - void clearServices(in int serverIf); - void sendResponse(in int serverIf, in String address, in int requestId, - in int status, in int offset, in byte[] value); - void sendNotification(in int serverIf, in String address, in int handle, - in boolean confirm, in byte[] value); - void disconnectAll(); - void unregAll(); - int numHwTrackFiltersAvailable(); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl deleted file mode 100644 index 4f85cdda87f..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.os.ParcelUuid; -import android.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattCallback { - void onClientRegistered(in int status, in int clientIf); - void onClientConnectionState(in int status, in int clientIf, - in boolean connected, in String address); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onSearchComplete(in String address, in List services, in int status); - void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); - void onCharacteristicWrite(in String address, in int status, in int handle); - void onExecuteWrite(in String address, in int status); - void onDescriptorRead(in String address, in int status, in int handle, in byte[] value); - void onDescriptorWrite(in String address, in int status, in int handle); - void onNotify(in String address, in int handle, in byte[] value); - void onReadRemoteRssi(in String address, in int rssi, in int status); - void onConfigureMTU(in String address, in int mtu, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl deleted file mode 100644 index 74ee11fbd32..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2017 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.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattServerCallback { - void onServerRegistered(in int status, in int serverIf); - void onServerConnectionState(in int status, in int serverIf, - in boolean connected, in String address); - void onServiceAdded(in int status, in BluetoothGattService service); - void onCharacteristicReadRequest(in String address, in int transId, in int offset, - in boolean isLong, in int handle); - void onDescriptorReadRequest(in String address, in int transId, - in int offset, in boolean isLong, - in int handle); - void onCharacteristicWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onDescriptorWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onExecuteWrite(in String address, in int transId, in boolean execWrite); - void onNotificationSent(in String address, in int status); - void onMtuChanged(in String address, in int mtu); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl deleted file mode 100755 index 6bd0d7fb43b..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Headset service - * - * {@hide} - */ -interface IBluetoothHeadset { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - boolean isAudioConnected(in BluetoothDevice device); - boolean sendVendorSpecificResultCode(in BluetoothDevice device, - in String command, - in String arg); - - // APIs that can be made public in future - int getBatteryUsageHint(in BluetoothDevice device); - - // Internal functions, not be made public - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - int getAudioState(in BluetoothDevice device); - - boolean isAudioOn(); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); - void setForceScoAudio(boolean forced); - boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); - boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); - void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type); - boolean enableWBS(); - boolean disableWBS(); - void bindResponse(int ind_id, boolean ind_status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl deleted file mode 100644 index e571b009f04..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadsetClientCall; -import android.os.Bundle; - -/** - * API for Bluetooth Headset Client service (HFP HF Role) - * - * {@hide} - */ -interface IBluetoothHeadsetClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - - List getCurrentCalls(in BluetoothDevice device); - Bundle getCurrentAgEvents(in BluetoothDevice device); - - boolean acceptCall(in BluetoothDevice device, int flag); - boolean holdCall(in BluetoothDevice device); - boolean rejectCall(in BluetoothDevice device); - boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call); - - boolean enterPrivateMode(in BluetoothDevice device, int index); - boolean explicitCallTransfer(in BluetoothDevice device); - - BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number); - - boolean sendDTMF(in BluetoothDevice device, byte code); - boolean getLastVoiceTagNumber(in BluetoothDevice device); - - int getAudioState(in BluetoothDevice device); - boolean connectAudio(in BluetoothDevice device); - boolean disconnectAudio(in BluetoothDevice device); - void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); - boolean getAudioRouteAllowed(in BluetoothDevice device); - - Bundle getCurrentAgFeatures(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl deleted file mode 100644 index d5e64f6faec..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 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; - -/** - * API for Bluetooth Headset Phone Service in phone app - * - * {@hide} - */ -interface IBluetoothHeadsetPhone { - // Internal functions, not be made public - boolean answerCall(); - boolean hangupCall(); - boolean sendDtmf(int dtmf); - boolean processChld(int chld); - String getNetworkOperator(); - String getSubscriberNumber(); - boolean listCurrentCalls(); - boolean queryPhoneState(); - - // Internal for phone app to call - void updateBtHandsfreeAfterRadioTechnologyChange(); - void cdmaSwapSecondCallState(); - void cdmaSetSecondCallState(boolean state); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealth.aidl b/framework/java/android/bluetooth/IBluetoothHealth.aidl deleted file mode 100644 index a84a42cb9ce..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealth.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.bluetooth.IBluetoothHealthCallback; -import android.os.ParcelFileDescriptor; - -/** - * API for Bluetooth Health service - * - * {@hide} - */ -interface IBluetoothHealth -{ - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, - in IBluetoothHealthCallback callback); - boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); - boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, - int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); - ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - List getConnectedHealthDevices(); - List getHealthDevicesMatchingConnectionStates(in int[] states); - int getHealthDeviceConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl deleted file mode 100644 index 0ace9fe1230..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011, 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.os.ParcelFileDescriptor; - -/** - *@hide - */ -interface IBluetoothHealthCallback -{ - void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); - void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, - in BluetoothDevice device, int prevState, int newState, in - ParcelFileDescriptor fd, int id); -} diff --git a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl deleted file mode 100644 index a737198ad95..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; - -/** @hide */ -interface IBluetoothHidDeviceCallback { - void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); - void onConnectionStateChanged(in BluetoothDevice device, in int state); - void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize); - void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - void onSetProtocol(in BluetoothDevice device, in byte protocol); - void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data); - void onVirtualCableUnplug(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl deleted file mode 100644 index 5bd3f781932..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth HID service - * - * {@hide} - */ -interface IBluetoothInputDevice { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - /** - * @hide - */ - boolean getProtocolMode(in BluetoothDevice device); - /** - * @hide - */ - boolean virtualUnplug(in BluetoothDevice device); - /** - * @hide - */ - boolean setProtocolMode(in BluetoothDevice device, int protocolMode); - /** - * @hide - */ - boolean getReport(in BluetoothDevice device, byte reportType, byte reportId, int bufferSize); - /** - * @hide - */ - boolean setReport(in BluetoothDevice device, byte reportType, String report); - /** - * @hide - */ - boolean sendData(in BluetoothDevice device, String report); - /** - * @hide - */ - boolean getIdleTime(in BluetoothDevice device); - /** - * @hide - */ - boolean setIdleTime(in BluetoothDevice device, byte idleTime); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputHost.aidl b/framework/java/android/bluetooth/IBluetoothInputHost.aidl deleted file mode 100644 index 6c4993f750e..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputHost.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; -import android.bluetooth.IBluetoothHidDeviceCallback; -import android.bluetooth.BluetoothHidDeviceAppSdpSettings; -import android.bluetooth.BluetoothHidDeviceAppQosSettings; - -/** @hide */ -interface IBluetoothInputHost { - boolean registerApp(in BluetoothHidDeviceAppConfiguration config, - in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, - in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); - boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); - boolean sendReport(in BluetoothDevice device, in int id, in byte[] data); - boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - boolean reportError(in BluetoothDevice device, byte error); - boolean unplug(in BluetoothDevice device); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl deleted file mode 100644 index 5afd7741822..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; -import android.bluetooth.IBluetoothStateChangeCallback; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetoothManager -{ - IBluetooth registerAdapter(in IBluetoothManagerCallback callback); - void unregisterAdapter(in IBluetoothManagerCallback callback); - void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); - void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); - boolean isEnabled(); - boolean enable(String packageName); - boolean enableNoAutoConnect(String packageName); - boolean disable(String packageName, boolean persist); - int getState(); - IBluetoothGatt getBluetoothGatt(); - - boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - - String getAddress(); - String getName(); - - boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable, String packageName); - boolean isBleAppPresent(); -} diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl deleted file mode 100644 index 8104d219948..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; - -/** - * API for Communication between BluetoothAdapter and BluetoothManager - * - * {@hide} - */ -oneway interface IBluetoothManagerCallback { - void onBluetoothServiceUp(in IBluetooth bluetoothService); - void onBluetoothServiceDown(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothMap.aidl b/framework/java/android/bluetooth/IBluetoothMap.aidl deleted file mode 100644 index d4af63d1fd2..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth MAP service - * - * {@hide} - */ -interface IBluetoothMap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothMapClient.aidl b/framework/java/android/bluetooth/IBluetoothMapClient.aidl deleted file mode 100644 index df45af91c92..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMapClient.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.net.Uri; - -/** - * System private API for Bluetooth MAP MCE service - * - * {@hide} - */ -interface IBluetoothMapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device,in int priority); - int getPriority(in BluetoothDevice device); - boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message, - in PendingIntent sentIntent, in PendingIntent deliveryIntent); - boolean getUnreadMessages(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPan.aidl b/framework/java/android/bluetooth/IBluetoothPan.aidl deleted file mode 100644 index 5a323477704..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPan.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Pan service - * - * {@hide} - */ -interface IBluetoothPan { - // Public API - boolean isTetheringOn(); - void setBluetoothTethering(boolean value); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbap.aidl b/framework/java/android/bluetooth/IBluetoothPbap.aidl deleted file mode 100644 index 7cc77d110e9..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbap.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth pbap service - * - * {@hide} - */ -interface IBluetoothPbap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - void disconnect(); - boolean isConnected(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl deleted file mode 100644 index 6d4c5a6f90b..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Phone Book Access Provile Client Side - * - * {@hide} - */ -interface IBluetoothPbapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl deleted file mode 100755 index 96c59e23915..00000000000 --- a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2014 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.content.ComponentName; -import android.os.IBinder; - -/** - * Callback for bluetooth profile connections. - * - * {@hide} - */ -interface IBluetoothProfileServiceConnection { - void onServiceConnected(in ComponentName comp, in IBinder service); - void onServiceDisconnected(in ComponentName comp); -} diff --git a/framework/java/android/bluetooth/IBluetoothSap.aidl b/framework/java/android/bluetooth/IBluetoothSap.aidl deleted file mode 100644 index 8970639467c..00000000000 --- a/framework/java/android/bluetooth/IBluetoothSap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth SAP service - * - * {@hide} - */ -interface IBluetoothSap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl deleted file mode 100644 index 0da4e884328..00000000000 --- a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011, 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; - -/** - * System private API for Bluetooth state change callback. - * - * {@hide} - */ -oneway interface IBluetoothStateChangeCallback -{ - void onBluetoothStateChange(boolean on); -} diff --git a/framework/java/android/bluetooth/OobData.aidl b/framework/java/android/bluetooth/OobData.aidl deleted file mode 100644 index d831c647769..00000000000 --- a/framework/java/android/bluetooth/OobData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable OobData; diff --git a/framework/java/android/bluetooth/le/AdvertiseData.aidl b/framework/java/android/bluetooth/le/AdvertiseData.aidl deleted file mode 100644 index bcbf2243e18..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseData; diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl b/framework/java/android/bluetooth/le/AdvertiseSettings.aidl deleted file mode 100644 index 9f47d74ca53..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseSettings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl deleted file mode 100644 index 39034a001fa..00000000000 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable AdvertisingSetParameters; diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl deleted file mode 100644 index 3628c775b79..00000000000 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IAdvertisingSetCallback { - void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); - void onOwnAddressRead(in int advertiserId, in int addressType, in String address); - void onAdvertisingSetStopped(in int advertiserId); - void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); - void onAdvertisingDataSet(in int advertiserId, in int status); - void onScanResponseDataSet(in int advertiserId, in int status); - void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); - void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); - void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); - void onPeriodicAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); -} diff --git a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl deleted file mode 100644 index a76c54d4ab4..00000000000 --- a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.PeriodicAdvertisingReport; - -/** - * Callback definitions for interacting with Periodic Advertising - * @hide - */ -oneway interface IPeriodicAdvertisingCallback { - - void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, - in int skip, in int timeout, in int status); - void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); - void onSyncLost(in int syncHandle); -} diff --git a/framework/java/android/bluetooth/le/IScannerCallback.aidl b/framework/java/android/bluetooth/le/IScannerCallback.aidl deleted file mode 100644 index 8cbbaef41ae..00000000000 --- a/framework/java/android/bluetooth/le/IScannerCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.le; - -import android.bluetooth.le.ScanResult; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IScannerCallback { - void onScannerRegistered(in int status, in int scannerId); - - void onScanResult(in ScanResult scanResult); - void onBatchScanResults(in List batchResults); - void onFoundOrLost(in boolean onFound, in ScanResult scanResult); - void onScanManagerErrorCallback(in int errorCode); -} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl deleted file mode 100644 index f4bea22a12f..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingParameters; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl deleted file mode 100644 index 547d09611fd..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingReport; diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl b/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl deleted file mode 100644 index f218a01a5d1..00000000000 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -/** - * {@hide} - */ - -parcelable ResultStorageDescriptor; diff --git a/framework/java/android/bluetooth/le/ScanFilter.aidl b/framework/java/android/bluetooth/le/ScanFilter.aidl deleted file mode 100644 index 4cecfe62e11..00000000000 --- a/framework/java/android/bluetooth/le/ScanFilter.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanFilter; diff --git a/framework/java/android/bluetooth/le/ScanResult.aidl b/framework/java/android/bluetooth/le/ScanResult.aidl deleted file mode 100644 index 39430350dac..00000000000 --- a/framework/java/android/bluetooth/le/ScanResult.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanResult; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/ScanSettings.aidl b/framework/java/android/bluetooth/le/ScanSettings.aidl deleted file mode 100644 index eb169c1209f..00000000000 --- a/framework/java/android/bluetooth/le/ScanSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanSettings; -- GitLab From d1d411284122b46a487beed7e1267319253d1840 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 9 Aug 2017 00:45:34 -0700 Subject: [PATCH 0817/1408] resolve merge conflicts of 1efd8ff2a5a6 to stage-aosp-master Test: this fixes merge conflict that I skipped Change-Id: I2e993d1f021b1bb671b07440cdc611853d6c3f08 --- .../BluetoothActivityEnergyInfo.aidl | 19 --- .../bluetooth/BluetoothAudioConfig.aidl | 19 --- .../BluetoothAvrcpPlayerSettings.aidl | 19 --- .../bluetooth/BluetoothCodecConfig.aidl | 19 --- .../bluetooth/BluetoothCodecStatus.aidl | 19 --- .../android/bluetooth/BluetoothDevice.aidl | 19 --- .../BluetoothGattCharacteristic.aidl | 19 --- .../bluetooth/BluetoothGattDescriptor.aidl | 19 --- .../BluetoothGattIncludedService.aidl | 19 --- .../bluetooth/BluetoothGattService.aidl | 19 --- .../bluetooth/BluetoothHeadsetClientCall.aidl | 18 --- .../BluetoothHealthAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppConfiguration.aidl | 19 --- .../BluetoothHidDeviceAppQosSettings.aidl | 19 --- .../BluetoothHidDeviceAppSdpSettings.aidl | 19 --- .../java/android/bluetooth/IBluetooth.aidl | 125 ------------------ .../android/bluetooth/IBluetoothA2dp.aidl | 48 ------- .../android/bluetooth/IBluetoothA2dpSink.aidl | 37 ------ .../bluetooth/IBluetoothAvrcpController.aidl | 36 ----- .../android/bluetooth/IBluetoothCallback.aidl | 28 ---- .../android/bluetooth/IBluetoothGatt.aidl | 115 ---------------- .../bluetooth/IBluetoothGattCallback.aidl | 42 ------ .../IBluetoothGattServerCallback.aidl | 47 ------- .../android/bluetooth/IBluetoothHeadset.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetClient.aidl | 64 --------- .../bluetooth/IBluetoothHeadsetPhone.aidl | 39 ------ .../android/bluetooth/IBluetoothHealth.aidl | 42 ------ .../bluetooth/IBluetoothHealthCallback.aidl | 32 ----- .../IBluetoothHidDeviceCallback.aidl | 31 ----- .../bluetooth/IBluetoothInputDevice.aidl | 67 ---------- .../bluetooth/IBluetoothInputHost.aidl | 40 ------ .../android/bluetooth/IBluetoothManager.aidl | 53 -------- .../bluetooth/IBluetoothManagerCallback.aidl | 30 ----- .../java/android/bluetooth/IBluetoothMap.aidl | 37 ------ .../bluetooth/IBluetoothMapClient.aidl | 40 ------ .../java/android/bluetooth/IBluetoothPan.aidl | 35 ----- .../android/bluetooth/IBluetoothPbap.aidl | 32 ----- .../bluetooth/IBluetoothPbapClient.aidl | 34 ----- .../IBluetoothProfileServiceConnection.aidl | 30 ----- .../java/android/bluetooth/IBluetoothSap.aidl | 37 ------ .../IBluetoothStateChangeCallback.aidl | 27 ---- framework/java/android/bluetooth/OobData.aidl | 19 --- .../android/bluetooth/le/AdvertiseData.aidl | 19 --- .../bluetooth/le/AdvertiseSettings.aidl | 19 --- .../le/AdvertisingSetParameters.aidl | 19 --- .../bluetooth/le/IAdvertisingSetCallback.aidl | 33 ----- .../le/IPeriodicAdvertisingCallback.aidl | 31 ----- .../bluetooth/le/IScannerCallback.aidl | 31 ----- .../le/PeriodicAdvertisingParameters.aidl | 19 --- .../le/PeriodicAdvertisingReport.aidl | 19 --- .../bluetooth/le/ResultStorageDescriptor.aidl | 23 ---- .../java/android/bluetooth/le/ScanFilter.aidl | 19 --- .../java/android/bluetooth/le/ScanResult.aidl | 19 --- .../android/bluetooth/le/ScanSettings.aidl | 19 --- 54 files changed, 1785 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAudioConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecConfig.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothCodecStatus.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothDevice.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattIncludedService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothGattService.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl delete mode 100644 framework/java/android/bluetooth/IBluetooth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothA2dp.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothA2dpSink.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothAvrcpController.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGatt.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothHeadset.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealth.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHealthCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputDevice.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothInputHost.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManager.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothManagerCallback.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothMapClient.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPan.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothPbapClient.aidl delete mode 100755 framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothSap.aidl delete mode 100644 framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl delete mode 100644 framework/java/android/bluetooth/OobData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseData.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertiseSettings.aidl delete mode 100644 framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/IScannerCallback.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl delete mode 100644 framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl delete mode 100644 framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanFilter.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanResult.aidl delete mode 100644 framework/java/android/bluetooth/le/ScanSettings.aidl diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl deleted file mode 100644 index 60cbf9f3133..00000000000 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothActivityEnergyInfo; diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl b/framework/java/android/bluetooth/BluetoothAudioConfig.aidl deleted file mode 100644 index 63be5cff87d..00000000000 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothAudioConfig; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl deleted file mode 100644 index 590fd63eda8..00000000000 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2015 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; - -parcelable BluetoothAvrcpPlayerSettings; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl b/framework/java/android/bluetooth/BluetoothCodecConfig.aidl deleted file mode 100644 index 553e66e1dac..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothCodecConfig; diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl b/framework/java/android/bluetooth/BluetoothCodecStatus.aidl deleted file mode 100644 index f9c3a3de2f4..00000000000 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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; - -parcelable BluetoothCodecStatus; diff --git a/framework/java/android/bluetooth/BluetoothDevice.aidl b/framework/java/android/bluetooth/BluetoothDevice.aidl deleted file mode 100644 index daae74d52c4..00000000000 --- a/framework/java/android/bluetooth/BluetoothDevice.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2009 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; - -parcelable BluetoothDevice; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl b/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl deleted file mode 100644 index bbb8623e217..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattCharacteristic; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl b/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl deleted file mode 100644 index 43932733167..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattDescriptor; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl b/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl deleted file mode 100644 index 1ef427edc3a..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattIncludedService; diff --git a/framework/java/android/bluetooth/BluetoothGattService.aidl b/framework/java/android/bluetooth/BluetoothGattService.aidl deleted file mode 100644 index 84314d2072d..00000000000 --- a/framework/java/android/bluetooth/BluetoothGattService.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable BluetoothGattService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl deleted file mode 100644 index 35f792387ec..00000000000 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2014 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; - -parcelable BluetoothHeadsetClientCall; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl deleted file mode 100644 index bc9e54f5787..00000000000 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2011, 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; - -parcelable BluetoothHealthAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl deleted file mode 100644 index 283a71790d3..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppConfiguration; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl deleted file mode 100644 index 14f91140af2..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppQosSettings; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl deleted file mode 100644 index 87dd10ee15f..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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; - -parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/framework/java/android/bluetooth/IBluetooth.aidl b/framework/java/android/bluetooth/IBluetooth.aidl deleted file mode 100644 index 1d7cfc900e4..00000000000 --- a/framework/java/android/bluetooth/IBluetooth.aidl +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008, 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.bluetooth.IBluetoothCallback; -import android.bluetooth.IBluetoothStateChangeCallback; -import android.bluetooth.BluetoothActivityEnergyInfo; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.OobData; -import android.os.ParcelUuid; -import android.os.ParcelFileDescriptor; -import android.os.ResultReceiver; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetooth -{ - boolean isEnabled(); - int getState(); - boolean enable(); - boolean enableNoAutoConnect(); - boolean disable(); - - String getAddress(); - ParcelUuid[] getUuids(); - boolean setName(in String name); - String getName(); - - int getScanMode(); - boolean setScanMode(int mode, int duration); - - int getDiscoverableTimeout(); - boolean setDiscoverableTimeout(int timeout); - - boolean startDiscovery(); - boolean cancelDiscovery(); - boolean isDiscovering(); - long getDiscoveryEndMillis(); - - int getAdapterConnectionState(); - int getProfileConnectionState(int profile); - - BluetoothDevice[] getBondedDevices(); - boolean createBond(in BluetoothDevice device, in int transport); - boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData); - boolean cancelBondProcess(in BluetoothDevice device); - boolean removeBond(in BluetoothDevice device); - int getBondState(in BluetoothDevice device); - boolean isBondingInitiatedLocally(in BluetoothDevice device); - long getSupportedProfiles(); - int getConnectionState(in BluetoothDevice device); - - String getRemoteName(in BluetoothDevice device); - int getRemoteType(in BluetoothDevice device); - String getRemoteAlias(in BluetoothDevice device); - boolean setRemoteAlias(in BluetoothDevice device, in String name); - int getRemoteClass(in BluetoothDevice device); - ParcelUuid[] getRemoteUuids(in BluetoothDevice device); - boolean fetchRemoteUuids(in BluetoothDevice device); - boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); - int getBatteryLevel(in BluetoothDevice device); - - boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); - boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] - passkey); - boolean setPairingConfirmation(in BluetoothDevice device, boolean accept); - - int getPhonebookAccessPermission(in BluetoothDevice device); - boolean setPhonebookAccessPermission(in BluetoothDevice device, int value); - int getMessageAccessPermission(in BluetoothDevice device); - boolean setMessageAccessPermission(in BluetoothDevice device, int value); - int getSimAccessPermission(in BluetoothDevice device); - boolean setSimAccessPermission(in BluetoothDevice device, int value); - - void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); - - void registerCallback(in IBluetoothCallback callback); - void unregisterCallback(in IBluetoothCallback callback); - - // For Socket - ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag); - ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag); - - boolean factoryReset(); - - boolean isMultiAdvertisementSupported(); - boolean isOffloadedFilteringSupported(); - boolean isOffloadedScanBatchingSupported(); - boolean isActivityAndEnergyReportingSupported(); - boolean isLe2MPhySupported(); - boolean isLeCodedPhySupported(); - boolean isLeExtendedAdvertisingSupported(); - boolean isLePeriodicAdvertisingSupported(); - int getLeMaximumAdvertisingDataLength(); - BluetoothActivityEnergyInfo reportActivityInfo(); - - /** - * Requests the controller activity info asynchronously. - * The implementor is expected to reply with the - * {@link android.bluetooth.BluetoothActivityEnergyInfo} object placed into the Bundle with the - * key {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. - * The result code is ignored. - */ - oneway void requestActivityInfo(in ResultReceiver result); - - void onLeServiceUp(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dp.aidl b/framework/java/android/bluetooth/IBluetoothA2dp.aidl deleted file mode 100644 index 1b533cba3d2..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dp.aidl +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothCodecConfig; -import android.bluetooth.BluetoothCodecStatus; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP service - * - * @hide - */ -interface IBluetoothA2dp { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isAvrcpAbsoluteVolumeSupported(); - oneway void adjustAvrcpAbsoluteVolume(int direction); - oneway void setAvrcpAbsoluteVolume(int volume); - boolean isA2dpPlaying(in BluetoothDevice device); - BluetoothCodecStatus getCodecStatus(); - oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); - oneway void enableOptionalCodecs(); - oneway void disableOptionalCodecs(); - int supportsOptionalCodecs(in BluetoothDevice device); - int getOptionalCodecsEnabled(in BluetoothDevice device); - oneway void setOptionalCodecsEnabled(in BluetoothDevice device, int value); -} diff --git a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl b/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl deleted file mode 100755 index d1458246dfe..00000000000 --- a/framework/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAudioConfig; -import android.bluetooth.BluetoothDevice; - -/** - * APIs for Bluetooth A2DP sink service - * - * @hide - */ -interface IBluetoothA2dpSink { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean isA2dpPlaying(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl b/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl deleted file mode 100644 index cfa11cac4a8..00000000000 --- a/framework/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothAvrcpPlayerSettings; -import android.bluetooth.BluetoothDevice; -import android.media.MediaMetadata; -import android.media.session.PlaybackState; - -/** - * APIs for Bluetooth AVRCP controller service - * - * @hide - */ -interface IBluetoothAvrcpController { - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); - void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); -} diff --git a/framework/java/android/bluetooth/IBluetoothCallback.aidl b/framework/java/android/bluetooth/IBluetoothCallback.aidl deleted file mode 100644 index e2809788d67..00000000000 --- a/framework/java/android/bluetooth/IBluetoothCallback.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009, 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; - -/** - * System private API for Bluetooth service callbacks. - * - * {@hide} - */ -interface IBluetoothCallback -{ - //void onRfcommChannelFound(int channel); - void onBluetoothStateChange(int prevState, int newState); -} diff --git a/framework/java/android/bluetooth/IBluetoothGatt.aidl b/framework/java/android/bluetooth/IBluetoothGatt.aidl deleted file mode 100644 index e87f0700722..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGatt.aidl +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.app.PendingIntent; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertisingSetParameters; -import android.bluetooth.le.PeriodicAdvertisingParameters; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; -import android.bluetooth.le.ResultStorageDescriptor; -import android.os.ParcelUuid; -import android.os.WorkSource; - -import android.bluetooth.IBluetoothGattCallback; -import android.bluetooth.IBluetoothGattServerCallback; -import android.bluetooth.le.IAdvertisingSetCallback; -import android.bluetooth.le.IPeriodicAdvertisingCallback; -import android.bluetooth.le.IScannerCallback; - -/** - * API for interacting with BLE / GATT - * @hide - */ -interface IBluetoothGatt { - List getDevicesMatchingConnectionStates(in int[] states); - - void registerScanner(in IScannerCallback callback, in WorkSource workSource); - void unregisterScanner(in int scannerId); - void startScan(in int scannerId, in ScanSettings settings, in List filters, - in List scanStorages, in String callingPackage); - void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List filters, - in String callingPackage); - void stopScanForIntent(in PendingIntent intent, in String callingPackage); - void stopScan(in int scannerId); - void flushPendingBatchResults(in int scannerId); - - void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData, - in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters, - in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, - in IAdvertisingSetCallback callback); - void stopAdvertisingSet(in IAdvertisingSetCallback callback); - - void getOwnAddress(in int advertiserId); - void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents); - void setAdvertisingData(in int advertiserId, in AdvertiseData data); - void setScanResponseData(in int advertiserId, in AdvertiseData data); - void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters); - void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters); - void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data); - void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable); - - void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback); - void unregisterSync(in IPeriodicAdvertisingCallback callback); - - void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); - - void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in boolean opportunistic, in int phy); - void clientDisconnect(in int clientIf, in String address); - void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void clientReadPhy(in int clientIf, in String address); - void refreshDevice(in int clientIf, in String address); - void discoverServices(in int clientIf, in String address); - void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid); - void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq); - void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid, - in int startHandle, in int endHandle, in int authReq); - void writeCharacteristic(in int clientIf, in String address, in int handle, - in int writeType, in int authReq, in byte[] value); - void readDescriptor(in int clientIf, in String address, in int handle, in int authReq); - void writeDescriptor(in int clientIf, in String address, in int handle, - in int authReq, in byte[] value); - void registerForNotification(in int clientIf, in String address, in int handle, in boolean enable); - void beginReliableWrite(in int clientIf, in String address); - void endReliableWrite(in int clientIf, in String address, in boolean execute); - void readRemoteRssi(in int clientIf, in String address); - void configureMTU(in int clientIf, in String address, in int mtu); - void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority); - - void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); - void unregisterServer(in int serverIf); - void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport); - void serverDisconnect(in int serverIf, in String address); - void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions); - void serverReadPhy(in int clientIf, in String address); - void addService(in int serverIf, in BluetoothGattService service); - void removeService(in int serverIf, in int handle); - void clearServices(in int serverIf); - void sendResponse(in int serverIf, in String address, in int requestId, - in int status, in int offset, in byte[] value); - void sendNotification(in int serverIf, in String address, in int handle, - in boolean confirm, in byte[] value); - void disconnectAll(); - void unregAll(); - int numHwTrackFiltersAvailable(); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattCallback.aidl deleted file mode 100644 index 4f85cdda87f..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattCallback.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.os.ParcelUuid; -import android.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattCallback { - void onClientRegistered(in int status, in int clientIf); - void onClientConnectionState(in int status, in int clientIf, - in boolean connected, in String address); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onSearchComplete(in String address, in List services, in int status); - void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value); - void onCharacteristicWrite(in String address, in int status, in int handle); - void onExecuteWrite(in String address, in int status); - void onDescriptorRead(in String address, in int status, in int handle, in byte[] value); - void onDescriptorWrite(in String address, in int status, in int handle); - void onNotify(in String address, in int handle, in byte[] value); - void onReadRemoteRssi(in String address, in int rssi, in int status); - void onConfigureMTU(in String address, in int mtu, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl deleted file mode 100644 index 74ee11fbd32..00000000000 --- a/framework/java/android/bluetooth/IBluetoothGattServerCallback.aidl +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2017 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.bluetooth.BluetoothGattService; - -/** - * Callback definitions for interacting with BLE / GATT - * @hide - */ -oneway interface IBluetoothGattServerCallback { - void onServerRegistered(in int status, in int serverIf); - void onServerConnectionState(in int status, in int serverIf, - in boolean connected, in String address); - void onServiceAdded(in int status, in BluetoothGattService service); - void onCharacteristicReadRequest(in String address, in int transId, in int offset, - in boolean isLong, in int handle); - void onDescriptorReadRequest(in String address, in int transId, - in int offset, in boolean isLong, - in int handle); - void onCharacteristicWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onDescriptorWriteRequest(in String address, in int transId, in int offset, - in int length, in boolean isPrep, in boolean needRsp, - in int handle, in byte[] value); - void onExecuteWrite(in String address, in int transId, in boolean execWrite); - void onNotificationSent(in String address, in int status); - void onMtuChanged(in String address, in int mtu); - void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status); - void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status); - void onConnectionUpdated(in String address, in int interval, in int latency, - in int timeout, in int status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadset.aidl b/framework/java/android/bluetooth/IBluetoothHeadset.aidl deleted file mode 100755 index 92ab8da7542..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadset.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Headset service - * - * {@hide} - */ -interface IBluetoothHeadset { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - boolean isAudioConnected(in BluetoothDevice device); - boolean sendVendorSpecificResultCode(in BluetoothDevice device, - in String command, - in String arg); - - // APIs that can be made public in future - int getBatteryUsageHint(in BluetoothDevice device); - - // Internal functions, not be made public - boolean acceptIncomingConnect(in BluetoothDevice device); - boolean rejectIncomingConnect(in BluetoothDevice device); - int getAudioState(in BluetoothDevice device); - - boolean isAudioOn(); - boolean connectAudio(); - boolean disconnectAudio(); - void setAudioRouteAllowed(boolean allowed); - boolean getAudioRouteAllowed(); - void setForceScoAudio(boolean forced); - boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); - boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); - oneway void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); - void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type); - boolean enableWBS(); - boolean disableWBS(); - void bindResponse(int ind_id, boolean ind_status); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl deleted file mode 100644 index e571b009f04..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadsetClientCall; -import android.os.Bundle; - -/** - * API for Bluetooth Headset Client service (HFP HF Role) - * - * {@hide} - */ -interface IBluetoothHeadsetClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - - boolean startVoiceRecognition(in BluetoothDevice device); - boolean stopVoiceRecognition(in BluetoothDevice device); - - List getCurrentCalls(in BluetoothDevice device); - Bundle getCurrentAgEvents(in BluetoothDevice device); - - boolean acceptCall(in BluetoothDevice device, int flag); - boolean holdCall(in BluetoothDevice device); - boolean rejectCall(in BluetoothDevice device); - boolean terminateCall(in BluetoothDevice device, in BluetoothHeadsetClientCall call); - - boolean enterPrivateMode(in BluetoothDevice device, int index); - boolean explicitCallTransfer(in BluetoothDevice device); - - BluetoothHeadsetClientCall dial(in BluetoothDevice device, String number); - - boolean sendDTMF(in BluetoothDevice device, byte code); - boolean getLastVoiceTagNumber(in BluetoothDevice device); - - int getAudioState(in BluetoothDevice device); - boolean connectAudio(in BluetoothDevice device); - boolean disconnectAudio(in BluetoothDevice device); - void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed); - boolean getAudioRouteAllowed(in BluetoothDevice device); - - Bundle getCurrentAgFeatures(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl b/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl deleted file mode 100644 index d5e64f6faec..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHeadsetPhone.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 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; - -/** - * API for Bluetooth Headset Phone Service in phone app - * - * {@hide} - */ -interface IBluetoothHeadsetPhone { - // Internal functions, not be made public - boolean answerCall(); - boolean hangupCall(); - boolean sendDtmf(int dtmf); - boolean processChld(int chld); - String getNetworkOperator(); - String getSubscriberNumber(); - boolean listCurrentCalls(); - boolean queryPhoneState(); - - // Internal for phone app to call - void updateBtHandsfreeAfterRadioTechnologyChange(); - void cdmaSwapSecondCallState(); - void cdmaSetSecondCallState(boolean state); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealth.aidl b/framework/java/android/bluetooth/IBluetoothHealth.aidl deleted file mode 100644 index a84a42cb9ce..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealth.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.bluetooth.IBluetoothHealthCallback; -import android.os.ParcelFileDescriptor; - -/** - * API for Bluetooth Health service - * - * {@hide} - */ -interface IBluetoothHealth -{ - boolean registerAppConfiguration(in BluetoothHealthAppConfiguration config, - in IBluetoothHealthCallback callback); - boolean unregisterAppConfiguration(in BluetoothHealthAppConfiguration config); - boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, - int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); - ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); - List getConnectedHealthDevices(); - List getHealthDevicesMatchingConnectionStates(in int[] states); - int getHealthDeviceConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl b/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl deleted file mode 100644 index 0ace9fe1230..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHealthCallback.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011, 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHealthAppConfiguration; -import android.os.ParcelFileDescriptor; - -/** - *@hide - */ -interface IBluetoothHealthCallback -{ - void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); - void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, - in BluetoothDevice device, int prevState, int newState, in - ParcelFileDescriptor fd, int id); -} diff --git a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl deleted file mode 100644 index a737198ad95..00000000000 --- a/framework/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; - -/** @hide */ -interface IBluetoothHidDeviceCallback { - void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); - void onConnectionStateChanged(in BluetoothDevice device, in int state); - void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize); - void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - void onSetProtocol(in BluetoothDevice device, in byte protocol); - void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data); - void onVirtualCableUnplug(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl b/framework/java/android/bluetooth/IBluetoothInputDevice.aidl deleted file mode 100644 index 5bd3f781932..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputDevice.aidl +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth HID service - * - * {@hide} - */ -interface IBluetoothInputDevice { - // Public API - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); - /** - * @hide - */ - boolean getProtocolMode(in BluetoothDevice device); - /** - * @hide - */ - boolean virtualUnplug(in BluetoothDevice device); - /** - * @hide - */ - boolean setProtocolMode(in BluetoothDevice device, int protocolMode); - /** - * @hide - */ - boolean getReport(in BluetoothDevice device, byte reportType, byte reportId, int bufferSize); - /** - * @hide - */ - boolean setReport(in BluetoothDevice device, byte reportType, String report); - /** - * @hide - */ - boolean sendData(in BluetoothDevice device, String report); - /** - * @hide - */ - boolean getIdleTime(in BluetoothDevice device); - /** - * @hide - */ - boolean setIdleTime(in BluetoothDevice device, byte idleTime); -} diff --git a/framework/java/android/bluetooth/IBluetoothInputHost.aidl b/framework/java/android/bluetooth/IBluetoothInputHost.aidl deleted file mode 100644 index 6c4993f750e..00000000000 --- a/framework/java/android/bluetooth/IBluetoothInputHost.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHidDeviceAppConfiguration; -import android.bluetooth.IBluetoothHidDeviceCallback; -import android.bluetooth.BluetoothHidDeviceAppSdpSettings; -import android.bluetooth.BluetoothHidDeviceAppQosSettings; - -/** @hide */ -interface IBluetoothInputHost { - boolean registerApp(in BluetoothHidDeviceAppConfiguration config, - in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, - in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); - boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); - boolean sendReport(in BluetoothDevice device, in int id, in byte[] data); - boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data); - boolean reportError(in BluetoothDevice device, byte error); - boolean unplug(in BluetoothDevice device); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl deleted file mode 100644 index 2d5fc98e5e5..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; -import android.bluetooth.IBluetoothStateChangeCallback; - -/** - * System private API for talking with the Bluetooth service. - * - * {@hide} - */ -interface IBluetoothManager -{ - IBluetooth registerAdapter(in IBluetoothManagerCallback callback); - void unregisterAdapter(in IBluetoothManagerCallback callback); - void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); - void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); - boolean isEnabled(); - boolean enable(String packageName); - boolean enableNoAutoConnect(String packageName); - boolean disable(String packageName, boolean persist); - int getState(); - IBluetoothGatt getBluetoothGatt(); - - boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - - String getAddress(); - String getName(); - - boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable, String packageName); - boolean isBleAppPresent(); -} - diff --git a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl b/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl deleted file mode 100644 index 8104d219948..00000000000 --- a/framework/java/android/bluetooth/IBluetoothManagerCallback.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.IBluetooth; - -/** - * API for Communication between BluetoothAdapter and BluetoothManager - * - * {@hide} - */ -oneway interface IBluetoothManagerCallback { - void onBluetoothServiceUp(in IBluetooth bluetoothService); - void onBluetoothServiceDown(); - void onBrEdrDown(); -} diff --git a/framework/java/android/bluetooth/IBluetoothMap.aidl b/framework/java/android/bluetooth/IBluetoothMap.aidl deleted file mode 100644 index d4af63d1fd2..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth MAP service - * - * {@hide} - */ -interface IBluetoothMap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothMapClient.aidl b/framework/java/android/bluetooth/IBluetoothMapClient.aidl deleted file mode 100644 index df45af91c92..00000000000 --- a/framework/java/android/bluetooth/IBluetoothMapClient.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; -import android.net.Uri; - -/** - * System private API for Bluetooth MAP MCE service - * - * {@hide} - */ -interface IBluetoothMapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device,in int priority); - int getPriority(in BluetoothDevice device); - boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message, - in PendingIntent sentIntent, in PendingIntent deliveryIntent); - boolean getUnreadMessages(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPan.aidl b/framework/java/android/bluetooth/IBluetoothPan.aidl deleted file mode 100644 index 5a323477704..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPan.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Pan service - * - * {@hide} - */ -interface IBluetoothPan { - // Public API - boolean isTetheringOn(); - void setBluetoothTethering(boolean value); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbap.aidl b/framework/java/android/bluetooth/IBluetoothPbap.aidl deleted file mode 100644 index 7cc77d110e9..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbap.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008 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.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth pbap service - * - * {@hide} - */ -interface IBluetoothPbap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - void disconnect(); - boolean isConnected(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl b/framework/java/android/bluetooth/IBluetoothPbapClient.aidl deleted file mode 100644 index 6d4c5a6f90b..00000000000 --- a/framework/java/android/bluetooth/IBluetoothPbapClient.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.bluetooth.BluetoothDevice; - -/** - * API for Bluetooth Phone Book Access Provile Client Side - * - * {@hide} - */ -interface IBluetoothPbapClient { - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl deleted file mode 100755 index 541583ff553..00000000000 --- a/framework/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2014 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.content.ComponentName; -import android.os.IBinder; - -/** - * Callback for bluetooth profile connections. - * - * {@hide} - */ -oneway interface IBluetoothProfileServiceConnection { - void onServiceConnected(in ComponentName comp, in IBinder service); - void onServiceDisconnected(in ComponentName comp); -} diff --git a/framework/java/android/bluetooth/IBluetoothSap.aidl b/framework/java/android/bluetooth/IBluetoothSap.aidl deleted file mode 100644 index 8970639467c..00000000000 --- a/framework/java/android/bluetooth/IBluetoothSap.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * System private API for Bluetooth SAP service - * - * {@hide} - */ -interface IBluetoothSap { - int getState(); - BluetoothDevice getClient(); - boolean connect(in BluetoothDevice device); - boolean disconnect(in BluetoothDevice device); - boolean isConnected(in BluetoothDevice device); - List getConnectedDevices(); - List getDevicesMatchingConnectionStates(in int[] states); - int getConnectionState(in BluetoothDevice device); - boolean setPriority(in BluetoothDevice device, int priority); - int getPriority(in BluetoothDevice device); -} diff --git a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl deleted file mode 100644 index 0da4e884328..00000000000 --- a/framework/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011, 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; - -/** - * System private API for Bluetooth state change callback. - * - * {@hide} - */ -oneway interface IBluetoothStateChangeCallback -{ - void onBluetoothStateChange(boolean on); -} diff --git a/framework/java/android/bluetooth/OobData.aidl b/framework/java/android/bluetooth/OobData.aidl deleted file mode 100644 index d831c647769..00000000000 --- a/framework/java/android/bluetooth/OobData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -parcelable OobData; diff --git a/framework/java/android/bluetooth/le/AdvertiseData.aidl b/framework/java/android/bluetooth/le/AdvertiseData.aidl deleted file mode 100644 index bcbf2243e18..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseData; diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl b/framework/java/android/bluetooth/le/AdvertiseSettings.aidl deleted file mode 100644 index 9f47d74ca53..00000000000 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable AdvertiseSettings; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl deleted file mode 100644 index 39034a001fa..00000000000 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable AdvertisingSetParameters; diff --git a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl deleted file mode 100644 index 3628c775b79..00000000000 --- a/framework/java/android/bluetooth/le/IAdvertisingSetCallback.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IAdvertisingSetCallback { - void onAdvertisingSetStarted(in int advertiserId, in int tx_power, in int status); - void onOwnAddressRead(in int advertiserId, in int addressType, in String address); - void onAdvertisingSetStopped(in int advertiserId); - void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); - void onAdvertisingDataSet(in int advertiserId, in int status); - void onScanResponseDataSet(in int advertiserId, in int status); - void onAdvertisingParametersUpdated(in int advertiserId, in int tx_power, in int status); - void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status); - void onPeriodicAdvertisingDataSet(in int advertiserId, in int status); - void onPeriodicAdvertisingEnabled(in int advertiserId, in boolean enable, in int status); -} diff --git a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl deleted file mode 100644 index a76c54d4ab4..00000000000 --- a/framework/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.PeriodicAdvertisingReport; - -/** - * Callback definitions for interacting with Periodic Advertising - * @hide - */ -oneway interface IPeriodicAdvertisingCallback { - - void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid, - in int skip, in int timeout, in int status); - void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report); - void onSyncLost(in int syncHandle); -} diff --git a/framework/java/android/bluetooth/le/IScannerCallback.aidl b/framework/java/android/bluetooth/le/IScannerCallback.aidl deleted file mode 100644 index 8cbbaef41ae..00000000000 --- a/framework/java/android/bluetooth/le/IScannerCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.le; - -import android.bluetooth.le.ScanResult; - -/** - * Callback definitions for interacting with Advertiser - * @hide - */ -oneway interface IScannerCallback { - void onScannerRegistered(in int status, in int scannerId); - - void onScanResult(in ScanResult scanResult); - void onBatchScanResults(in List batchResults); - void onFoundOrLost(in boolean onFound, in ScanResult scanResult); - void onScanManagerErrorCallback(in int errorCode); -} diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl deleted file mode 100644 index f4bea22a12f..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingParameters; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl deleted file mode 100644 index 547d09611fd..00000000000 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2017 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.le; - -parcelable PeriodicAdvertisingReport; diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl b/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl deleted file mode 100644 index f218a01a5d1..00000000000 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.aidl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -/** - * {@hide} - */ - -parcelable ResultStorageDescriptor; diff --git a/framework/java/android/bluetooth/le/ScanFilter.aidl b/framework/java/android/bluetooth/le/ScanFilter.aidl deleted file mode 100644 index 4cecfe62e11..00000000000 --- a/framework/java/android/bluetooth/le/ScanFilter.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanFilter; diff --git a/framework/java/android/bluetooth/le/ScanResult.aidl b/framework/java/android/bluetooth/le/ScanResult.aidl deleted file mode 100644 index 39430350dac..00000000000 --- a/framework/java/android/bluetooth/le/ScanResult.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanResult; \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/ScanSettings.aidl b/framework/java/android/bluetooth/le/ScanSettings.aidl deleted file mode 100644 index eb169c1209f..00000000000 --- a/framework/java/android/bluetooth/le/ScanSettings.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2014 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.le; - -parcelable ScanSettings; -- GitLab From 2cc51ca30be4234c553a415d018ced43f65b38cc Mon Sep 17 00:00:00 2001 From: Vinay Kalia Date: Fri, 28 Jul 2017 15:09:57 -0700 Subject: [PATCH 0818/1408] Fix BLE scan leaks If an app starts multiple scans with same callback then there is a collision in mLeScanClients hash map which results in leaking first scan. This change fixes it by not allowing the second scan with same callback. BUG: 62389939 BUG: 38198694 Test: Tested applications which started multiple scans with same callback. Change-Id: I569069a40b6f8b4b8bb070731225e732c6b23ec8 (cherry picked from commit 49cdd0df0697809ddde41f72a232ea8922824123) --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 1eac395bd06..e3bc78e5a2b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -205,7 +205,8 @@ public final class BluetoothLeScanner { } synchronized (mLeScanClients) { if (callback != null && mLeScanClients.containsKey(callback)) { - postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED); + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_ALREADY_STARTED); } IBluetoothGatt gatt; try { -- GitLab From 90740d5520ca5f34b7bda177bdba5a343bf331b4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 8 Aug 2017 15:47:57 -0700 Subject: [PATCH 0819/1408] Bluetooth: AdvertiseData parceling simplification Use writeTypedArray instead of writeList - this will not serialize string with type name, which is unnecessary Use createByteArray instead of readByteArra - it takes care of null and empty array handling Test: sl4a FilteringTest, BleAdvertiseApiTest Change-Id: I6a22674a0bf9933e39691de7f2b2b52a060ae368 --- .../android/bluetooth/le/AdvertiseData.java | 51 +++++-------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index ff0db9aeb61..bde2d2f890f 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -141,32 +141,18 @@ public final class AdvertiseData implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeList(mServiceUuids); + dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags); // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { dest.writeInt(mManufacturerSpecificData.keyAt(i)); - byte[] data = mManufacturerSpecificData.valueAt(i); - if (data == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(data.length); - dest.writeByteArray(data); - } + dest.writeByteArray(mManufacturerSpecificData.valueAt(i)); } dest.writeInt(mServiceData.size()); for (ParcelUuid uuid : mServiceData.keySet()) { - dest.writeParcelable(uuid, flags); - byte[] data = mServiceData.get(uuid); - if (data == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeInt(data.length); - dest.writeByteArray(data); - } + dest.writeTypedObject(uuid, flags); + dest.writeByteArray(mServiceData.get(uuid)); } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); @@ -182,33 +168,22 @@ public final class AdvertiseData implements Parcelable { @Override public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); - @SuppressWarnings("unchecked") - List uuids = in.readArrayList(ParcelUuid.class.getClassLoader()); - if (uuids != null) { - for (ParcelUuid uuid : uuids) { - builder.addServiceUuid(uuid); - } + ArrayList uuids = in.createTypedArrayList(ParcelUuid.CREATOR); + for (ParcelUuid uuid : uuids) { + builder.addServiceUuid(uuid); } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - builder.addManufacturerData(manufacturerId, manufacturerData); - } + byte[] manufacturerData = in.createByteArray(); + builder.addManufacturerData(manufacturerId, manufacturerData); } int serviceDataSize = in.readInt(); for (int i = 0; i < serviceDataSize; ++i) { - ParcelUuid serviceDataUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - builder.addServiceData(serviceDataUuid, serviceData); - } + ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR); + byte[] serviceData = in.createByteArray(); + builder.addServiceData(serviceDataUuid, serviceData); } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); -- GitLab From 91135a1c0de13cd74dfecc2028fbdd8eb56cd705 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Aug 2017 07:19:12 -0700 Subject: [PATCH 0820/1408] Bluetooth: Fix startAdvertisingSet error handling Calls to old advertising API (startAdvertising), should never throw exceptions. Instead, it used to post failure callback. This behaviour was accidentally modified when implementing new API. Right now, instead of posting error callback, we throw IllegalArgumentException if we fail to obtain BluetoothGatt object, or the call to startAdvertisingSet fails. This patch brings back the old behaviour to the API. It also makes new API post callback instead of throwing exception in this error case. Bug: 63819108 Test: manual Change-Id: I897b99839f899ca3f3dc609918d665c8c327b777 --- .../bluetooth/le/BluetoothLeAdvertiser.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index dfd5996c6a0..5830c278c8d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -415,7 +415,8 @@ public final class BluetoothLeAdvertiser { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - throw new IllegalStateException("Failed to get Bluetooth"); + postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } IAdvertisingSetCallback wrapped = wrap(callback, handler); @@ -429,7 +430,8 @@ public final class BluetoothLeAdvertiser { periodicData, duration, maxExtendedAdvertisingEvents, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); - throw new IllegalStateException("Failed to start advertising set"); + postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } } @@ -648,6 +650,16 @@ public final class BluetoothLeAdvertiser { }; } + private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback, + final int error) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onAdvertisingSetStarted(null, 0, error); + } + }); + } + private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override -- GitLab From 7866d3821e6bdd3c38971fac73807960a81b600b Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Aug 2017 06:15:43 -0700 Subject: [PATCH 0821/1408] Bluetooth: Don't throw exception when stopAdvertisingSet fails When advertising is stopped while Bluetooth is disabled, we should not throw any exceptions, just log the failure. This was the default behaviour before the AdvertisingSet was introduced. Bug: 63819108 Test: manual Change-Id: I518e071b77b127973aee6f24fa6ced4f28bc9531 --- framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index dfd5996c6a0..fb9b19f1fdd 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -453,8 +453,7 @@ public final class BluetoothLeAdvertiser { gatt.stopAdvertisingSet(wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising - ", e); - throw new IllegalStateException("Failed to stop advertising"); - } + } } /** -- GitLab From fc925a0b56e9c2686be3e8c2c070e2945ae783db Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Aug 2017 06:15:43 -0700 Subject: [PATCH 0822/1408] Bluetooth: Don't throw exception when stopAdvertisingSet fails When advertising is stopped while Bluetooth is disabled, we should not throw any exceptions, just log the failure. This was the default behaviour before the AdvertisingSet was introduced. Bug: 63819108 Test: manual Change-Id: I518e071b77b127973aee6f24fa6ced4f28bc9531 Merged-In: I518e071b77b127973aee6f24fa6ced4f28bc9531 (cherry picked from commit 7866d3821e6bdd3c38971fac73807960a81b600b) --- framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index dfd5996c6a0..fb9b19f1fdd 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -453,8 +453,7 @@ public final class BluetoothLeAdvertiser { gatt.stopAdvertisingSet(wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising - ", e); - throw new IllegalStateException("Failed to stop advertising"); - } + } } /** -- GitLab From 6da633fa5002bf14ff8e85e7c738d84cf00ac2e9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 17 Aug 2017 07:19:12 -0700 Subject: [PATCH 0823/1408] Bluetooth: Fix startAdvertisingSet error handling Calls to old advertising API (startAdvertising), should never throw exceptions. Instead, it used to post failure callback. This behaviour was accidentally modified when implementing new API. Right now, instead of posting error callback, we throw IllegalArgumentException if we fail to obtain BluetoothGatt object, or the call to startAdvertisingSet fails. This patch brings back the old behaviour to the API. It also makes new API post callback instead of throwing exception in this error case. Bug: 63819108 Test: manual Change-Id: I897b99839f899ca3f3dc609918d665c8c327b777 Merged-In: I897b99839f899ca3f3dc609918d665c8c327b777 (cherry picked from commit 91135a1c0de13cd74dfecc2028fbdd8eb56cd705) --- .../bluetooth/le/BluetoothLeAdvertiser.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index dfd5996c6a0..5830c278c8d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -415,7 +415,8 @@ public final class BluetoothLeAdvertiser { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - throw new IllegalStateException("Failed to get Bluetooth"); + postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } IAdvertisingSetCallback wrapped = wrap(callback, handler); @@ -429,7 +430,8 @@ public final class BluetoothLeAdvertiser { periodicData, duration, maxExtendedAdvertisingEvents, wrapped); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); - throw new IllegalStateException("Failed to start advertising set"); + postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } } @@ -648,6 +650,16 @@ public final class BluetoothLeAdvertiser { }; } + private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback, + final int error) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onAdvertisingSetStarted(null, 0, error); + } + }); + } + private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override -- GitLab From 910201beb0bde1dcf6b33e4ec5d1eb60042419d8 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 22 Aug 2017 16:06:54 -0700 Subject: [PATCH 0824/1408] Fix checkstyle errors (1/2) * Automatic style corrections through IDE Bug: 63596319 Test: make checkbuild, no manual changes, no functional changes Change-Id: I2397d55abc34c9b7a9b748bec6137778df3421a7 --- .../java/android/bluetooth/BluetoothA2dp.java | 151 +-- .../android/bluetooth/BluetoothA2dpSink.java | 121 +- .../BluetoothActivityEnergyInfo.java | 40 +- .../android/bluetooth/BluetoothAdapter.java | 498 ++++---- .../bluetooth/BluetoothAssignedNumbers.java | 4 +- .../bluetooth/BluetoothAudioConfig.java | 26 +- .../android/bluetooth/BluetoothAvrcp.java | 118 +- .../bluetooth/BluetoothAvrcpController.java | 87 +- .../BluetoothAvrcpPlayerSettings.java | 14 +- .../android/bluetooth/BluetoothClass.java | 171 +-- .../bluetooth/BluetoothCodecConfig.java | 149 ++- .../bluetooth/BluetoothCodecStatus.java | 50 +- .../android/bluetooth/BluetoothDevice.java | 506 ++++---- .../bluetooth/BluetoothDevicePicker.java | 12 +- .../java/android/bluetooth/BluetoothGatt.java | 1028 +++++++++-------- .../bluetooth/BluetoothGattCallback.java | 103 +- .../BluetoothGattCharacteristic.java | 132 ++- .../bluetooth/BluetoothGattDescriptor.java | 21 +- .../BluetoothGattIncludedService.java | 10 +- .../bluetooth/BluetoothGattServer.java | 673 +++++------ .../BluetoothGattServerCallback.java | 78 +- .../bluetooth/BluetoothGattService.java | 56 +- .../android/bluetooth/BluetoothHeadset.java | 232 ++-- .../bluetooth/BluetoothHeadsetClient.java | 399 +++---- .../bluetooth/BluetoothHeadsetClientCall.java | 52 +- .../android/bluetooth/BluetoothHealth.java | 116 +- .../BluetoothHealthAppConfiguration.java | 50 +- .../bluetooth/BluetoothHealthCallback.java | 19 +- .../BluetoothHidDeviceAppConfiguration.java | 22 +- .../BluetoothHidDeviceAppQosSettings.java | 29 +- .../BluetoothHidDeviceAppSdpSettings.java | 24 +- .../bluetooth/BluetoothHidDeviceCallback.java | 30 +- .../bluetooth/BluetoothInputDevice.java | 140 +-- .../android/bluetooth/BluetoothInputHost.java | 113 +- .../bluetooth/BluetoothInputStream.java | 28 +- .../android/bluetooth/BluetoothManager.java | 49 +- .../java/android/bluetooth/BluetoothMap.java | 101 +- .../android/bluetooth/BluetoothMapClient.java | 10 +- .../bluetooth/BluetoothMasInstance.java | 25 +- .../bluetooth/BluetoothOutputStream.java | 34 +- .../java/android/bluetooth/BluetoothPan.java | 117 +- .../java/android/bluetooth/BluetoothPbap.java | 80 +- .../bluetooth/BluetoothPbapClient.java | 71 +- .../android/bluetooth/BluetoothProfile.java | 49 +- .../java/android/bluetooth/BluetoothSap.java | 99 +- .../bluetooth/BluetoothServerSocket.java | 69 +- .../android/bluetooth/BluetoothSocket.java | 308 ++--- .../java/android/bluetooth/BluetoothUuid.java | 58 +- framework/java/android/bluetooth/OobData.java | 5 +- .../java/android/bluetooth/SdpMasRecord.java | 44 +- .../java/android/bluetooth/SdpMnsRecord.java | 30 +- .../android/bluetooth/SdpOppOpsRecord.java | 14 +- .../java/android/bluetooth/SdpPseRecord.java | 33 +- .../java/android/bluetooth/SdpRecord.java | 7 +- .../java/android/bluetooth/SdpSapsRecord.java | 3 +- .../java/android/bluetooth/UidTraffic.java | 1 + .../bluetooth/le/AdvertiseCallback.java | 4 +- .../android/bluetooth/le/AdvertiseData.java | 17 +- .../bluetooth/le/AdvertiseSettings.java | 37 +- .../android/bluetooth/le/AdvertisingSet.java | 53 +- .../bluetooth/le/AdvertisingSetCallback.java | 37 +- .../le/AdvertisingSetParameters.java | 161 +-- .../bluetooth/le/BluetoothLeAdvertiser.java | 211 ++-- .../bluetooth/le/BluetoothLeScanner.java | 35 +- .../bluetooth/le/BluetoothLeUtils.java | 7 +- .../le/PeriodicAdvertisingCallback.java | 23 +- .../le/PeriodicAdvertisingManager.java | 366 +++--- .../le/PeriodicAdvertisingParameters.java | 35 +- .../le/PeriodicAdvertisingReport.java | 43 +- .../bluetooth/le/ResultStorageDescriptor.java | 18 +- .../android/bluetooth/le/ScanCallback.java | 8 +- .../java/android/bluetooth/le/ScanFilter.java | 152 +-- .../java/android/bluetooth/le/ScanRecord.java | 7 +- .../java/android/bluetooth/le/ScanResult.java | 61 +- .../android/bluetooth/le/ScanSettings.java | 57 +- .../android/bluetooth/le/TruncatedFilter.java | 1 + 76 files changed, 4123 insertions(+), 3719 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c58eaa14ed8..7d6879d5a61 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * This class provides the public APIs to control the Bluetooth A2DP * profile. * - *

        BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP + *

        BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothA2dp proxy object. * @@ -60,9 +60,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -74,7 +74,7 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent used to broadcast the change in the Playing state of the A2DP @@ -82,9 +82,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -95,12 +95,12 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PLAYING_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; + "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; + "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; /** * Intent used to broadcast the change in the Audio Codec state of the @@ -108,9 +108,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * *

        This intent will have 2 extras: *

          - *
        • {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently - * connected, otherwise it is not included.
        • + *
        • {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently + * connected, otherwise it is not included.
        • *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to @@ -120,58 +120,71 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CODEC_CONFIG_CHANGED = - "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; + "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; /** * A2DP sink device is streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_PLAYING_STATE_CHANGED} intent. */ - public static final int STATE_PLAYING = 10; + public static final int STATE_PLAYING = 10; /** * A2DP sink device is NOT streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_PLAYING_STATE_CHANGED} intent. */ - public static final int STATE_NOT_PLAYING = 11; + public static final int STATE_NOT_PLAYING = 11; /** * We don't have a stored preference for whether or not the given A2DP sink device supports * optional codecs. - * @hide */ + * + * @hide + */ public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; /** * The given A2DP sink device does not support optional codecs. - * @hide */ + * + * @hide + */ public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; /** * The given A2DP sink device does support optional codecs. - * @hide */ + * + * @hide + */ public static final int OPTIONAL_CODECS_SUPPORTED = 1; /** * We don't have a stored preference for whether optional codecs should be enabled or disabled * for the given A2DP device. - * @hide */ + * + * @hide + */ public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; /** * Optional codecs should be disabled for the given A2DP device. - * @hide */ + * + * @hide + */ public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; /** - * Optional codecs should be enabled for the given A2DP device. - * @hide */ + * Optional codecs should be enabled for the given A2DP device. + * + * @hide + */ public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; private Context mContext; private ServiceListener mServiceListener; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") private IBluetoothA2dp mService; + @GuardedBy("mServiceLock") + private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -193,21 +206,21 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } finally { mServiceLock.readLock().unlock(); } } } - }; + }; + /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. - * */ /*package*/ BluetoothA2dp(Context context, ServiceListener l) { mContext = context; @@ -218,7 +231,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -244,7 +257,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -265,6 +278,7 @@ public final class BluetoothA2dp implements BluetoothProfile { // The empty finalize needs to be kept or the // cts signature tests would fail. } + /** * Initiate connection to a profile of the remote bluetooth device. * @@ -283,8 +297,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -292,7 +305,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { return mService.connect(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -327,8 +340,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -336,7 +348,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { return mService.disconnect(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -397,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { return mService.getConnectionState(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -414,7 +426,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * Set priority of the profile * *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} @@ -430,9 +442,9 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + priority != BluetoothProfile.PRIORITY_ON) { return false; } return mService.setPriority(device, priority); @@ -464,7 +476,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { return mService.getPriority(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -560,7 +572,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { return mService.isA2dpPlaying(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -577,6 +589,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * This function checks if the remote device is an AVCRP * target and thus whether we should send volume keys * changes or not. + * * @hide */ public boolean shouldSendVolumeKeys(BluetoothDevice device) { @@ -584,7 +597,7 @@ public final class BluetoothA2dp implements BluetoothProfile { ParcelUuid[] uuids = device.getUuids(); if (uuids == null) return false; - for (ParcelUuid uuid: uuids) { + for (ParcelUuid uuid : uuids) { if (BluetoothUuid.isAvrcpTarget(uuid)) { return true; } @@ -691,8 +704,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @param device The device to check * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or - * OPTIONAL_CODECS_SUPPORTED. - * + * OPTIONAL_CODECS_SUPPORTED. * @hide */ public int supportsOptionalCodecs(BluetoothDevice device) { @@ -716,8 +728,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @param device The device in question. * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or - * OPTIONAL_CODECS_PREF_DISABLED. - * + * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ public int getOptionalCodecsEnabled(BluetoothDevice device) { @@ -741,8 +752,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @param device The device to set this preference for. * @param value Whether the optional codecs should be enabled for this device. This should be - * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or - * OPTIONAL_CODECS_PREF_DISABLED. + * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or + * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { @@ -772,24 +783,25 @@ public final class BluetoothA2dp implements BluetoothProfile { * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. + * * @hide */ public static String stateToString(int state) { switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - case STATE_PLAYING: - return "playing"; - case STATE_NOT_PLAYING: - return "not playing"; - default: - return ""; + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; + default: + return ""; } } @@ -807,6 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); try { @@ -822,18 +835,18 @@ public final class BluetoothA2dp implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 9dfc4b442fa..78dab1b2a4c 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -32,7 +32,7 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth A2DP Sink * profile. * - *

        BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink + *

        BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothA2dpSink proxy object. * @@ -49,9 +49,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -62,7 +62,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * receive. */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent used to broadcast the change in the Playing state of the A2DP Sink @@ -70,9 +70,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -82,21 +82,21 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * receive. */ public static final String ACTION_PLAYING_STATE_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED"; + "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED"; /** * A2DP sink device is streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_PLAYING_STATE_CHANGED} intent. */ - public static final int STATE_PLAYING = 10; + public static final int STATE_PLAYING = 10; /** * A2DP sink device is NOT streaming music. This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_PLAYING_STATE_CHANGED} intent. */ - public static final int STATE_NOT_PLAYING = 11; + public static final int STATE_NOT_PLAYING = 11; /** * Intent used to broadcast the change in the Playing state of the A2DP Sink @@ -104,15 +104,15 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */ public static final String ACTION_AUDIO_CONFIG_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED"; + "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED"; /** * Extra for the {@link #ACTION_AUDIO_CONFIG_CHANGED} intent. @@ -133,33 +133,33 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; + /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. - * */ /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { mContext = context; @@ -170,7 +170,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -196,7 +196,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -206,7 +206,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -215,6 +215,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public void finalize() { close(); } + /** * Initiate connection to a profile of the remote bluetooth device. * @@ -233,14 +234,13 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { @@ -274,14 +274,13 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -333,7 +332,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -356,10 +355,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * * {@see BluetoothAudioConfig} */ - public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { + public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mService.getAudioConfig(device); } catch (RemoteException e) { @@ -375,7 +374,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Set priority of the profile * *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} @@ -389,20 +388,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON){ + priority != BluetoothProfile.PRIORITY_ON) { return false; } try { return mService.setPriority(device, priority); } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return false; } /** @@ -421,7 +420,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -442,7 +441,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean isA2dpPlaying(BluetoothDevice device) { if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mService.isA2dpPlaying(device); } catch (RemoteException e) { @@ -458,24 +457,25 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. + * * @hide */ public static String stateToString(int state) { switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - case STATE_PLAYING: - return "playing"; - case STATE_NOT_PLAYING: - return "not playing"; - default: - return ""; + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; + default: + return ""; } } @@ -489,6 +489,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { BluetoothA2dpSink.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; @@ -499,18 +500,18 @@ public final class BluetoothA2dpSink implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index 84f6060d3ae..da2aba765b6 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -25,6 +25,7 @@ import java.util.Arrays; * Record of energy and activity information from controller and * underlying bt stack state.Timestamp the record with system * time + * * @hide */ public final class BluetoothActivityEnergyInfo implements Parcelable { @@ -42,7 +43,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { public static final int BT_STACK_STATE_STATE_IDLE = 3; public BluetoothActivityEnergyInfo(long timestamp, int stackState, - long txTime, long rxTime, long idleTime, long energyUsed) { + long txTime, long rxTime, long idleTime, long energyUsed) { mTimestamp = timestamp; mBluetoothStackState = stackState; mControllerTxTimeMs = txTime; @@ -65,26 +66,26 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { @Override public String toString() { return "BluetoothActivityEnergyInfo{" - + " mTimestamp=" + mTimestamp - + " mBluetoothStackState=" + mBluetoothStackState - + " mControllerTxTimeMs=" + mControllerTxTimeMs - + " mControllerRxTimeMs=" + mControllerRxTimeMs - + " mControllerIdleTimeMs=" + mControllerIdleTimeMs - + " mControllerEnergyUsed=" + mControllerEnergyUsed - + " mUidTraffic=" + Arrays.toString(mUidTraffic) - + " }"; + + " mTimestamp=" + mTimestamp + + " mBluetoothStackState=" + mBluetoothStackState + + " mControllerTxTimeMs=" + mControllerTxTimeMs + + " mControllerRxTimeMs=" + mControllerRxTimeMs + + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + + " mControllerEnergyUsed=" + mControllerEnergyUsed + + " mUidTraffic=" + Arrays.toString(mUidTraffic) + + " }"; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { - return new BluetoothActivityEnergyInfo(in); - } + public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { + return new BluetoothActivityEnergyInfo(in); + } - public BluetoothActivityEnergyInfo[] newArray(int size) { - return new BluetoothActivityEnergyInfo[size]; - } - }; + public BluetoothActivityEnergyInfo[] newArray(int size) { + return new BluetoothActivityEnergyInfo[size]; + } + }; @SuppressWarnings("unchecked") public void writeToParcel(Parcel out, int flags) { @@ -131,6 +132,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { /** * product of current(mA), voltage(V) and time(ms) + * * @return energy used */ public long getControllerEnergyUsed() { @@ -156,8 +158,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { * @return if the record is valid */ public boolean isValid() { - return ((mControllerTxTimeMs >=0) && - (mControllerRxTimeMs >=0) && - (mControllerIdleTimeMs >=0)); + return ((mControllerTxTimeMs >= 0) && + (mControllerRxTimeMs >= 0) && + (mControllerIdleTimeMs >= 0)); } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index fc3a72482a7..1b9ce88de08 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -80,7 +80,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to * listen for incoming connection requests with - * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}; or start a scan for + * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

        *

        This class is thread safe.

        @@ -92,7 +92,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; *
        *

        Developer Guides

        *

        - * For more information about using Bluetooth, read the Bluetooth developer * guide. *

        @@ -161,7 +161,8 @@ public final class BluetoothAdapter { @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON, STATE_BLE_ON, STATE_BLE_TURNING_OFF}) @Retention(RetentionPolicy.SOURCE) - public @interface AdapterState {} + public @interface AdapterState { + } /** * Indicates the local Bluetooth adapter is off. @@ -185,36 +186,48 @@ public final class BluetoothAdapter { /** * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on. + * * @hide */ public static final int STATE_BLE_TURNING_ON = 14; /** * Indicates the local Bluetooth adapter is in LE only mode. + * * @hide */ public static final int STATE_BLE_ON = 15; /** * Indicates the local Bluetooth adapter is turning off LE only mode. + * * @hide */ public static final int STATE_BLE_TURNING_OFF = 16; /** * Human-readable string helper for AdapterState + * * @hide */ public static String nameForState(@AdapterState int state) { - switch(state) { - case STATE_OFF: return "OFF"; - case STATE_TURNING_ON: return "TURNING_ON"; - case STATE_ON: return "ON"; - case STATE_TURNING_OFF: return "TURNING_OFF"; - case STATE_BLE_TURNING_ON: return "BLE_TURNING_ON"; - case STATE_BLE_ON: return "BLE_ON"; - case STATE_BLE_TURNING_OFF: return "BLE_TURNING_OFF"; - default: return "?!?!? (" + state + ")"; + switch (state) { + case STATE_OFF: + return "OFF"; + case STATE_TURNING_ON: + return "TURNING_ON"; + case STATE_ON: + return "ON"; + case STATE_TURNING_OFF: + return "TURNING_OFF"; + case STATE_BLE_TURNING_ON: + return "BLE_TURNING_ON"; + case STATE_BLE_ON: + return "BLE_ON"; + case STATE_BLE_TURNING_OFF: + return "BLE_TURNING_OFF"; + default: + return "?!?!? (" + state + ")"; } } @@ -346,7 +359,8 @@ public final class BluetoothAdapter { /** @hide */ @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE}) @Retention(RetentionPolicy.SOURCE) - public @interface ScanMode {} + public @interface ScanMode { + } /** * Indicates that both inquiry scan and page scan are disabled on the local @@ -439,7 +453,7 @@ public final class BluetoothAdapter { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; /** * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} @@ -447,7 +461,7 @@ public final class BluetoothAdapter { * This extra represents the current connection state. */ public static final String EXTRA_CONNECTION_STATE = - "android.bluetooth.adapter.extra.CONNECTION_STATE"; + "android.bluetooth.adapter.extra.CONNECTION_STATE"; /** * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} @@ -455,15 +469,16 @@ public final class BluetoothAdapter { * This extra represents the previous connection state. */ public static final String EXTRA_PREVIOUS_CONNECTION_STATE = - "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; + "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; /** * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. + * * @hide */ @SystemApi public static final String ACTION_BLE_STATE_CHANGED = - "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; + "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; /** * Intent used to broadcast the change in the Bluetooth address @@ -477,7 +492,7 @@ public final class BluetoothAdapter { * @hide */ public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = - "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; + "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; /** * Used as a String extra field in {@link @@ -487,7 +502,7 @@ public final class BluetoothAdapter { * @hide */ public static final String EXTRA_BLUETOOTH_ADDRESS = - "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; + "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be @@ -497,10 +512,11 @@ public final class BluetoothAdapter { * * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which * works in Bluetooth state STATE_ON + * * @hide */ public static final String ACTION_BLE_ACL_CONNECTED = - "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; + "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be @@ -510,17 +526,18 @@ public final class BluetoothAdapter { * * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which * works in Bluetooth state STATE_ON + * * @hide */ public static final String ACTION_BLE_ACL_DISCONNECTED = - "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; + "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + public static final int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = 1; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = 2; /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; @@ -529,14 +546,17 @@ public final class BluetoothAdapter { private final IBinder mToken; - /** When creating a ServerSocket using listenUsingRfcommOn() or - * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create - * a ServerSocket that auto assigns a channel number to the first - * bluetooth socket. - * The channel number assigned to this first Bluetooth Socket will - * be stored in the ServerSocket, and reused for subsequent Bluetooth - * sockets. - * @hide */ + /** + * When creating a ServerSocket using listenUsingRfcommOn() or + * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create + * a ServerSocket that auto assigns a channel number to the first + * bluetooth socket. + * The channel number assigned to this first Bluetooth Socket will + * be stored in the ServerSocket, and reused for subsequent Bluetooth + * sockets. + * + * @hide + */ public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2; @@ -555,7 +575,7 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = - new ReentrantReadWriteLock(); + new ReentrantReadWriteLock(); private final Object mLock = new Object(); private final Map mLeScanClients; @@ -566,8 +586,9 @@ public final class BluetoothAdapter { * could be extended to support more. This will always return the default * adapter. *

        - * @return the default local adapter, or null if Bluetooth is not supported - * on this hardware platform + * + * @return the default local adapter, or null if Bluetooth is not supported on this hardware + * platform */ public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { @@ -648,7 +669,7 @@ public final class BluetoothAdapter { */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { if (!getLeAccess()) return null; - synchronized(mLock) { + synchronized (mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); } @@ -663,22 +684,25 @@ public final class BluetoothAdapter { *

        * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is * supported on this device before calling this method. + * * @hide */ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { - if (!getLeAccess()) - return null; + if (!getLeAccess()) { + return null; + } - if (!isLePeriodicAdvertisingSupported()) - return null; + if (!isLePeriodicAdvertisingSupported()) { + return null; + } - synchronized (mLock) { - if (sPeriodicAdvertisingManager == null) { - sPeriodicAdvertisingManager = - new PeriodicAdvertisingManager(mManagerService); + synchronized (mLock) { + if (sPeriodicAdvertisingManager == null) { + sPeriodicAdvertisingManager = + new PeriodicAdvertisingManager(mManagerService); + } } - } - return sPeriodicAdvertisingManager; + return sPeriodicAdvertisingManager; } /** @@ -686,7 +710,7 @@ public final class BluetoothAdapter { */ public BluetoothLeScanner getBluetoothLeScanner() { if (!getLeAccess()) return null; - synchronized(mLock) { + synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); } @@ -725,9 +749,9 @@ public final class BluetoothAdapter { */ @SystemApi public boolean isLeEnabled() { - final int state = getLeState(); - if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); - return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); + final int state = getLeState(); + if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); + return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); } /** @@ -752,8 +776,7 @@ public final class BluetoothAdapter { * immediate problem that will prevent the QAdapter from being turned off - * such as the QAadapter already being turned off. * - * @return true to indicate success, or false on - * immediate error + * @return true to indicate success, or false on immediate error * @hide */ @SystemApi @@ -763,7 +786,7 @@ public final class BluetoothAdapter { int state = getLeState(); if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { String packageName = ActivityThread.currentPackageName(); - if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); + if (DBG) Log.d(TAG, "disableBLE(): de-registering " + packageName); try { mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { @@ -772,7 +795,7 @@ public final class BluetoothAdapter { return true; } - if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); + if (DBG) Log.d(TAG, "disableBLE(): Already disabled"); return false; } @@ -804,8 +827,7 @@ public final class BluetoothAdapter { * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * - * @return true to indicate Bluetooth LE will be available, or false on - * immediate error + * @return true to indicate Bluetooth LE will be available, or false on immediate error * @hide */ @SystemApi @@ -856,12 +878,19 @@ public final class BluetoothAdapter { // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); + || state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { + if (VDBG) { + Log.d(TAG, + "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); + } state = BluetoothAdapter.STATE_OFF; } - if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(state)); + if (VDBG) { + Log.d(TAG, + "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState( + state)); + } return state; } @@ -897,16 +926,16 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } - if (VDBG) Log.d(TAG,"getLeState() returning " + BluetoothAdapter.nameForState(state)); + if (VDBG) Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); return state; } boolean getLeAccess() { - if (getLeState() == STATE_ON) + if (getLeState() == STATE_ON) { return true; - - else if (getLeState() == STATE_BLE_ON) + } else if (getLeState() == STATE_BLE_ON) { return true; // TODO: FILTER SYSTEM APPS HERE <-- + } return false; } @@ -933,8 +962,7 @@ public final class BluetoothAdapter { * immediate problem that will prevent the adapter from being turned on - * such as Airplane mode, or the adapter is already turned on. * - * @return true to indicate adapter startup has begun, or false on - * immediate error + * @return true to indicate adapter startup has begun, or false on immediate error */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { @@ -944,7 +972,9 @@ public final class BluetoothAdapter { } try { return mManagerService.enable(ActivityThread.currentPackageName()); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -967,14 +997,15 @@ public final class BluetoothAdapter { * immediate problem that will prevent the adapter from being turned off - * such as the adapter already being turned off. * - * @return true to indicate adapter shutdown has begun, or false on - * immediate error + * @return true to indicate adapter shutdown has begun, or false on immediate error */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { return mManagerService.disable(ActivityThread.currentPackageName(), true); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -984,15 +1015,16 @@ public final class BluetoothAdapter { *

        Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} * permission * - * @return true to indicate adapter shutdown has begun, or false on - * immediate error + * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ public boolean disable(boolean persist) { try { return mManagerService.disable(ActivityThread.currentPackageName(), persist); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1006,7 +1038,9 @@ public final class BluetoothAdapter { public String getAddress() { try { return mManagerService.getAddress(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -1020,7 +1054,9 @@ public final class BluetoothAdapter { public String getName() { try { return mManagerService.getName(); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -1031,7 +1067,6 @@ public final class BluetoothAdapter { * permission * * @return true to indicate that the config file was successfully cleared - * * @hide */ public boolean factoryReset() { @@ -1082,7 +1117,7 @@ public final class BluetoothAdapter { * to get the updated value. * * @param name a valid Bluetooth name - * @return true if the name was set, false otherwise + * @return true if the name was set, false otherwise */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setName(String name) { @@ -1151,9 +1186,9 @@ public final class BluetoothAdapter { * instead. * * @param mode valid scan mode - * @param duration time in seconds to apply scan mode, only used for - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE} - * @return true if the scan mode was set, false otherwise + * @param duration time in seconds to apply scan mode, only used for {@link + * #SCAN_MODE_CONNECTABLE_DISCOVERABLE} + * @return true if the scan mode was set, false otherwise * @hide */ public boolean setScanMode(@ScanMode int mode, int duration) { @@ -1205,9 +1240,10 @@ public final class BluetoothAdapter { /** * Get the end time of the latest remote device discovery process. - * @return the latest time that the bluetooth adapter was/will be in discovery mode, - * in milliseconds since the epoch. - * This time can be in the future if {@link #startDiscovery()} has been called recently. + * + * @return the latest time that the bluetooth adapter was/will be in discovery mode, in + * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has + * been called recently. * @hide */ public long getDiscoveryEndMillis() { @@ -1517,13 +1553,13 @@ public final class BluetoothAdapter { * Return the record of {@link BluetoothActivityEnergyInfo} object that * has the activity and energy info. This can be used to ascertain what * the controller has been up to, since the last sample. - * @param updateType Type of info, cached vs refreshed. * - * @return a record with {@link BluetoothActivityEnergyInfo} or null if - * report is unavailable or unsupported - * @deprecated use the asynchronous - * {@link #requestControllerActivityEnergyInfo(ResultReceiver)} instead. + * @param updateType Type of info, cached vs refreshed. + * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or + * unsupported * @hide + * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)} + * instead. */ @Deprecated public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { @@ -1599,11 +1635,11 @@ public final class BluetoothAdapter { /** * Gets the currently supported profiles by the adapter. * - *

        This can be used to check whether a profile is supported before attempting + *

        This can be used to check whether a profile is supported before attempting * to connect to its respective proxy. * - * @return a list of integers indicating the ids of supported profiles as defined in - * {@link BluetoothProfile}. + * @return a list of integers indicating the ids of supported profiles as defined in {@link + * BluetoothProfile}. * @hide */ public List getSupportedProfiles() { @@ -1622,7 +1658,7 @@ public final class BluetoothAdapter { } } } catch (RemoteException e) { - Log.e(TAG, "getSupportedProfiles:", e); + Log.e(TAG, "getSupportedProfiles:", e); } return supportedProfiles; } @@ -1635,9 +1671,8 @@ public final class BluetoothAdapter { *

        Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED} * intent to get the connection state of the adapter. * - * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, - * {@link #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} - * + * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link + * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} * @hide */ public int getConnectionState() { @@ -1688,10 +1723,11 @@ public final class BluetoothAdapter { * connections from a listening {@link BluetoothServerSocket}. *

        Valid RFCOMM channels are in range 1 to 30. *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or channel in use. * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { @@ -1708,12 +1744,14 @@ public final class BluetoothAdapter { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} *

        To auto assign a channel without creating a SDP record use * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. + * * @param channel RFCOMM channel to listen on - * @param mitm enforce man-in-the-middle protection for authentication. - * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. + * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 + * connections. * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or channel in use. * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, @@ -1749,11 +1787,12 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. *

        Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. + * * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or channel in use. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) @@ -1780,11 +1819,12 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. *

        Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. + * * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or channel in use. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) @@ -1792,7 +1832,7 @@ public final class BluetoothAdapter { return createNewRfcommSocketAndRecord(name, uuid, false, false); } - /** + /** * Create a listening, encrypted, * RFCOMM Bluetooth socket with Service Record. *

        The link will be encrypted, but the link key is not required to be authenticated @@ -1818,11 +1858,12 @@ public final class BluetoothAdapter { *

        Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions, or channel in use. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or channel in use. * @hide */ public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord( @@ -1835,7 +1876,7 @@ public final class BluetoothAdapter { boolean auth, boolean encrypt) throws IOException { BluetoothServerSocket socket; socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, - encrypt, new ParcelUuid(uuid)); + encrypt, new ParcelUuid(uuid)); socket.setServiceName(name); int errno = socket.mSocket.bindListen(); if (errno != 0) { @@ -1850,16 +1891,17 @@ public final class BluetoothAdapter { /** * Construct an unencrypted, unauthenticated, RFCOMM server socket. * Call #accept to retrieve connections to this socket. + * * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); int errno = socket.mSocket.bindListen(); - if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno != 0) { @@ -1871,12 +1913,13 @@ public final class BluetoothAdapter { return socket; } - /** + /** * Construct an encrypted, RFCOMM server socket. * Call #accept to retrieve connections to this socket. + * * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) @@ -1884,7 +1927,7 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, true, port); int errno = socket.mSocket.bindListen(); - if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno < 0) { @@ -1899,9 +1942,10 @@ public final class BluetoothAdapter { /** * Construct a SCO server socket. * Call #accept to retrieve connections to this socket. + * * @return A SCO BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public static BluetoothServerSocket listenUsingScoOn() throws IOException { @@ -1921,12 +1965,14 @@ public final class BluetoothAdapter { * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * @param port the PSM to listen on - * @param mitm enforce man-in-the-middle protection for authentication. - * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 connections. + * + * @param port the PSM to listen on + * @param mitm enforce man-in-the-middle protection for authentication. + * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 + * connections. * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) @@ -1934,7 +1980,7 @@ public final class BluetoothAdapter { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin); int errno = socket.mSocket.bindListen(); - if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno != 0) { @@ -1951,10 +1997,11 @@ public final class BluetoothAdapter { * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * @param port the PSM to listen on + * + * @param port the PSM to listen on * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { @@ -1967,17 +2014,18 @@ public final class BluetoothAdapter { * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * @param port the PSM to listen on + * + * @param port the PSM to listen on * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_L2CAP, false, false, port, false, false); int errno = socket.mSocket.bindListen(); - if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); } if (errno != 0) { @@ -1995,7 +2043,6 @@ public final class BluetoothAdapter { *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Pair of Hash and Randomizer - * * @hide */ public Pair readOutOfBandData() { @@ -2013,13 +2060,13 @@ public final class BluetoothAdapter { * * @param context Context of the application * @param listener The service Listener for connection callbacks. - * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, - * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. - * {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}. + * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link + * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or + * {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, - int profile) { + int profile) { if (context == null || listener == null) return false; if (profile == BluetoothProfile.HEADSET) { @@ -2082,59 +2129,59 @@ public final class BluetoothAdapter { switch (profile) { case BluetoothProfile.HEADSET: - BluetoothHeadset headset = (BluetoothHeadset)proxy; + BluetoothHeadset headset = (BluetoothHeadset) proxy; headset.close(); break; case BluetoothProfile.A2DP: - BluetoothA2dp a2dp = (BluetoothA2dp)proxy; + BluetoothA2dp a2dp = (BluetoothA2dp) proxy; a2dp.close(); break; case BluetoothProfile.A2DP_SINK: - BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink)proxy; + BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink) proxy; a2dpSink.close(); break; case BluetoothProfile.AVRCP_CONTROLLER: - BluetoothAvrcpController avrcp = (BluetoothAvrcpController)proxy; + BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy; avrcp.close(); break; case BluetoothProfile.INPUT_DEVICE: - BluetoothInputDevice iDev = (BluetoothInputDevice)proxy; + BluetoothInputDevice iDev = (BluetoothInputDevice) proxy; iDev.close(); break; case BluetoothProfile.PAN: - BluetoothPan pan = (BluetoothPan)proxy; + BluetoothPan pan = (BluetoothPan) proxy; pan.close(); break; case BluetoothProfile.HEALTH: - BluetoothHealth health = (BluetoothHealth)proxy; + BluetoothHealth health = (BluetoothHealth) proxy; health.close(); break; - case BluetoothProfile.GATT: - BluetoothGatt gatt = (BluetoothGatt)proxy; + case BluetoothProfile.GATT: + BluetoothGatt gatt = (BluetoothGatt) proxy; gatt.close(); break; case BluetoothProfile.GATT_SERVER: - BluetoothGattServer gattServer = (BluetoothGattServer)proxy; + BluetoothGattServer gattServer = (BluetoothGattServer) proxy; gattServer.close(); break; case BluetoothProfile.MAP: - BluetoothMap map = (BluetoothMap)proxy; + BluetoothMap map = (BluetoothMap) proxy; map.close(); break; case BluetoothProfile.HEADSET_CLIENT: - BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy; + BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient) proxy; headsetClient.close(); break; case BluetoothProfile.SAP: - BluetoothSap sap = (BluetoothSap)proxy; + BluetoothSap sap = (BluetoothSap) proxy; sap.close(); break; case BluetoothProfile.PBAP_CLIENT: - BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy; + BluetoothPbapClient pbapClient = (BluetoothPbapClient) proxy; pbapClient.close(); break; case BluetoothProfile.MAP_CLIENT: - BluetoothMapClient mapClient = (BluetoothMapClient)proxy; + BluetoothMapClient mapClient = (BluetoothMapClient) proxy; mapClient.close(); break; case BluetoothProfile.INPUT_HOST: @@ -2145,75 +2192,78 @@ public final class BluetoothAdapter { } final private IBluetoothManagerCallback mManagerCallback = - new IBluetoothManagerCallback.Stub() { - public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); - - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ) { - try { - if (cb != null) { - cb.onBluetoothServiceUp(bluetoothService); - } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!"); + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + + mServiceLock.writeLock().lock(); + mService = bluetoothService; + mServiceLock.writeLock().unlock(); + + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + try { + if (cb != null) { + cb.onBluetoothServiceUp(bluetoothService); + } else { + Log.d(TAG, "onBluetoothServiceUp: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); } - } catch (Exception e) { - Log.e(TAG,"",e); } } } - } - - public void onBluetoothServiceDown() { - if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); - try { - mServiceLock.writeLock().lock(); - mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); - } finally { - mServiceLock.writeLock().unlock(); - } + public void onBluetoothServiceDown() { + if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + + try { + mServiceLock.writeLock().lock(); + mService = null; + if (mLeScanClients != null) mLeScanClients.clear(); + if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); + if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + } finally { + mServiceLock.writeLock().unlock(); + } - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + synchronized (mProxyServiceStateCallbacks) { + for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); } - } catch (Exception e) { - Log.e(TAG,"",e); } } } - } - public void onBrEdrDown() { - if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService); - } - }; + public void onBrEdrDown() { + if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService); + } + }; /** * Enable the Bluetooth Adapter, but don't auto-connect devices * and don't persist state. Only for use by system applications. + * * @hide */ public boolean enableNoAutoConnect() { - if (isEnabled() == true){ + if (isEnabled() == true) { if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); return true; } try { return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -2247,7 +2297,7 @@ public final class BluetoothAdapter { * @hide */ public boolean changeApplicationBluetoothState(boolean on, - BluetoothStateChangeCallback callback) { + BluetoothStateChangeCallback callback) { return false; } @@ -2305,28 +2355,29 @@ public final class BluetoothAdapter { for (int i = 0; i < ADDRESS_LENGTH; i++) { char c = address.charAt(i); switch (i % 3) { - case 0: - case 1: - if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { - // hex character, OK - break; - } - return false; - case 2: - if (c == ':') { - break; // OK - } - return false; + case 0: + case 1: + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { + // hex character, OK + break; + } + return false; + case 2: + if (c == ':') { + break; // OK + } + return false; } } return true; } /*package*/ IBluetoothManager getBluetoothManager() { - return mManagerService; + return mManagerService; } - final private ArrayList mProxyServiceStateCallbacks = new ArrayList(); + final private ArrayList mProxyServiceStateCallbacks = + new ArrayList(); /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { synchronized (mProxyServiceStateCallbacks) { @@ -2357,10 +2408,9 @@ public final class BluetoothAdapter { * by the {@link BluetoothAdapter#startLeScan} function. * * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. + * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0 + * if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by the remote device. */ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); } @@ -2374,7 +2424,7 @@ public final class BluetoothAdapter { * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} - * instead. + * instead. */ @Deprecated @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @@ -2393,7 +2443,7 @@ public final class BluetoothAdapter { * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} - * instead. + * instead. */ @Deprecated @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @@ -2409,7 +2459,7 @@ public final class BluetoothAdapter { return false; } - synchronized(mLeScanClients) { + synchronized (mLeScanClients) { if (mLeScanClients.containsKey(callback)) { if (DBG) Log.e(TAG, "LE Scan has already started"); return false; @@ -2450,8 +2500,8 @@ public final class BluetoothAdapter { } }; ScanSettings settings = new ScanSettings.Builder() - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); List filters = new ArrayList(); if (serviceUuids != null && serviceUuids.length > 0) { @@ -2467,7 +2517,7 @@ public final class BluetoothAdapter { return true; } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } return false; @@ -2476,8 +2526,8 @@ public final class BluetoothAdapter { /** * Stops an ongoing Bluetooth LE device scan. * - * @param callback used to identify which scan to stop - * must be the same handle used to start the scan + * @param callback used to identify which scan to stop must be the same handle used to start the + * scan * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ @Deprecated diff --git a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java index 124bdc118d4..41a34e06184 100644 --- a/framework/java/android/bluetooth/BluetoothAssignedNumbers.java +++ b/framework/java/android/bluetooth/BluetoothAssignedNumbers.java @@ -20,9 +20,9 @@ package android.bluetooth; * Bluetooth Assigned Numbers. *

        * For now we only include Company ID values. - * @see - * The Official Bluetooth SIG Member Website | Company Identifiers * + * @see The Official + * Bluetooth SIG Member Website | Company Identifiers */ public class BluetoothAssignedNumbers { diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java index 03176b9e073..238bf842b73 100644 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.java +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java @@ -41,7 +41,7 @@ public final class BluetoothAudioConfig implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothAudioConfig) { - BluetoothAudioConfig bac = (BluetoothAudioConfig)o; + BluetoothAudioConfig bac = (BluetoothAudioConfig) o; return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig && bac.mAudioFormat == mAudioFormat); @@ -66,16 +66,17 @@ public final class BluetoothAudioConfig implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothAudioConfig createFromParcel(Parcel in) { - int sampleRate = in.readInt(); - int channelConfig = in.readInt(); - int audioFormat = in.readInt(); - return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat); - } - public BluetoothAudioConfig[] newArray(int size) { - return new BluetoothAudioConfig[size]; - } - }; + public BluetoothAudioConfig createFromParcel(Parcel in) { + int sampleRate = in.readInt(); + int channelConfig = in.readInt(); + int audioFormat = in.readInt(); + return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat); + } + + public BluetoothAudioConfig[] newArray(int size) { + return new BluetoothAudioConfig[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeInt(mSampleRate); @@ -85,6 +86,7 @@ public final class BluetoothAudioConfig implements Parcelable { /** * Returns the sample rate in samples per second + * * @return sample rate */ public int getSampleRate() { @@ -94,6 +96,7 @@ public final class BluetoothAudioConfig implements Parcelable { /** * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO} * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO}) + * * @return channel configuration */ public int getChannelConfig() { @@ -103,6 +106,7 @@ public final class BluetoothAudioConfig implements Parcelable { /** * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT} * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT} + * * @return audio format */ public int getAudioFormat() { diff --git a/framework/java/android/bluetooth/BluetoothAvrcp.java b/framework/java/android/bluetooth/BluetoothAvrcp.java index 44fe1b73712..1a4c7590648 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcp.java +++ b/framework/java/android/bluetooth/BluetoothAvrcp.java @@ -26,68 +26,68 @@ public final class BluetoothAvrcp { /* * State flags for Passthrough commands */ - public static final int PASSTHROUGH_STATE_PRESS = 0; - public static final int PASSTHROUGH_STATE_RELEASE = 1; + public static final int PASSTHROUGH_STATE_PRESS = 0; + public static final int PASSTHROUGH_STATE_RELEASE = 1; /* * Operation IDs for Passthrough commands */ - public static final int PASSTHROUGH_ID_SELECT = 0x00; /* select */ - public static final int PASSTHROUGH_ID_UP = 0x01; /* up */ - public static final int PASSTHROUGH_ID_DOWN = 0x02; /* down */ - public static final int PASSTHROUGH_ID_LEFT = 0x03; /* left */ - public static final int PASSTHROUGH_ID_RIGHT = 0x04; /* right */ - public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05; /* right-up */ - public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06; /* right-down */ - public static final int PASSTHROUGH_ID_LEFT_UP = 0x07; /* left-up */ - public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08; /* left-down */ - public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09; /* root menu */ - public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A; /* setup menu */ - public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B; /* contents menu */ - public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C; /* favorite menu */ - public static final int PASSTHROUGH_ID_EXIT = 0x0D; /* exit */ - public static final int PASSTHROUGH_ID_0 = 0x20; /* 0 */ - public static final int PASSTHROUGH_ID_1 = 0x21; /* 1 */ - public static final int PASSTHROUGH_ID_2 = 0x22; /* 2 */ - public static final int PASSTHROUGH_ID_3 = 0x23; /* 3 */ - public static final int PASSTHROUGH_ID_4 = 0x24; /* 4 */ - public static final int PASSTHROUGH_ID_5 = 0x25; /* 5 */ - public static final int PASSTHROUGH_ID_6 = 0x26; /* 6 */ - public static final int PASSTHROUGH_ID_7 = 0x27; /* 7 */ - public static final int PASSTHROUGH_ID_8 = 0x28; /* 8 */ - public static final int PASSTHROUGH_ID_9 = 0x29; /* 9 */ - public static final int PASSTHROUGH_ID_DOT = 0x2A; /* dot */ - public static final int PASSTHROUGH_ID_ENTER = 0x2B; /* enter */ - public static final int PASSTHROUGH_ID_CLEAR = 0x2C; /* clear */ - public static final int PASSTHROUGH_ID_CHAN_UP = 0x30; /* channel up */ - public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31; /* channel down */ - public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32; /* previous channel */ - public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33; /* sound select */ - public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34; /* input select */ - public static final int PASSTHROUGH_ID_DISP_INFO = 0x35; /* display information */ - public static final int PASSTHROUGH_ID_HELP = 0x36; /* help */ - public static final int PASSTHROUGH_ID_PAGE_UP = 0x37; /* page up */ - public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38; /* page down */ - public static final int PASSTHROUGH_ID_POWER = 0x40; /* power */ - public static final int PASSTHROUGH_ID_VOL_UP = 0x41; /* volume up */ - public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42; /* volume down */ - public static final int PASSTHROUGH_ID_MUTE = 0x43; /* mute */ - public static final int PASSTHROUGH_ID_PLAY = 0x44; /* play */ - public static final int PASSTHROUGH_ID_STOP = 0x45; /* stop */ - public static final int PASSTHROUGH_ID_PAUSE = 0x46; /* pause */ - public static final int PASSTHROUGH_ID_RECORD = 0x47; /* record */ - public static final int PASSTHROUGH_ID_REWIND = 0x48; /* rewind */ - public static final int PASSTHROUGH_ID_FAST_FOR = 0x49; /* fast forward */ - public static final int PASSTHROUGH_ID_EJECT = 0x4A; /* eject */ - public static final int PASSTHROUGH_ID_FORWARD = 0x4B; /* forward */ - public static final int PASSTHROUGH_ID_BACKWARD = 0x4C; /* backward */ - public static final int PASSTHROUGH_ID_ANGLE = 0x50; /* angle */ - public static final int PASSTHROUGH_ID_SUBPICT = 0x51; /* subpicture */ - public static final int PASSTHROUGH_ID_F1 = 0x71; /* F1 */ - public static final int PASSTHROUGH_ID_F2 = 0x72; /* F2 */ - public static final int PASSTHROUGH_ID_F3 = 0x73; /* F3 */ - public static final int PASSTHROUGH_ID_F4 = 0x74; /* F4 */ - public static final int PASSTHROUGH_ID_F5 = 0x75; /* F5 */ - public static final int PASSTHROUGH_ID_VENDOR = 0x7E; /* vendor unique */ + public static final int PASSTHROUGH_ID_SELECT = 0x00; /* select */ + public static final int PASSTHROUGH_ID_UP = 0x01; /* up */ + public static final int PASSTHROUGH_ID_DOWN = 0x02; /* down */ + public static final int PASSTHROUGH_ID_LEFT = 0x03; /* left */ + public static final int PASSTHROUGH_ID_RIGHT = 0x04; /* right */ + public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05; /* right-up */ + public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06; /* right-down */ + public static final int PASSTHROUGH_ID_LEFT_UP = 0x07; /* left-up */ + public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08; /* left-down */ + public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09; /* root menu */ + public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A; /* setup menu */ + public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B; /* contents menu */ + public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C; /* favorite menu */ + public static final int PASSTHROUGH_ID_EXIT = 0x0D; /* exit */ + public static final int PASSTHROUGH_ID_0 = 0x20; /* 0 */ + public static final int PASSTHROUGH_ID_1 = 0x21; /* 1 */ + public static final int PASSTHROUGH_ID_2 = 0x22; /* 2 */ + public static final int PASSTHROUGH_ID_3 = 0x23; /* 3 */ + public static final int PASSTHROUGH_ID_4 = 0x24; /* 4 */ + public static final int PASSTHROUGH_ID_5 = 0x25; /* 5 */ + public static final int PASSTHROUGH_ID_6 = 0x26; /* 6 */ + public static final int PASSTHROUGH_ID_7 = 0x27; /* 7 */ + public static final int PASSTHROUGH_ID_8 = 0x28; /* 8 */ + public static final int PASSTHROUGH_ID_9 = 0x29; /* 9 */ + public static final int PASSTHROUGH_ID_DOT = 0x2A; /* dot */ + public static final int PASSTHROUGH_ID_ENTER = 0x2B; /* enter */ + public static final int PASSTHROUGH_ID_CLEAR = 0x2C; /* clear */ + public static final int PASSTHROUGH_ID_CHAN_UP = 0x30; /* channel up */ + public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31; /* channel down */ + public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32; /* previous channel */ + public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33; /* sound select */ + public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34; /* input select */ + public static final int PASSTHROUGH_ID_DISP_INFO = 0x35; /* display information */ + public static final int PASSTHROUGH_ID_HELP = 0x36; /* help */ + public static final int PASSTHROUGH_ID_PAGE_UP = 0x37; /* page up */ + public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38; /* page down */ + public static final int PASSTHROUGH_ID_POWER = 0x40; /* power */ + public static final int PASSTHROUGH_ID_VOL_UP = 0x41; /* volume up */ + public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42; /* volume down */ + public static final int PASSTHROUGH_ID_MUTE = 0x43; /* mute */ + public static final int PASSTHROUGH_ID_PLAY = 0x44; /* play */ + public static final int PASSTHROUGH_ID_STOP = 0x45; /* stop */ + public static final int PASSTHROUGH_ID_PAUSE = 0x46; /* pause */ + public static final int PASSTHROUGH_ID_RECORD = 0x47; /* record */ + public static final int PASSTHROUGH_ID_REWIND = 0x48; /* rewind */ + public static final int PASSTHROUGH_ID_FAST_FOR = 0x49; /* fast forward */ + public static final int PASSTHROUGH_ID_EJECT = 0x4A; /* eject */ + public static final int PASSTHROUGH_ID_FORWARD = 0x4B; /* forward */ + public static final int PASSTHROUGH_ID_BACKWARD = 0x4C; /* backward */ + public static final int PASSTHROUGH_ID_ANGLE = 0x50; /* angle */ + public static final int PASSTHROUGH_ID_SUBPICT = 0x51; /* subpicture */ + public static final int PASSTHROUGH_ID_F1 = 0x71; /* F1 */ + public static final int PASSTHROUGH_ID_F2 = 0x72; /* F2 */ + public static final int PASSTHROUGH_ID_F3 = 0x73; /* F3 */ + public static final int PASSTHROUGH_ID_F4 = 0x74; /* F4 */ + public static final int PASSTHROUGH_ID_F5 = 0x75; /* F5 */ + public static final int PASSTHROUGH_ID_VENDOR = 0x7E; /* vendor unique */ public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80; } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 0261b1b35e2..1a0ae149f1c 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -20,8 +20,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.media.MediaMetadata; -import android.media.session.PlaybackState; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -34,7 +32,7 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently * supports player information, playback support and track metadata. * - *

        BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP + *

        BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothAvrcpController proxy object. * @@ -51,9 +49,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -64,19 +62,19 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * receive. */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent used to broadcast the change in player application setting state on AVRCP AG. * *

        This intent will have the following extras: *

          - *
        • {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the - * most recent player setting.
        • + *
        • {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the + * most recent player setting.
        • *
        */ public static final String ACTION_PLAYER_SETTING = - "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; + "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; @@ -87,38 +85,37 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private BluetoothAdapter mAdapter; 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); + 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(); + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + doBind(); + } + } catch (Exception re) { + Log.e(TAG, "", re); } - } catch (Exception re) { - Log.e(TAG,"",re); } } } - } - }; + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. - * */ /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { mContext = context; @@ -129,7 +126,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -155,7 +152,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -165,7 +162,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -215,7 +212,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -269,7 +266,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * possible keycode values: next_grp, previous_grp defined above */ public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { - Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); + Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + + keyState); if (mService != null && isEnabled()) { try { mService.sendGroupNavigationCmd(device, keyCode, keyState); @@ -292,6 +290,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { BluetoothAvrcpController.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; @@ -302,18 +301,18 @@ public final class BluetoothAvrcpController implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java index 927cb566651..036d36d0e5d 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java @@ -34,22 +34,22 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { /** * Equalizer setting. */ - public static final int SETTING_EQUALIZER = 0x01; + public static final int SETTING_EQUALIZER = 0x01; /** * Repeat setting. */ - public static final int SETTING_REPEAT = 0x02; + public static final int SETTING_REPEAT = 0x02; /** * Shuffle setting. */ - public static final int SETTING_SHUFFLE = 0x04; + public static final int SETTING_SHUFFLE = 0x04; /** * Scan mode setting. */ - public static final int SETTING_SCAN = 0x08; + public static final int SETTING_SCAN = 0x08; /** * Invalid state. @@ -84,14 +84,14 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { * * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. */ - public static final int STATE_ALL_TRACK = 0x03; + public static final int STATE_ALL_TRACK = 0x03; /** * Group repeat/shuffle. * * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. */ - public static final int STATE_GROUP = 0x04; + public static final int STATE_GROUP = 0x04; /** * List of supported settings ORed. @@ -157,6 +157,7 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { * Add a setting value. * * The setting must be part of possible settings in {@link getSettings()}. + * * @param setting setting config. * @param value value for the setting. * @throws IllegalStateException if the setting is not supported. @@ -173,6 +174,7 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { * Get a setting value. * * The setting must be part of possible settings in {@link getSettings()}. + * * @param setting setting config. * @return value value for the setting. * @throws IllegalStateException if the setting is not supported. diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 4a38287e7ca..2f6a79352b3 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -51,6 +51,7 @@ import android.os.Parcelable; public final class BluetoothClass implements Parcelable { /** * Legacy error value. Applications should use null instead. + * * @hide */ public static final int ERROR = 0xFF000000; @@ -65,7 +66,7 @@ public final class BluetoothClass implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothClass) { - return mClass == ((BluetoothClass)o).mClass; + return mClass == ((BluetoothClass) o).mClass; } return false; } @@ -86,13 +87,14 @@ public final class BluetoothClass implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothClass createFromParcel(Parcel in) { - return new BluetoothClass(in.readInt()); - } - public BluetoothClass[] newArray(int size) { - return new BluetoothClass[size]; - } - }; + public BluetoothClass createFromParcel(Parcel in) { + return new BluetoothClass(in.readInt()); + } + + public BluetoothClass[] newArray(int size) { + return new BluetoothClass[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeInt(mClass); @@ -103,17 +105,17 @@ public final class BluetoothClass implements Parcelable { *

        Each {@link BluetoothClass} encodes zero or more service classes. */ public static final class Service { - private static final int BITMASK = 0xFFE000; + private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; - public static final int POSITIONING = 0x010000; - public static final int NETWORKING = 0x020000; - public static final int RENDER = 0x040000; - public static final int CAPTURE = 0x080000; - public static final int OBJECT_TRANSFER = 0x100000; - public static final int AUDIO = 0x200000; - public static final int TELEPHONY = 0x400000; - public static final int INFORMATION = 0x800000; + public static final int POSITIONING = 0x010000; + public static final int NETWORKING = 0x020000; + public static final int RENDER = 0x040000; + public static final int CAPTURE = 0x080000; + public static final int OBJECT_TRANSFER = 0x100000; + public static final int AUDIO = 0x200000; + public static final int TELEPHONY = 0x400000; + public static final int INFORMATION = 0x800000; } /** @@ -141,91 +143,91 @@ public final class BluetoothClass implements Parcelable { *

        See {@link BluetoothClass.Service} for service class constants. */ public static class Device { - private static final int BITMASK = 0x1FFC; + private static final int BITMASK = 0x1FFC; /** * Defines all major device class constants. *

        See {@link BluetoothClass.Device} for minor classes. */ public static class Major { - private static final int BITMASK = 0x1F00; + private static final int BITMASK = 0x1F00; - public static final int MISC = 0x0000; - public static final int COMPUTER = 0x0100; - public static final int PHONE = 0x0200; - public static final int NETWORKING = 0x0300; - public static final int AUDIO_VIDEO = 0x0400; - public static final int PERIPHERAL = 0x0500; - public static final int IMAGING = 0x0600; - public static final int WEARABLE = 0x0700; - public static final int TOY = 0x0800; - public static final int HEALTH = 0x0900; - public static final int UNCATEGORIZED = 0x1F00; + public static final int MISC = 0x0000; + public static final int COMPUTER = 0x0100; + public static final int PHONE = 0x0200; + public static final int NETWORKING = 0x0300; + public static final int AUDIO_VIDEO = 0x0400; + public static final int PERIPHERAL = 0x0500; + public static final int IMAGING = 0x0600; + public static final int WEARABLE = 0x0700; + public static final int TOY = 0x0800; + public static final int HEALTH = 0x0900; + public static final int UNCATEGORIZED = 0x1F00; } // Devices in the COMPUTER major class - public static final int COMPUTER_UNCATEGORIZED = 0x0100; - public static final int COMPUTER_DESKTOP = 0x0104; - public static final int COMPUTER_SERVER = 0x0108; - public static final int COMPUTER_LAPTOP = 0x010C; - public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; - public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; - public static final int COMPUTER_WEARABLE = 0x0118; + public static final int COMPUTER_UNCATEGORIZED = 0x0100; + public static final int COMPUTER_DESKTOP = 0x0104; + public static final int COMPUTER_SERVER = 0x0108; + public static final int COMPUTER_LAPTOP = 0x010C; + public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; + public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; + public static final int COMPUTER_WEARABLE = 0x0118; // Devices in the PHONE major class - public static final int PHONE_UNCATEGORIZED = 0x0200; - public static final int PHONE_CELLULAR = 0x0204; - public static final int PHONE_CORDLESS = 0x0208; - public static final int PHONE_SMART = 0x020C; - public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; - public static final int PHONE_ISDN = 0x0214; + public static final int PHONE_UNCATEGORIZED = 0x0200; + public static final int PHONE_CELLULAR = 0x0204; + public static final int PHONE_CORDLESS = 0x0208; + public static final int PHONE_SMART = 0x020C; + public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; + public static final int PHONE_ISDN = 0x0214; // Minor classes for the AUDIO_VIDEO major class - public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; - public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; - public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; + public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; + public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; + public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; //public static final int AUDIO_VIDEO_RESERVED = 0x040C; - public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; - public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; - public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; - public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; - public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; - public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; - public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; - public static final int AUDIO_VIDEO_VCR = 0x042C; - public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; - public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; - public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; + public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; + public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; + public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; + public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; + public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; + public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; + public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; + public static final int AUDIO_VIDEO_VCR = 0x042C; + public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; + public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; + public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; - public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; + public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; //public static final int AUDIO_VIDEO_RESERVED = 0x0444; - public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; + public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; // Devices in the WEARABLE major class - public static final int WEARABLE_UNCATEGORIZED = 0x0700; - public static final int WEARABLE_WRIST_WATCH = 0x0704; - public static final int WEARABLE_PAGER = 0x0708; - public static final int WEARABLE_JACKET = 0x070C; - public static final int WEARABLE_HELMET = 0x0710; - public static final int WEARABLE_GLASSES = 0x0714; + public static final int WEARABLE_UNCATEGORIZED = 0x0700; + public static final int WEARABLE_WRIST_WATCH = 0x0704; + public static final int WEARABLE_PAGER = 0x0708; + public static final int WEARABLE_JACKET = 0x070C; + public static final int WEARABLE_HELMET = 0x0710; + public static final int WEARABLE_GLASSES = 0x0714; // Devices in the TOY major class - public static final int TOY_UNCATEGORIZED = 0x0800; - public static final int TOY_ROBOT = 0x0804; - public static final int TOY_VEHICLE = 0x0808; - public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; - public static final int TOY_CONTROLLER = 0x0810; - public static final int TOY_GAME = 0x0814; + public static final int TOY_UNCATEGORIZED = 0x0800; + public static final int TOY_ROBOT = 0x0804; + public static final int TOY_VEHICLE = 0x0808; + public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; + public static final int TOY_CONTROLLER = 0x0810; + public static final int TOY_GAME = 0x0814; // Devices in the HEALTH major class - public static final int HEALTH_UNCATEGORIZED = 0x0900; - public static final int HEALTH_BLOOD_PRESSURE = 0x0904; - public static final int HEALTH_THERMOMETER = 0x0908; - public static final int HEALTH_WEIGHING = 0x090C; - public static final int HEALTH_GLUCOSE = 0x0910; - public static final int HEALTH_PULSE_OXIMETER = 0x0914; - public static final int HEALTH_PULSE_RATE = 0x0918; - public static final int HEALTH_DATA_DISPLAY = 0x091C; + public static final int HEALTH_UNCATEGORIZED = 0x0900; + public static final int HEALTH_BLOOD_PRESSURE = 0x0904; + public static final int HEALTH_THERMOMETER = 0x0908; + public static final int HEALTH_WEIGHING = 0x090C; + public static final int HEALTH_GLUCOSE = 0x0910; + public static final int HEALTH_PULSE_OXIMETER = 0x0914; + public static final int HEALTH_PULSE_RATE = 0x0918; + public static final int HEALTH_DATA_DISPLAY = 0x091C; // Devices in PERIPHERAL major class /** @@ -235,15 +237,15 @@ public final class BluetoothClass implements Parcelable { /** * @hide */ - public static final int PERIPHERAL_KEYBOARD = 0x0540; + public static final int PERIPHERAL_KEYBOARD = 0x0540; /** * @hide */ - public static final int PERIPHERAL_POINTING = 0x0580; + public static final int PERIPHERAL_POINTING = 0x0580; /** * @hide */ - public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0; + public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0; } /** @@ -291,6 +293,7 @@ public final class BluetoothClass implements Parcelable { * This is a simple heuristic that tries to guess if a device with the * given class bits might support specified profile. It is not accurate for all * devices. It tries to err on the side of false positives. + * * @param profile The profile to be checked * @return True if this device might support specified profile. * @hide @@ -322,7 +325,7 @@ public final class BluetoothClass implements Parcelable { switch (getDeviceClass()) { case Device.AUDIO_VIDEO_HIFI_AUDIO: case Device.AUDIO_VIDEO_SET_TOP_BOX: - case Device.AUDIO_VIDEO_VCR : + case Device.AUDIO_VIDEO_VCR: return true; default: return false; @@ -367,7 +370,7 @@ public final class BluetoothClass implements Parcelable { } } else if (profile == PROFILE_HID) { return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL; - } else if (profile == PROFILE_PANU || profile == PROFILE_NAP){ + } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) { // No good way to distinguish between the two, based on class bits. if (hasService(Service.NETWORKING)) { return true; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index d5e14298101..1e463f7baf6 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -32,12 +32,12 @@ public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h - public static final int SOURCE_CODEC_TYPE_SBC = 0; - public static final int SOURCE_CODEC_TYPE_AAC = 1; - public static final int SOURCE_CODEC_TYPE_APTX = 2; + public static final int SOURCE_CODEC_TYPE_SBC = 0; + public static final int SOURCE_CODEC_TYPE_AAC = 1; + public static final int SOURCE_CODEC_TYPE_APTX = 2; public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - public static final int SOURCE_CODEC_TYPE_LDAC = 4; - public static final int SOURCE_CODEC_TYPE_MAX = 5; + public static final int SOURCE_CODEC_TYPE_LDAC = 4; + public static final int SOURCE_CODEC_TYPE_MAX = 5; public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; @@ -45,21 +45,21 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int CODEC_PRIORITY_DEFAULT = 0; public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - public static final int SAMPLE_RATE_NONE = 0; - public static final int SAMPLE_RATE_44100 = 0x1 << 0; - public static final int SAMPLE_RATE_48000 = 0x1 << 1; - public static final int SAMPLE_RATE_88200 = 0x1 << 2; - public static final int SAMPLE_RATE_96000 = 0x1 << 3; + public static final int SAMPLE_RATE_NONE = 0; + public static final int SAMPLE_RATE_44100 = 0x1 << 0; + public static final int SAMPLE_RATE_48000 = 0x1 << 1; + public static final int SAMPLE_RATE_88200 = 0x1 << 2; + public static final int SAMPLE_RATE_96000 = 0x1 << 3; public static final int SAMPLE_RATE_176400 = 0x1 << 4; public static final int SAMPLE_RATE_192000 = 0x1 << 5; public static final int BITS_PER_SAMPLE_NONE = 0; - public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; + public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - public static final int CHANNEL_MODE_NONE = 0; - public static final int CHANNEL_MODE_MONO = 0x1 << 0; + public static final int CHANNEL_MODE_NONE = 0; + public static final int CHANNEL_MODE_MONO = 0x1 << 0; public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final int mCodecType; @@ -73,10 +73,10 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific4; public BluetoothCodecConfig(int codecType, int codecPriority, - int sampleRate, int bitsPerSample, - int channelMode, long codecSpecific1, - long codecSpecific2, long codecSpecific3, - long codecSpecific4) { + int sampleRate, int bitsPerSample, + int channelMode, long codecSpecific1, + long codecSpecific2, long codecSpecific3, + long codecSpecific4) { mCodecType = codecType; mCodecPriority = codecPriority; mSampleRate = sampleRate; @@ -91,7 +91,7 @@ public final class BluetoothCodecConfig implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothCodecConfig) { - BluetoothCodecConfig other = (BluetoothCodecConfig)o; + BluetoothCodecConfig other = (BluetoothCodecConfig) o; return (other.mCodecType == mCodecType && other.mCodecPriority == mCodecPriority && other.mSampleRate == mSampleRate && @@ -108,32 +108,30 @@ public final class BluetoothCodecConfig implements Parcelable { @Override public int hashCode() { return Objects.hash(mCodecType, mCodecPriority, mSampleRate, - mBitsPerSample, mChannelMode, mCodecSpecific1, - mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); + mBitsPerSample, mChannelMode, mCodecSpecific1, + mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); } /** * Checks whether the object contains valid codec configuration. * - * @return true if the object contains valid codec configuration, - * otherwise false. + * @return true if the object contains valid codec configuration, otherwise false. */ public boolean isValid() { return (mSampleRate != SAMPLE_RATE_NONE) && - (mBitsPerSample != BITS_PER_SAMPLE_NONE) && - (mChannelMode != CHANNEL_MODE_NONE); + (mBitsPerSample != BITS_PER_SAMPLE_NONE) && + (mChannelMode != CHANNEL_MODE_NONE); } /** * Adds capability string to an existing string. * - * @param prevStr the previous string with the capabilities. Can be - * a null pointer. + * @param prevStr the previous string with the capabilities. Can be a null pointer. * @param capStr the capability string to append to prevStr argument. * @return the result string in the form "prevStr|capStr". */ private static String appendCapabilityToString(String prevStr, - String capStr) { + String capStr) { if (prevStr == null) { return capStr; } @@ -191,18 +189,18 @@ public final class BluetoothCodecConfig implements Parcelable { } return "{codecName:" + getCodecName() + - ",mCodecType:" + mCodecType + - ",mCodecPriority:" + mCodecPriority + - ",mSampleRate:" + String.format("0x%x", mSampleRate) + - "(" + sampleRateStr + ")" + - ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + - "(" + bitsPerSampleStr + ")" + - ",mChannelMode:" + String.format("0x%x", mChannelMode) + - "(" + channelModeStr + ")" + - ",mCodecSpecific1:" + mCodecSpecific1 + - ",mCodecSpecific2:" + mCodecSpecific2 + - ",mCodecSpecific3:" + mCodecSpecific3 + - ",mCodecSpecific4:" + mCodecSpecific4 + "}"; + ",mCodecType:" + mCodecType + + ",mCodecPriority:" + mCodecPriority + + ",mSampleRate:" + String.format("0x%x", mSampleRate) + + "(" + sampleRateStr + ")" + + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + + "(" + bitsPerSampleStr + ")" + + ",mChannelMode:" + String.format("0x%x", mChannelMode) + + "(" + channelModeStr + ")" + + ",mCodecSpecific1:" + mCodecSpecific1 + + ",mCodecSpecific2:" + mCodecSpecific2 + + ",mCodecSpecific3:" + mCodecSpecific3 + + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; } public int describeContents() { @@ -211,26 +209,27 @@ public final class BluetoothCodecConfig implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothCodecConfig createFromParcel(Parcel in) { - final int codecType = in.readInt(); - final int codecPriority = in.readInt(); - final int sampleRate = in.readInt(); - final int bitsPerSample = in.readInt(); - final int channelMode = in.readInt(); - final long codecSpecific1 = in.readLong(); - final long codecSpecific2 = in.readLong(); - final long codecSpecific3 = in.readLong(); - final long codecSpecific4 = in.readLong(); - return new BluetoothCodecConfig(codecType, codecPriority, - sampleRate, bitsPerSample, - channelMode, codecSpecific1, - codecSpecific2, codecSpecific3, - codecSpecific4); - } - public BluetoothCodecConfig[] newArray(int size) { - return new BluetoothCodecConfig[size]; - } - }; + public BluetoothCodecConfig createFromParcel(Parcel in) { + final int codecType = in.readInt(); + final int codecPriority = in.readInt(); + final int sampleRate = in.readInt(); + final int bitsPerSample = in.readInt(); + final int channelMode = in.readInt(); + final long codecSpecific1 = in.readLong(); + final long codecSpecific2 = in.readLong(); + final long codecSpecific3 = in.readLong(); + final long codecSpecific4 = in.readLong(); + return new BluetoothCodecConfig(codecType, codecPriority, + sampleRate, bitsPerSample, + channelMode, codecSpecific1, + codecSpecific2, codecSpecific3, + codecSpecific4); + } + + public BluetoothCodecConfig[] newArray(int size) { + return new BluetoothCodecConfig[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeInt(mCodecType); @@ -251,20 +250,20 @@ public final class BluetoothCodecConfig implements Parcelable { */ public String getCodecName() { switch (mCodecType) { - case SOURCE_CODEC_TYPE_SBC: - return "SBC"; - case SOURCE_CODEC_TYPE_AAC: - return "AAC"; - case SOURCE_CODEC_TYPE_APTX: - return "aptX"; - case SOURCE_CODEC_TYPE_APTX_HD: - return "aptX HD"; - case SOURCE_CODEC_TYPE_LDAC: - return "LDAC"; - case SOURCE_CODEC_TYPE_INVALID: - return "INVALID CODEC"; - default: - break; + case SOURCE_CODEC_TYPE_SBC: + return "SBC"; + case SOURCE_CODEC_TYPE_AAC: + return "AAC"; + case SOURCE_CODEC_TYPE_APTX: + return "aptX"; + case SOURCE_CODEC_TYPE_APTX_HD: + return "aptX HD"; + case SOURCE_CODEC_TYPE_LDAC: + return "LDAC"; + case SOURCE_CODEC_TYPE_INVALID: + return "INVALID CODEC"; + default: + break; } return "UNKNOWN CODEC(" + mCodecType + ")"; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index c8cd8d17ce3..61e29416973 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -38,15 +38,15 @@ public final class BluetoothCodecStatus implements Parcelable { * profile. */ public static final String EXTRA_CODEC_STATUS = - "android.bluetooth.codec.extra.CODEC_STATUS"; + "android.bluetooth.codec.extra.CODEC_STATUS"; private final BluetoothCodecConfig mCodecConfig; private final BluetoothCodecConfig[] mCodecsLocalCapabilities; private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; public BluetoothCodecStatus(BluetoothCodecConfig codecConfig, - BluetoothCodecConfig[] codecsLocalCapabilities, - BluetoothCodecConfig[] codecsSelectableCapabilities) { + BluetoothCodecConfig[] codecsLocalCapabilities, + BluetoothCodecConfig[] codecsSelectableCapabilities) { mCodecConfig = codecConfig; mCodecsLocalCapabilities = codecsLocalCapabilities; mCodecsSelectableCapabilities = codecsSelectableCapabilities; @@ -55,12 +55,12 @@ public final class BluetoothCodecStatus implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothCodecStatus) { - BluetoothCodecStatus other = (BluetoothCodecStatus)o; + BluetoothCodecStatus other = (BluetoothCodecStatus) o; return (Objects.equals(other.mCodecConfig, mCodecConfig) && Objects.equals(other.mCodecsLocalCapabilities, - mCodecsLocalCapabilities) && + mCodecsLocalCapabilities) && Objects.equals(other.mCodecsSelectableCapabilities, - mCodecsSelectableCapabilities)); + mCodecsSelectableCapabilities)); } return false; } @@ -68,15 +68,15 @@ public final class BluetoothCodecStatus implements Parcelable { @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, - mCodecsLocalCapabilities); + mCodecsLocalCapabilities); } @Override public String toString() { return "{mCodecConfig:" + mCodecConfig + - ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + - ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + - "}"; + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + "}"; } public int describeContents() { @@ -85,19 +85,23 @@ public final class BluetoothCodecStatus implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothCodecStatus createFromParcel(Parcel in) { - final BluetoothCodecConfig codecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); - - return new BluetoothCodecStatus(codecConfig, - codecsLocalCapabilities, - codecsSelectableCapabilities); - } - public BluetoothCodecStatus[] newArray(int size) { - return new BluetoothCodecStatus[size]; - } - }; + public BluetoothCodecStatus createFromParcel(Parcel in) { + final BluetoothCodecConfig codecConfig = in.readTypedObject( + BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray( + BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray( + BluetoothCodecConfig.CREATOR); + + return new BluetoothCodecStatus(codecConfig, + codecsLocalCapabilities, + codecsSelectableCapabilities); + } + + public BluetoothCodecStatus[] newArray(int size) { + return new BluetoothCodecStatus[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a206b53b536..537d913b7dc 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -23,10 +23,9 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.Context; import android.os.Handler; -import android.os.Looper; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -101,7 +100,7 @@ public final class BluetoothDevice implements Parcelable { *

        Requires {@link android.Manifest.permission#BLUETOOTH} and * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive. */ - // TODO: Change API to not broadcast RSSI if not available (incoming connection) + // TODO: Change API to not broadcast RSSI if not available (incoming connection) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND"; @@ -112,6 +111,7 @@ public final class BluetoothDevice implements Parcelable { * found in the current discovery. *

        Always contains the extra field {@link #EXTRA_DEVICE}. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -208,6 +208,7 @@ public final class BluetoothDevice implements Parcelable { *

        Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_BATTERY_LEVEL}. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -219,6 +220,7 @@ public final class BluetoothDevice implements Parcelable { * intent. It contains the most recently retrieved battery level information * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN} * when the valid is unknown or there is an error + * * @hide */ public static final String EXTRA_BATTERY_LEVEL = @@ -226,6 +228,7 @@ public final class BluetoothDevice implements Parcelable { /** * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()} + * * @hide */ public static final int BATTERY_LEVEL_UNKNOWN = -1; @@ -300,6 +303,7 @@ public final class BluetoothDevice implements Parcelable { /** * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} * intents for unbond reason. + * * @hide */ public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; @@ -368,6 +372,7 @@ public final class BluetoothDevice implements Parcelable { * device. *

        Always contains the extra field {@link #EXTRA_DEVICE}. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * * @hide */ //TODO: is this actually useful? @@ -405,26 +410,28 @@ public final class BluetoothDevice implements Parcelable { /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. + * * @hide */ public static final String EXTRA_ACCESS_REQUEST_TYPE = - "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE"; + "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE"; - /**@hide*/ + /** @hide */ public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1; - /**@hide*/ + /** @hide */ public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2; - /**@hide*/ + /** @hide */ public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; - /**@hide*/ + /** @hide */ public static final int REQUEST_TYPE_SIM_ACCESS = 4; /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, * Contains package name to return reply intent to. + * * @hide */ public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME"; @@ -432,34 +439,38 @@ public final class BluetoothDevice implements Parcelable { /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, * Contains class name to return reply intent to. + * * @hide */ public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME"; /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. + * * @hide */ public static final String EXTRA_CONNECTION_ACCESS_RESULT = - "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; + "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; - /**@hide*/ + /** @hide */ public static final int CONNECTION_ACCESS_YES = 1; - /**@hide*/ + /** @hide */ public static final int CONNECTION_ACCESS_NO = 2; /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents, * Contains boolean to indicate if the allowed response is once-for-all so that * next request will be granted without asking user again. + * * @hide */ public static final String EXTRA_ALWAYS_ALLOWED = - "android.bluetooth.device.extra.ALWAYS_ALLOWED"; + "android.bluetooth.device.extra.ALWAYS_ALLOWED"; /** * A bond attempt succeeded + * * @hide */ public static final int BOND_SUCCESS = 0; @@ -467,6 +478,7 @@ public final class BluetoothDevice implements Parcelable { /** * A bond attempt failed because pins did not match, or remote device did * not respond to pin request in time + * * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; @@ -474,36 +486,42 @@ public final class BluetoothDevice implements Parcelable { /** * A bond attempt failed because the other side explicitly rejected * bonding + * * @hide */ public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** * A bond attempt failed because we canceled the bonding process + * * @hide */ public static final int UNBOND_REASON_AUTH_CANCELED = 3; /** * A bond attempt failed because we could not contact the remote device + * * @hide */ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** * A bond attempt failed because a discovery is in progress + * * @hide */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** * A bond attempt failed because of authentication timeout + * * @hide */ public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** * A bond attempt failed because of repeated attempts + * * @hide */ public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; @@ -511,12 +529,14 @@ public final class BluetoothDevice implements Parcelable { /** * A bond attempt failed because we received an Authentication Cancel * by remote end + * * @hide */ public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** * An existing bond was explicitly revoked + * * @hide */ public static final int UNBOND_REASON_REMOVED = 9; @@ -529,6 +549,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter a passkey + * * @hide */ public static final int PAIRING_VARIANT_PASSKEY = 1; @@ -541,6 +562,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to accept or deny the incoming pairing request + * * @hide */ public static final int PAIRING_VARIANT_CONSENT = 3; @@ -548,6 +570,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter the passkey displayed on remote device * This is used for Bluetooth 2.1 pairing. + * * @hide */ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; @@ -555,12 +578,14 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter the PIN displayed on remote device. * This is used for Bluetooth 2.0 pairing. + * * @hide */ public static final int PAIRING_VARIANT_DISPLAY_PIN = 5; /** * The user will be prompted to accept or deny the OOB pairing request + * * @hide */ public static final int PAIRING_VARIANT_OOB_CONSENT = 6; @@ -568,6 +593,7 @@ public final class BluetoothDevice implements Parcelable { /** * The user will be prompted to enter a 16 digit pin or * an app will enter a 16 digit pin for user. + * * @hide */ public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7; @@ -581,7 +607,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public static final String EXTRA_SDP_RECORD = - "android.bluetooth.device.extra.SDP_RECORD"; + "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ public static final String EXTRA_SDP_SEARCH_STATUS = @@ -589,6 +615,7 @@ public final class BluetoothDevice implements Parcelable { /** * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * * @hide */ public static final int ACCESS_UNKNOWN = 0; @@ -596,6 +623,7 @@ public final class BluetoothDevice implements Parcelable { /** * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * * @hide */ public static final int ACCESS_ALLOWED = 1; @@ -603,13 +631,14 @@ public final class BluetoothDevice implements Parcelable { /** * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. + * * @hide */ public static final int ACCESS_REJECTED = 2; - /** - * No preferrence of physical transport for GATT connections to remote dual-mode devices - */ + /** + * No preferrence of physical transport for GATT connections to remote dual-mode devices + */ public static final int TRANSPORT_AUTO = 0; /** @@ -676,7 +705,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public static final String EXTRA_MAS_INSTANCE = - "android.bluetooth.device.extra.MAS_INSTANCE"; + "android.bluetooth.device.extra.MAS_INSTANCE"; /** * Lazy initialization. Guaranteed final after first object constructed, or @@ -687,7 +716,8 @@ public final class BluetoothDevice implements Parcelable { private final String mAddress; - /*package*/ static IBluetooth getService() { + /*package*/ + static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -709,21 +739,22 @@ public final class BluetoothDevice implements Parcelable { } public void onBluetoothServiceDown() - throws RemoteException { + throws RemoteException { synchronized (BluetoothDevice.class) { sService = null; } } - public void onBrEdrDown() - { + public void onBrEdrDown() { if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); } }; + /** * Create a new BluetoothDevice * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", * and is validated in this constructor. + * * @param address valid Bluetooth MAC address * @throws RuntimeException Bluetooth is not available on this platform * @throws IllegalArgumentException address is invalid @@ -741,7 +772,7 @@ public final class BluetoothDevice implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothDevice) { - return mAddress.equals(((BluetoothDevice)o).getAddress()); + return mAddress.equals(((BluetoothDevice) o).getAddress()); } return false; } @@ -757,6 +788,7 @@ public final class BluetoothDevice implements Parcelable { * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} * if you explicitly require the Bluetooth hardware address in case the * {@link #toString} representation changes in the future. + * * @return string representation of this BluetoothDevice */ @Override @@ -770,13 +802,14 @@ public final class BluetoothDevice implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothDevice createFromParcel(Parcel in) { - return new BluetoothDevice(in.readString()); - } - public BluetoothDevice[] newArray(int size) { - return new BluetoothDevice[size]; - } - }; + public BluetoothDevice createFromParcel(Parcel in) { + return new BluetoothDevice(in.readString()); + } + + public BluetoothDevice[] newArray(int size) { + return new BluetoothDevice[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); @@ -785,6 +818,7 @@ public final class BluetoothDevice implements Parcelable { /** * Returns the hardware address of this BluetoothDevice. *

        For example, "00:11:22:AA:BB:CC". + * * @return Bluetooth hardware address as string */ public String getAddress() { @@ -809,16 +843,17 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.getRemoteName(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } /** * Get the Bluetooth device type of the remote device. * - * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} - * {@link #DEVICE_TYPE_DUAL}. - * {@link #DEVICE_TYPE_UNKNOWN} if it's not available + * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link + * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getType() { @@ -828,7 +863,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.getRemoteType(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return DEVICE_TYPE_UNKNOWN; } @@ -846,7 +883,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.getRemoteAlias(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -867,18 +906,20 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.setRemoteAlias(this, alias); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } /** * Get the Bluetooth alias of the remote device. * If Alias is null, get the Bluetooth name instead. - * @see #getAlias() - * @see #getName() * * @return the Bluetooth alias, or null if no alias or there was a problem * @hide + * @see #getAlias() + * @see #getName() */ public String getAliasName() { String name = getAlias(); @@ -893,8 +934,8 @@ public final class BluetoothDevice implements Parcelable { *

        Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if - * Bluetooth is disabled, or device is disconnected, or does not have any battery - * reporting service, or return value is invalid + * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting + * service, or return value is invalid * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) @@ -905,7 +946,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.getBatteryLevel(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return BATTERY_LEVEL_UNKNOWN; } @@ -930,7 +973,9 @@ public final class BluetoothDevice implements Parcelable { " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); return sService.createBond(this, TRANSPORT_AUTO); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -955,8 +1000,7 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } - if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) - { + if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) { throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); } try { @@ -964,7 +1008,9 @@ public final class BluetoothDevice implements Parcelable { " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); return sService.createBond(this, transport); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -984,13 +1030,14 @@ public final class BluetoothDevice implements Parcelable { * @param transport - Transport to use * @param oobData - Out Of Band data * @return false on immediate error, true if bonding will begin - * * @hide */ public boolean createBondOutOfBand(int transport, OobData oobData) { try { return sService.createBondOutOfBand(this, transport, oobData); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -998,7 +1045,9 @@ public final class BluetoothDevice implements Parcelable { public boolean isBondingInitiatedLocally() { try { return sService.isBondingInitiatedLocally(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1012,16 +1061,15 @@ public final class BluetoothDevice implements Parcelable { * @param hash Simple Secure pairing hash * @param randomizer The random key obtained using OOB * @return false on error; true otherwise - * * @hide */ public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) { - //TODO(BT) + //TODO(BT) /* try { return sService.setDeviceOutOfBandData(this, hash, randomizer); } catch (RemoteException e) {Log.e(TAG, "", e);} */ - return false; + return false; } /** @@ -1041,7 +1089,9 @@ public final class BluetoothDevice implements Parcelable { " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); return sService.cancelBondProcess(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1065,7 +1115,9 @@ public final class BluetoothDevice implements Parcelable { " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); return sService.removeBond(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1086,12 +1138,13 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.getBondState(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} - catch (NullPointerException npe) { + } catch (RemoteException e) { + Log.e(TAG, "", e); + } catch (NullPointerException npe) { // Handle case where bluetooth service proxy // is already null. - Log.e(TAG, "NullPointerException for getBondState() of device ("+ - getAddress()+")", npe); + Log.e(TAG, "NullPointerException for getBondState() of device (" + + getAddress() + ")", npe); } return BOND_NONE; } @@ -1154,7 +1207,9 @@ public final class BluetoothDevice implements Parcelable { int classInt = sService.getRemoteClass(this); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -1166,37 +1221,37 @@ public final class BluetoothDevice implements Parcelable { * UUIDs are returned. *

        Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. * - * @return the supported features (UUIDs) of the remote device, - * or null on error + * @return the supported features (UUIDs) of the remote device, or null on error */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public ParcelUuid[] getUuids() { - if (sService == null || isBluetoothEnabled() == false) { + public ParcelUuid[] getUuids() { + if (sService == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); - return null; - } + return null; + } try { return sService.getRemoteUuids(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } - /** - * Perform a service discovery on the remote device to get the UUIDs supported. - * - *

        This API is asynchronous and {@link #ACTION_UUID} intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently - * present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. - * - * @return False if the sanity check fails, True if the process - * of initiating an ACL connection to the remote device - * was started. - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public boolean fetchUuidsWithSdp() { + /** + * Perform a service discovery on the remote device to get the UUIDs supported. + * + *

        This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently + * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * if service discovery is not to be performed. + * + * @return False if the sanity check fails, True if the process of initiating an ACL connection + * to the remote device was started. + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean fetchUuidsWithSdp() { IBluetooth service = sService; if (service == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); @@ -1204,48 +1259,51 @@ public final class BluetoothDevice implements Parcelable { } try { return service.fetchRemoteUuids(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; } - /** - * Perform a service discovery on the remote device to get the SDP records associated - * with the specified UUID. - * - *

        This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, - * with the SDP records found on the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_SDP_RECORD} intent is sent with an status value in - * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. - * Detailed status error codes can be found by members of the Bluetooth package in - * the AbstractionLayer class. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. - * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. - * The object type will match one of the SdpXxxRecord types, depending on the UUID searched - * for. - * - * @return False if the sanity check fails, True if the process - * of initiating an ACL connection to the remote device - * was started. - */ - /** @hide */ - public boolean sdpSearch(ParcelUuid uuid) { - if (sService == null) { - Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); - return false; - } - try { - return sService.sdpSearch(this,uuid); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + /** + * Perform a service discovery on the remote device to get the SDP records associated + * with the specified UUID. + * + *

        This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, + * with the SDP records found on the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_SDP_RECORD} intent is sent with an status value in + * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. + * Detailed status error codes can be found by members of the Bluetooth package in + * the AbstractionLayer class. + *

        Requires {@link android.Manifest.permission#BLUETOOTH}. + * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. + * The object type will match one of the SdpXxxRecord types, depending on the UUID searched + * for. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + */ + /** @hide */ + public boolean sdpSearch(ParcelUuid uuid) { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); + return false; + } + try { + return sService.sdpSearch(this, uuid); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * - * @return true pin has been set - * false for error + * @return true pin has been set false for error */ public boolean setPin(byte[] pin) { if (sService == null) { @@ -1254,7 +1312,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.setPin(this, true, pin.length, pin); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1271,8 +1331,7 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. * - * @return true confirmation has been sent out - * false for error + * @return true confirmation has been sent out false for error */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { @@ -1282,7 +1341,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.setPairingConfirmation(this, confirm); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1293,7 +1354,7 @@ public final class BluetoothDevice implements Parcelable { try { return sService.setRemoteOutOfBandData(this); } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return false; + return false; } /** @hide */ @@ -1304,7 +1365,9 @@ public final class BluetoothDevice implements Parcelable { } try { return sService.cancelBondProcess(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1318,19 +1381,20 @@ public final class BluetoothDevice implements Parcelable { return false; } - boolean isBluetoothEnabled() { - boolean ret = false; - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.isEnabled() == true) { - ret = true; - } - return ret; - } + boolean isBluetoothEnabled() { + boolean ret = false; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.isEnabled() == true) { + ret = true; + } + return ret; + } /** * Requires {@link android.Manifest.permission#BLUETOOTH}. - * @return Whether the phonebook access is allowed to this device. Can be - * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * + * @return Whether the phonebook access is allowed to this device. Can be {@link + * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ public int getPhonebookAccessPermission() { @@ -1348,8 +1412,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the phonebook access is allowed to this device. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or - * {@link #ACCESS_REJECTED}. + * + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link + * #ACCESS_REJECTED}. * @return Whether the value has been successfully set. * @hide */ @@ -1367,8 +1432,9 @@ public final class BluetoothDevice implements Parcelable { /** * Requires {@link android.Manifest.permission#BLUETOOTH}. - * @return Whether the message access is allowed to this device. Can be - * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * + * @return Whether the message access is allowed to this device. Can be {@link #ACCESS_UNKNOWN}, + * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ public int getMessageAccessPermission() { @@ -1386,8 +1452,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the message access is allowed to this device. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or - * {@link #ACCESS_REJECTED}. + * + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link + * #ACCESS_REJECTED}. * @return Whether the value has been successfully set. * @hide */ @@ -1405,8 +1472,9 @@ public final class BluetoothDevice implements Parcelable { /** * Requires {@link android.Manifest.permission#BLUETOOTH}. - * @return Whether the Sim access is allowed to this device. Can be - * {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * + * @return Whether the Sim access is allowed to this device. Can be {@link #ACCESS_UNKNOWN}, + * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ public int getSimAccessPermission() { @@ -1424,8 +1492,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the Sim access is allowed to this device. *

        Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or - * {@link #ACCESS_REJECTED}. + * + * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link + * #ACCESS_REJECTED}. * @return Whether the value has been successfully set. * @hide */ @@ -1462,8 +1531,8 @@ public final class BluetoothDevice implements Parcelable { * * @param channel RFCOMM channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions * @hide */ public BluetoothSocket createRfcommSocket(int channel) throws IOException { @@ -1496,8 +1565,8 @@ public final class BluetoothDevice implements Parcelable { * * @param channel L2cap PSM/channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions * @hide */ public BluetoothSocket createL2capSocket(int channel) throws IOException { @@ -1517,8 +1586,8 @@ public final class BluetoothDevice implements Parcelable { * * @param channel L2cap PSM/channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions * @hide */ public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { @@ -1553,8 +1622,8 @@ public final class BluetoothDevice implements Parcelable { * * @param uuid service record uuid to lookup RFCOMM channel * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { @@ -1591,8 +1660,8 @@ public final class BluetoothDevice implements Parcelable { * * @param uuid service record uuid to lookup RFCOMM channel * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { @@ -1612,10 +1681,10 @@ public final class BluetoothDevice implements Parcelable { * socket will not be encrypted. *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * - * @param port remote port + * @param port remote port * @return An RFCOMM BluetoothSocket - * @throws IOException On error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException On error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { @@ -1634,8 +1703,8 @@ public final class BluetoothDevice implements Parcelable { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @return a SCO BluetoothSocket - * @throws IOException on error, for example Bluetooth not available, or - * insufficient permissions. + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions. * @hide */ public BluetoothSocket createScoSocket() throws IOException { @@ -1651,9 +1720,9 @@ public final class BluetoothDevice implements Parcelable { * Check that a pin is valid and convert to byte array. * * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters. + * * @param pin pin as java String - * @return the pin code as a UTF-8 byte array, or null if it is an invalid - * Bluetooth pin. + * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ public static byte[] convertPinToBytes(String pin) { @@ -1679,15 +1748,15 @@ public final class BluetoothDevice implements Parcelable { * as any further GATT client operations. * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct * GATT client operations. + * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). * @throws IllegalArgumentException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback) { - return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + BluetoothGattCallback callback) { + return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO)); } /** @@ -1696,18 +1765,18 @@ public final class BluetoothDevice implements Parcelable { * as any further GATT client operations. * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct * GATT client operations. + * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} * @throws IllegalArgumentException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect,callback, transport, PHY_LE_1M_MASK)); + BluetoothGattCallback callback, int transport) { + return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK)); } /** @@ -1716,22 +1785,22 @@ public final class BluetoothDevice implements Parcelable { * as any further GATT client operations. * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct * GATT client operations. + * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect - * if {@code autoConnect} is set to true. + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link + * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} + * is set to true. * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, int phy) { - return connectGatt(context, autoConnect,callback, transport, phy, null); + BluetoothGattCallback callback, int transport, int phy) { + return connectGatt(context, autoConnect, callback, transport, phy, null); } /** @@ -1740,24 +1809,24 @@ public final class BluetoothDevice implements Parcelable { * as any further GATT client operations. * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct * GATT client operations. + * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect - * if {@code autoConnect} is set to true. - * @param handler The handler to use for the callback. If {@code null}, callbacks will happen - * on an un-specified background thread. + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link + * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} + * is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on + * an un-specified background thread. * @throws NullPointerException if callback is null */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, int phy, - Handler handler) { + BluetoothGattCallback callback, int transport, int phy, + Handler handler) { return connectGatt(context, autoConnect, callback, transport, false, phy, handler); } @@ -1767,31 +1836,32 @@ public final class BluetoothDevice implements Parcelable { * as any further GATT client operations. * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct * GATT client operations. + * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client - * does not hold a GATT connection. It automatically disconnects when no - * other GATT connections are active for the remote device. - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * an d{@link BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect - * if {@code autoConnect} is set to true. - * @param handler The handler to use for the callback. If {@code null}, callbacks will happen - * on an un-specified background thread. + * does not hold a GATT connection. It automatically disconnects when no other GATT connections + * are active for the remote device. + * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link + * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} + * is set to true. + * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on + * an un-specified background thread. * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client - * operations. + * operations. * @hide */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, - boolean opportunistic, int phy, Handler handler) { - if (callback == null) + BluetoothGattCallback callback, int transport, + boolean opportunistic, int phy, Handler handler) { + if (callback == null) { throw new NullPointerException("callback is null"); + } // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? @@ -1806,7 +1876,9 @@ public final class BluetoothDevice implements Parcelable { BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy); gatt.connect(autoConnect, callback, handler); return gatt; - } catch (RemoteException e) {Log.e(TAG, "", e);} + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } } diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java index c794be2e2c9..09b0a80313f 100644 --- a/framework/java/android/bluetooth/BluetoothDevicePicker.java +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -48,11 +48,11 @@ public interface BluetoothDevicePicker { * This intent contains below extra data: * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be - * listed + * listed * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this - * intent come from + * intent come from * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent - * come from + * come from */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAUNCH = @@ -64,8 +64,10 @@ public interface BluetoothDevicePicker { public static final int FILTER_TYPE_AUDIO = 1; /** Ask device picker to show BT devices that support Object Transfer */ public static final int FILTER_TYPE_TRANSFER = 2; - /** Ask device picker to show BT devices that support - * Personal Area Networking User (PANU) profile*/ + /** + * Ask device picker to show BT devices that support + * Personal Area Networking User (PANU) profile + */ public static final int FILTER_TYPE_PANU = 3; /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */ public static final int FILTER_TYPE_NAP = 4; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 678159b7129..8a3650c8698 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -118,18 +118,21 @@ public final class BluetoothGatt implements BluetoothProfile { /** * No authentication required. + * * @hide */ /*package*/ static final int AUTHENTICATION_NONE = 0; /** * Authentication requested; no man-in-the-middle protection required. + * * @hide */ /*package*/ static final int AUTHENTICATION_NO_MITM = 1; /** * Authentication with man-in-the-middle protection requested. + * * @hide */ /*package*/ static final int AUTHENTICATION_MITM = 2; @@ -138,498 +141,536 @@ public final class BluetoothGatt implements BluetoothProfile { * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { - /** - * Application interface registered - app is ready to go - * @hide - */ - @Override - public void onClientRegistered(int status, int clientIf) { - if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status - + " clientIf=" + clientIf); - if (VDBG) { - synchronized(mStateLock) { - if (mConnState != CONN_STATE_CONNECTING) { - Log.e(TAG, "Bad connection state: " + mConnState); + new IBluetoothGattCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + @Override + public void onClientRegistered(int status, int clientIf) { + if (DBG) { + Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + } + if (VDBG) { + synchronized (mStateLock) { + if (mConnState != CONN_STATE_CONNECTING) { + Log.e(TAG, "Bad connection state: " + mConnState); + } } } + mClientIf = clientIf; + if (status != GATT_SUCCESS) { + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, + GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + } + } + }); + + synchronized (mStateLock) { + mConnState = CONN_STATE_IDLE; + } + return; + } + try { + mService.clientConnect(mClientIf, mDevice.getAddress(), + !mAutoConnect, mTransport, mOpportunistic, + mPhy); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG, "", e); + } } - mClientIf = clientIf; - if (status != GATT_SUCCESS) { + + /** + * Phy update callback + * @hide + */ + @Override + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) { + Log.d(TAG, "onPhyUpdate() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + } + if (!address.equals(mDevice.getAddress())) { + return; + } + runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); + mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); } } }); + } - synchronized(mStateLock) { - mConnState = CONN_STATE_IDLE; + /** + * Phy read callback + * @hide + */ + @Override + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) { + Log.d(TAG, "onPhyRead() - status=" + status + + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); + } + if (!address.equals(mDevice.getAddress())) { + return; } - return; - } - try { - mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport, mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** - * Phy update callback - * @hide - */ - @Override - public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { - if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status - + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); - if (!address.equals(mDevice.getAddress())) { - return; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + } + } + }); } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); - } + /** + * Client connection state changed + * @hide + */ + @Override + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + if (DBG) { + Log.d(TAG, "onClientConnectionState() - status=" + status + + " clientIf=" + clientIf + " device=" + address); } - }); - } - - /** - * Phy read callback - * @hide - */ - @Override - public void onPhyRead(String address, int txPhy, int rxPhy, int status) { - if (DBG) Log.d(TAG, "onPhyRead() - status=" + status - + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); - if (!address.equals(mDevice.getAddress())) { - return; - } + if (!address.equals(mDevice.getAddress())) { + return; + } + int profileState = connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED; - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onConnectionStateChange(BluetoothGatt.this, status, + profileState); + } } - } - }); - } + }); - /** - * Client connection state changed - * @hide - */ - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status - + " clientIf=" + clientIf + " device=" + address); - if (!address.equals(mDevice.getAddress())) { - return; - } - int profileState = connected ? BluetoothProfile.STATE_CONNECTED : - BluetoothProfile.STATE_DISCONNECTED; - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, - profileState); + synchronized (mStateLock) { + if (connected) { + mConnState = CONN_STATE_CONNECTED; + } else { + mConnState = CONN_STATE_IDLE; } } - }); - synchronized(mStateLock) { - if (connected) { - mConnState = CONN_STATE_CONNECTED; - } else { - mConnState = CONN_STATE_IDLE; + synchronized (mDeviceBusy) { + mDeviceBusy = false; } } - synchronized(mDeviceBusy) { - mDeviceBusy = false; - } - } - - /** - * Remote search has been completed. - * The internal object structure should now reflect the state - * of the remote device database. Let the application know that - * we are done at this point. - * @hide - */ - @Override - public void onSearchComplete(String address, List services, - int status) { - if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); - if (!address.equals(mDevice.getAddress())) { - return; - } - - for (BluetoothGattService s : services) { - //services we receive don't have device set properly. - s.setDevice(mDevice); - } - - mServices.addAll(services); - - // Fix references to included services, as they doesn't point to right objects. - for (BluetoothGattService fixedService : mServices) { - ArrayList includedServices = - new ArrayList(fixedService.getIncludedServices()); - fixedService.getIncludedServices().clear(); + /** + * Remote search has been completed. + * The internal object structure should now reflect the state + * of the remote device database. Let the application know that + * we are done at this point. + * @hide + */ + @Override + public void onSearchComplete(String address, List services, + int status) { + if (DBG) { + Log.d(TAG, + "onSearchComplete() = Device=" + address + " Status=" + status); + } + if (!address.equals(mDevice.getAddress())) { + return; + } - for(BluetoothGattService brokenRef : includedServices) { - BluetoothGattService includedService = getService(mDevice, - brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType()); - if (includedService != null) { - fixedService.addIncludedService(includedService); - } else { - Log.e(TAG, "Broken GATT database: can't find included service."); - } + for (BluetoothGattService s : services) { + //services we receive don't have device set properly. + s.setDevice(mDevice); } - } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); + mServices.addAll(services); + + // Fix references to included services, as they doesn't point to right objects. + for (BluetoothGattService fixedService : mServices) { + ArrayList includedServices = + new ArrayList(fixedService.getIncludedServices()); + fixedService.getIncludedServices().clear(); + + for (BluetoothGattService brokenRef : includedServices) { + BluetoothGattService includedService = getService(mDevice, + brokenRef.getUuid(), brokenRef.getInstanceId(), + brokenRef.getType()); + if (includedService != null) { + fixedService.addIncludedService(includedService); + } else { + Log.e(TAG, "Broken GATT database: can't find included service."); + } } } - }); - } - /** - * Remote characteristic has been read. - * Updates the internal value. - * @hide - */ - @Override - public void onCharacteristicRead(String address, int status, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address - + " handle=" + handle + " Status=" + status); - - if (!address.equals(mDevice.getAddress())) { - return; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onServicesDiscovered(BluetoothGatt.this, status); + } + } + }); } - synchronized(mDeviceBusy) { - mDeviceBusy = false; - } + /** + * Remote characteristic has been read. + * Updates the internal value. + * @hide + */ + @Override + public void onCharacteristicRead(String address, int status, int handle, + byte[] value) { + if (VDBG) { + Log.d(TAG, "onCharacteristicRead() - Device=" + address + + " handle=" + handle + " Status=" + status); + } - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readCharacteristic(mClientIf, address, handle, authReq); - mAuthRetryState++; + if (!address.equals(mDevice.getAddress())) { return; - } catch (RemoteException e) { - Log.e(TAG,"",e); } - } - mAuthRetryState = AUTH_RETRY_STATE_IDLE; + synchronized (mDeviceBusy) { + mDeviceBusy = false; + } - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); - if (characteristic == null) { - Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); - return; - } + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + try { + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readCharacteristic(mClientIf, address, handle, authReq); + mAuthRetryState++; + return; + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } - if (status == 0) characteristic.setValue(value); + mAuthRetryState = AUTH_RETRY_STATE_IDLE; - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, - status); - } + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, + handle); + if (characteristic == null) { + Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); + return; } - }); - } - /** - * Characteristic has been written to the remote device. - * Let the app know how we did... - * @hide - */ - @Override - public void onCharacteristicWrite(String address, int status, int handle) { - if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address - + " handle=" + handle + " Status=" + status); - - if (!address.equals(mDevice.getAddress())) { - return; - } + if (status == 0) characteristic.setValue(value); - synchronized(mDeviceBusy) { - mDeviceBusy = false; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, + status); + } + } + }); } - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); - if (characteristic == null) return; + /** + * Characteristic has been written to the remote device. + * Let the app know how we did... + * @hide + */ + @Override + public void onCharacteristicWrite(String address, int status, int handle) { + if (VDBG) { + Log.d(TAG, "onCharacteristicWrite() - Device=" + address + + " handle=" + handle + " Status=" + status); + } - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.writeCharacteristic(mClientIf, address, handle, - characteristic.getWriteType(), authReq, characteristic.getValue()); - mAuthRetryState++; + if (!address.equals(mDevice.getAddress())) { return; - } catch (RemoteException e) { - Log.e(TAG,"",e); } - } - mAuthRetryState = AUTH_RETRY_STATE_IDLE; + synchronized (mDeviceBusy) { + mDeviceBusy = false; + } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, - status); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, + handle); + if (characteristic == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + try { + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.writeCharacteristic(mClientIf, address, handle, + characteristic.getWriteType(), authReq, + characteristic.getValue()); + mAuthRetryState++; + return; + } catch (RemoteException e) { + Log.e(TAG, "", e); } } - }); - } - /** - * Remote characteristic has been updated. - * Updates the internal value. - * @hide - */ - @Override - public void onNotify(String address, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); - - if (!address.equals(mDevice.getAddress())) { - return; - } + mAuthRetryState = AUTH_RETRY_STATE_IDLE; - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); - if (characteristic == null) return; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, + status); + } + } + }); + } - characteristic.setValue(value); + /** + * Remote characteristic has been updated. + * Updates the internal value. + * @hide + */ + @Override + public void onNotify(String address, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); - } + if (!address.equals(mDevice.getAddress())) { + return; } - }); - } - /** - * Descriptor has been read. - * @hide - */ - @Override - public void onDescriptorRead(String address, int status, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle); + BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, + handle); + if (characteristic == null) return; - if (!address.equals(mDevice.getAddress())) { - return; - } + characteristic.setValue(value); - synchronized(mDeviceBusy) { - mDeviceBusy = false; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onCharacteristicChanged(BluetoothGatt.this, + characteristic); + } + } + }); } - BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); - if (descriptor == null) return; - - if (status == 0) descriptor.setValue(value); + /** + * Descriptor has been read. + * @hide + */ + @Override + public void onDescriptorRead(String address, int status, int handle, byte[] value) { + if (VDBG) { + Log.d(TAG, + "onDescriptorRead() - Device=" + address + " handle=" + handle); + } - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readDescriptor(mClientIf, address, handle, authReq); - mAuthRetryState++; + if (!address.equals(mDevice.getAddress())) { return; - } catch (RemoteException e) { - Log.e(TAG,"",e); } - } - mAuthRetryState = AUTH_RETRY_STATE_IDLE; + synchronized (mDeviceBusy) { + mDeviceBusy = false; + } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); + if (descriptor == null) return; + + if (status == 0) descriptor.setValue(value); + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + try { + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.readDescriptor(mClientIf, address, handle, authReq); + mAuthRetryState++; + return; + } catch (RemoteException e) { + Log.e(TAG, "", e); } } - }); - } - - /** - * Descriptor write operation complete. - * @hide - */ - @Override - public void onDescriptorWrite(String address, int status, int handle) { - if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); - if (!address.equals(mDevice.getAddress())) { - return; - } + mAuthRetryState = AUTH_RETRY_STATE_IDLE; - synchronized(mDeviceBusy) { - mDeviceBusy = false; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + } + } + }); } - BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); - if (descriptor == null) return; + /** + * Descriptor write operation complete. + * @hide + */ + @Override + public void onDescriptorWrite(String address, int status, int handle) { + if (VDBG) { + Log.d(TAG, + "onDescriptorWrite() - Device=" + address + " handle=" + handle); + } - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.writeDescriptor(mClientIf, address, handle, - authReq, descriptor.getValue()); - mAuthRetryState++; + if (!address.equals(mDevice.getAddress())) { return; - } catch (RemoteException e) { - Log.e(TAG,"",e); } - } - mAuthRetryState = AUTH_RETRY_STATE_IDLE; + synchronized (mDeviceBusy) { + mDeviceBusy = false; + } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); + if (descriptor == null) return; + + if ((status == GATT_INSUFFICIENT_AUTHENTICATION + || status == GATT_INSUFFICIENT_ENCRYPTION) + && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + try { + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? + AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + mService.writeDescriptor(mClientIf, address, handle, + authReq, descriptor.getValue()); + mAuthRetryState++; + return; + } catch (RemoteException e) { + Log.e(TAG, "", e); } } - }); - } - /** - * Prepared write transaction completed (or aborted) - * @hide - */ - @Override - public void onExecuteWrite(String address, int status) { - if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address - + " status=" + status); - if (!address.equals(mDevice.getAddress())) { - return; - } + mAuthRetryState = AUTH_RETRY_STATE_IDLE; - synchronized(mDeviceBusy) { - mDeviceBusy = false; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + } + } + }); } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); - } + /** + * Prepared write transaction completed (or aborted) + * @hide + */ + @Override + public void onExecuteWrite(String address, int status) { + if (VDBG) { + Log.d(TAG, "onExecuteWrite() - Device=" + address + + " status=" + status); + } + if (!address.equals(mDevice.getAddress())) { + return; } - }); - } - /** - * Remote device RSSI has been read - * @hide - */ - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + - " rssi=" + rssi + " status=" + status); - if (!address.equals(mDevice.getAddress())) { - return; - } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); - } + synchronized (mDeviceBusy) { + mDeviceBusy = false; } - }); - } - /** - * Callback invoked when the MTU for a given connection changes - * @hide - */ - @Override - public void onConfigureMTU(String address, int mtu, int status) { - if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address + - " mtu=" + mtu + " status=" + status); - if (!address.equals(mDevice.getAddress())) { - return; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + } + } + }); } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + /** + * Remote device RSSI has been read + * @hide + */ + @Override + public void onReadRemoteRssi(String address, int rssi, int status) { + if (VDBG) { + Log.d(TAG, "onReadRemoteRssi() - Device=" + address + + " rssi=" + rssi + " status=" + status); + } + if (!address.equals(mDevice.getAddress())) { + return; + } + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + } } + }); + } + + /** + * Callback invoked when the MTU for a given connection changes + * @hide + */ + @Override + public void onConfigureMTU(String address, int mtu, int status) { + if (DBG) { + Log.d(TAG, "onConfigureMTU() - Device=" + address + + " mtu=" + mtu + " status=" + status); + } + if (!address.equals(mDevice.getAddress())) { + return; } - }); - } - /** - * Callback invoked when the given connection is updated - * @hide - */ - @Override - public void onConnectionUpdated(String address, int interval, int latency, - int timeout, int status) { - if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address + - " interval=" + interval + " latency=" + latency + - " timeout=" + timeout + " status=" + status); - if (!address.equals(mDevice.getAddress())) { - return; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + } + } + }); } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - if (mCallback != null) { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); - } + /** + * Callback invoked when the given connection is updated + * @hide + */ + @Override + public void onConnectionUpdated(String address, int interval, int latency, + int timeout, int status) { + if (DBG) { + Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); + } + if (!address.equals(mDevice.getAddress())) { + return; } - }); - } - }; + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + if (mCallback != null) { + mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + timeout, status); + } + } + }); + } + }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport, boolean opportunistic, int phy) { + int transport, boolean opportunistic, int phy) { mService = iGatt; mDevice = device; mTransport = transport; @@ -657,15 +698,16 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Returns a service by UUID, instance and type. + * * @hide */ /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, - int instanceId, int type) { - for(BluetoothGattService svc : mServices) { + int instanceId, int type) { + for (BluetoothGattService svc : mServices) { if (svc.getDevice().equals(device) && - svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { + svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { return svc; } } @@ -675,13 +717,16 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Returns a characteristic with id equal to instanceId. + * * @hide */ - /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) { - for(BluetoothGattService svc : mServices) { - for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - if (charac.getInstanceId() == instanceId) + /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, + int instanceId) { + for (BluetoothGattService svc : mServices) { + for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + if (charac.getInstanceId() == instanceId) { return charac; + } } } return null; @@ -689,14 +734,16 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Returns a descriptor with id equal to instanceId. + * * @hide */ /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { - for(BluetoothGattService svc : mServices) { - for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - for(BluetoothGattDescriptor desc : charac.getDescriptors()) { - if (desc.getInstanceId() == instanceId) + for (BluetoothGattService svc : mServices) { + for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + for (BluetoothGattDescriptor desc : charac.getDescriptors()) { + if (desc.getInstanceId() == instanceId) { return desc; + } } } } @@ -709,13 +756,13 @@ public final class BluetoothGatt implements BluetoothProfile { */ private void runOrQueueCallback(final Runnable cb) { if (mHandler == null) { - try { - cb.run(); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } + try { + cb.run(); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } else { - mHandler.post(cb); + mHandler.post(cb); } } @@ -728,8 +775,8 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param callback GATT callback handler that will receive asynchronous callbacks. - * @return If true, the callback will be called to notify success or failure, - * false on immediate error + * @return If true, the callback will be called to notify success or failure, false on immediate + * error */ private boolean registerApp(BluetoothGattCallback callback, Handler handler) { if (DBG) Log.d(TAG, "registerApp()"); @@ -743,7 +790,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -762,7 +809,7 @@ public final class BluetoothGatt implements BluetoothProfile { mService.unregisterClient(mClientIf); mClientIf = 0; } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -784,15 +831,17 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Remote device to connect to - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, - Handler handler) { - if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); - synchronized(mStateLock) { + Handler handler) { + if (DBG) { + Log.d(TAG, + "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); + } + synchronized (mStateLock) { if (mConnState != CONN_STATE_IDLE) { throw new IllegalStateException("Not idle"); } @@ -802,7 +851,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAutoConnect = autoConnect; if (!registerApp(callback, handler)) { - synchronized(mStateLock) { + synchronized (mStateLock) { mConnState = CONN_STATE_IDLE; } Log.e(TAG, "Failed to register callback"); @@ -826,7 +875,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.clientDisconnect(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -845,7 +894,7 @@ public final class BluetoothGatt implements BluetoothProfile { mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } } @@ -858,22 +907,22 @@ public final class BluetoothGatt implements BluetoothProfile { * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even * if no PHY change happens. It is also triggered when remote device updates the PHY. * - * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. - * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. + * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link + * BluetoothDevice#PHY_LE_CODED_MASK}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link + * BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one - * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, - * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or + * {@link BluetoothDevice#PHY_OPTION_S8} */ public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { try { mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -885,7 +934,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.clientReadPhy(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -920,7 +969,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.discoverServices(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -960,8 +1009,8 @@ public final class BluetoothGatt implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @return List of services on the remote device. Returns an empty list - * if service discovery has not yet been performed. + * @return List of services on the remote device. Returns an empty list if service discovery has + * not yet been performed. */ public List getServices() { List result = @@ -989,13 +1038,13 @@ public final class BluetoothGatt implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param uuid UUID of the requested service - * @return BluetoothGattService if supported, or null if the requested - * service is not offered by the remote device. + * @return BluetoothGattService if supported, or null if the requested service is not offered by + * the remote device. */ public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { if (service.getDevice().equals(mDevice) && - service.getUuid().equals(uuid)) { + service.getUuid().equals(uuid)) { return service; } } @@ -1017,7 +1066,9 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { if ((characteristic.getProperties() & - BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; + BluetoothGattCharacteristic.PROPERTY_READ) == 0) { + return false; + } if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); if (mService == null || mClientIf == 0) return false; @@ -1028,16 +1079,16 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } try { mService.readCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), AUTHENTICATION_NONE); + characteristic.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1062,16 +1113,16 @@ public final class BluetoothGatt implements BluetoothProfile { if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); if (mService == null || mClientIf == 0) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } try { mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), - new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); + new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1094,8 +1145,10 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 - && (characteristic.getProperties() & - BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; + && (characteristic.getProperties() & + BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + return false; + } if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false; @@ -1106,17 +1159,17 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } try { mService.writeCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), characteristic.getWriteType(), - AUTHENTICATION_NONE, characteristic.getValue()); + characteristic.getInstanceId(), characteristic.getWriteType(), + AUTHENTICATION_NONE, characteristic.getValue()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1149,16 +1202,16 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } try { mService.readDescriptor(mClientIf, device.getAddress(), - descriptor.getInstanceId(), AUTHENTICATION_NONE); + descriptor.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1190,16 +1243,16 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - AUTHENTICATION_NONE, descriptor.getValue()); + AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1234,7 +1287,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.beginReliableWrite(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1258,7 +1311,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; - synchronized(mDeviceBusy) { + synchronized (mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1266,7 +1319,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mDeviceBusy = false; return false; } @@ -1289,7 +1342,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -1316,9 +1369,11 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the requested notification status was set successfully */ public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, - boolean enable) { - if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() - + " enable: " + enable); + boolean enable) { + if (DBG) { + Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() + + " enable: " + enable); + } if (mService == null || mClientIf == 0) return false; BluetoothGattService service = characteristic.getService(); @@ -1329,9 +1384,9 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification(mClientIf, device.getAddress(), - characteristic.getInstanceId(), enable); + characteristic.getInstanceId(), enable); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1341,6 +1396,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Clears the internal cache and forces a refresh of the services from the * remote device. + * * @hide */ public boolean refresh() { @@ -1350,7 +1406,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.refreshDevice(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1374,7 +1430,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readRemoteRssi(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1396,14 +1452,16 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the new MTU value has been requested successfully */ public boolean requestMtu(int mtu) { - if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() - + " mtu: " + mtu); + if (DBG) { + Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() + + " mtu: " + mtu); + } if (mService == null || mClientIf == 0) return false; try { mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1416,16 +1474,14 @@ public final class BluetoothGatt implements BluetoothProfile { *

        This function will send a connection parameter update request to the * remote device. * - * @param connectionPriority Request a specific connection priority. Must be one of - * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, - * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} - * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. - * @throws IllegalArgumentException If the parameters are outside of their - * specified range. + * @param connectionPriority Request a specific connection priority. Must be one of {@link + * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} + * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. + * @throws IllegalArgumentException If the parameters are outside of their specified range. */ public boolean requestConnectionPriority(int connectionPriority) { if (connectionPriority < CONNECTION_PRIORITY_BALANCED || - connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { + connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { throw new IllegalArgumentException("connectionPriority not within valid range"); } @@ -1435,7 +1491,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -1462,7 +1518,7 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public List getConnectedDevices() { throw new UnsupportedOperationException - ("Use BluetoothManager#getConnectedDevices instead."); + ("Use BluetoothManager#getConnectedDevices instead."); } /** @@ -1475,6 +1531,6 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { throw new UnsupportedOperationException - ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index c6f82ffb7ae..cf82a330457 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -19,19 +19,19 @@ package android.bluetooth; /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. */ -public abstract class BluetoothGattCallback{ +public abstract class BluetoothGattCallback { /** * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of * remote device changing the PHY. * * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status Status of the PHY update operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } @@ -40,12 +40,12 @@ public abstract class BluetoothGattCallback{ * Callback triggered as result of {@link BluetoothGatt#readPhy} * * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status Status of the PHY read operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. + * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { } @@ -55,14 +55,13 @@ public abstract class BluetoothGattCallback{ * GATT server. * * @param gatt GATT client - * @param status Status of the connect or disconnect operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @param status Status of the connect or disconnect operation. {@link + * BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param newState Returns the new connection state. Can be one of {@link + * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED} */ public void onConnectionStateChange(BluetoothGatt gatt, int status, - int newState) { + int newState) { } /** @@ -70,8 +69,8 @@ public abstract class BluetoothGattCallback{ * for the remote device have been updated, ie new services have been discovered. * * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device - * has been explored successfully. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device has been explored + * successfully. */ public void onServicesDiscovered(BluetoothGatt gatt, int status) { } @@ -80,13 +79,12 @@ public abstract class BluetoothGattCallback{ * Callback reporting the result of a characteristic read operation. * * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} - * @param characteristic Characteristic that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully. + * @param characteristic Characteristic that was read from the associated remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully. */ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, - int status) { + int status) { } /** @@ -99,58 +97,55 @@ public abstract class BluetoothGattCallback{ * the application must abort the reliable write transaction. * * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} - * @param characteristic Characteristic that was written to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param characteristic Characteristic that was written to the associated remote device. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + BluetoothGattCharacteristic characteristic, int status) { } /** * Callback triggered as a result of a remote characteristic notification. * * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result - * of a remote notification event. + * @param characteristic Characteristic that has been updated as a result of a remote + * notification event. */ public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + BluetoothGattCharacteristic characteristic) { } /** * Callback reporting the result of a descriptor read operation. * * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated - * remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation - * was completed successfully + * @param descriptor Descriptor that was read from the associated remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully */ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { + int status) { } /** * Callback indicating the result of a descriptor write operation. * * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} - * @param descriptor Descriptor that was writte to the associated - * remote device. - * @param status The result of the write operation - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param descriptor Descriptor that was writte to the associated remote device. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { + int status) { } /** * Callback invoked when a reliable write transaction has been completed. * * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write - * transaction was executed successfully + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write transaction was + * executed successfully */ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { } @@ -186,17 +181,17 @@ public abstract class BluetoothGattCallback{ * Callback indicating the connection parameters were updated. * * @param gatt GATT client involved - * @param interval Connection interval used on this connection, 1.25ms unit. Valid - * range is from 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid - * range is from 0 to 499 - * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is - * from 10 (0.1s) to 3200 (32s) + * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from + * 6 (7.5ms) to 3200 (4000ms). + * @param latency Slave latency for the connection in number of connection events. Valid range + * is from 0 to 499 + * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 + * (0.1s) to 3200 (32s) * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated - * successfully + * successfully * @hide */ public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout, - int status) { + int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 1cc2270be56..48168046524 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -16,8 +16,9 @@ package android.bluetooth; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Parcelable; + import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -171,30 +172,35 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * The UUID of this characteristic. + * * @hide */ protected UUID mUuid; /** * Instance ID for this characteristic. + * * @hide */ protected int mInstance; /** * Characteristic properties. + * * @hide */ protected int mProperties; /** * Characteristic permissions. + * * @hide */ protected int mPermissions; /** * Key size (default = 16). + * * @hide */ protected int mKeySize = 16; @@ -202,18 +208,21 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Write type for this characteristic. * See WRITE_TYPE_* constants. + * * @hide */ protected int mWriteType; /** * Back-reference to the service this characteristic belongs to. + * * @hide */ protected BluetoothGattService mService; /** * The cached value of this characteristic. + * * @hide */ protected byte[] mValue; @@ -237,26 +246,28 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Create a new BluetoothGattCharacteristic + * * @hide */ /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, - UUID uuid, int instanceId, - int properties, int permissions) { + UUID uuid, int instanceId, + int properties, int permissions) { initCharacteristic(service, uuid, instanceId, properties, permissions); } /** * Create a new BluetoothGattCharacteristic + * * @hide */ public BluetoothGattCharacteristic(UUID uuid, int instanceId, - int properties, int permissions) { + int properties, int permissions) { initCharacteristic(null, uuid, instanceId, properties, permissions); } private void initCharacteristic(BluetoothGattService service, - UUID uuid, int instanceId, - int properties, int permissions) { + UUID uuid, int instanceId, + int properties, int permissions) { mUuid = uuid; mInstance = instanceId; mProperties = properties; @@ -301,7 +312,7 @@ public class BluetoothGattCharacteristic implements Parcelable { }; private BluetoothGattCharacteristic(Parcel in) { - mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstance = in.readInt(); mProperties = in.readInt(); mPermissions = in.readInt(); @@ -313,7 +324,7 @@ public class BluetoothGattCharacteristic implements Parcelable { ArrayList descs = in.createTypedArrayList(BluetoothGattDescriptor.CREATOR); if (descs != null) { - for (BluetoothGattDescriptor desc: descs) { + for (BluetoothGattDescriptor desc : descs) { desc.setCharacteristic(this); mDescriptors.add(desc); } @@ -322,6 +333,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Returns the desired key size. + * * @hide */ public int getKeySize() { @@ -343,12 +355,13 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Get a descriptor by UUID and isntance id. + * * @hide */ /*package*/ BluetoothGattDescriptor getDescriptor(UUID uuid, int instanceId) { - for(BluetoothGattDescriptor descriptor : mDescriptors) { + for (BluetoothGattDescriptor descriptor : mDescriptors) { if (descriptor.getUuid().equals(uuid) - && descriptor.getInstanceId() == instanceId) { + && descriptor.getInstanceId() == instanceId) { return descriptor; } } @@ -357,6 +370,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Returns the service this characteristic belongs to. + * * @return The asscociated service */ public BluetoothGattService getService() { @@ -365,6 +379,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Sets the service associated with this device. + * * @hide */ /*package*/ void setService(BluetoothGattService service) { @@ -394,6 +409,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Force the instance ID. + * * @hide */ public void setInstanceId(int instanceId) { @@ -437,11 +453,8 @@ public class BluetoothGattCharacteristic implements Parcelable { * {@link BluetoothGatt#writeCharacteristic} function write this * characteristic. * - * @param writeType The write type to for this characteristic. Can be one - * of: - * {@link #WRITE_TYPE_DEFAULT}, - * {@link #WRITE_TYPE_NO_RESPONSE} or - * {@link #WRITE_TYPE_SIGNED}. + * @param writeType The write type to for this characteristic. Can be one of: {@link + * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}. */ public void setWriteType(int writeType) { mWriteType = writeType; @@ -449,6 +462,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Set the desired key size. + * * @hide */ public void setKeySize(int keySize) { @@ -468,11 +482,10 @@ public class BluetoothGattCharacteristic implements Parcelable { * Returns a descriptor with a given UUID out of the list of * descriptors for this characteristic. * - * @return GATT descriptor object or null if no descriptor with the - * given UUID was found. + * @return GATT descriptor object or null if no descriptor with the given UUID was found. */ public BluetoothGattDescriptor getDescriptor(UUID uuid) { - for(BluetoothGattDescriptor descriptor : mDescriptors) { + for (BluetoothGattDescriptor descriptor : mDescriptors) { if (descriptor.getUuid().equals(uuid)) { return descriptor; } @@ -503,11 +516,9 @@ public class BluetoothGattCharacteristic implements Parcelable { * characteristic value at the given offset are interpreted to generate the * return value. * - * @param formatType The format type used to interpret the characteristic - * value. + * @param formatType The format type used to interpret the characteristic value. * @param offset Offset at which the integer value can be found. - * @return Cached value of the characteristic or null of offset exceeds - * value size. + * @return Cached value of the characteristic or null of offset exceeds value size. */ public Integer getIntValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; @@ -517,21 +528,21 @@ public class BluetoothGattCharacteristic implements Parcelable { return unsignedByteToInt(mValue[offset]); case FORMAT_UINT16: - return unsignedBytesToInt(mValue[offset], mValue[offset+1]); + return unsignedBytesToInt(mValue[offset], mValue[offset + 1]); case FORMAT_UINT32: - return unsignedBytesToInt(mValue[offset], mValue[offset+1], - mValue[offset+2], mValue[offset+3]); + return unsignedBytesToInt(mValue[offset], mValue[offset + 1], + mValue[offset + 2], mValue[offset + 3]); case FORMAT_SINT8: return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8); case FORMAT_SINT16: return unsignedToSigned(unsignedBytesToInt(mValue[offset], - mValue[offset+1]), 16); + mValue[offset + 1]), 16); case FORMAT_SINT32: return unsignedToSigned(unsignedBytesToInt(mValue[offset], - mValue[offset+1], mValue[offset+2], mValue[offset+3]), 32); + mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32); } return null; @@ -541,22 +552,21 @@ public class BluetoothGattCharacteristic implements Parcelable { * Return the stored value of this characteristic. *

        See {@link #getValue} for details. * - * @param formatType The format type used to interpret the characteristic - * value. + * @param formatType The format type used to interpret the characteristic value. * @param offset Offset at which the float value can be found. - * @return Cached value of the characteristic at a given offset or null - * if the requested offset exceeds the value size. + * @return Cached value of the characteristic at a given offset or null if the requested offset + * exceeds the value size. */ public Float getFloatValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; switch (formatType) { case FORMAT_SFLOAT: - return bytesToFloat(mValue[offset], mValue[offset+1]); + return bytesToFloat(mValue[offset], mValue[offset + 1]); case FORMAT_FLOAT: - return bytesToFloat(mValue[offset], mValue[offset+1], - mValue[offset+2], mValue[offset+3]); + return bytesToFloat(mValue[offset], mValue[offset + 1], + mValue[offset + 2], mValue[offset + 3]); } return null; @@ -572,7 +582,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public String getStringValue(int offset) { if (mValue == null || offset > mValue.length) return null; byte[] strBytes = new byte[mValue.length - offset]; - for (int i=0; i != (mValue.length-offset); ++i) strBytes[i] = mValue[offset+i]; + for (int i = 0; i != (mValue.length - offset); ++i) strBytes[i] = mValue[offset + i]; return new String(strBytes); } @@ -585,8 +595,8 @@ public class BluetoothGattCharacteristic implements Parcelable { * remote device. * * @param value New value for this characteristic - * @return true if the locally stored value has been set, false if the - * requested value could not be stored locally. + * @return true if the locally stored value has been set, false if the requested value could not + * be stored locally. */ public boolean setValue(byte[] value) { mValue = value; @@ -612,25 +622,25 @@ public class BluetoothGattCharacteristic implements Parcelable { value = intToSignedBits(value, 8); // Fall-through intended case FORMAT_UINT8: - mValue[offset] = (byte)(value & 0xFF); + mValue[offset] = (byte) (value & 0xFF); break; case FORMAT_SINT16: value = intToSignedBits(value, 16); // Fall-through intended case FORMAT_UINT16: - mValue[offset++] = (byte)(value & 0xFF); - mValue[offset] = (byte)((value >> 8) & 0xFF); + mValue[offset++] = (byte) (value & 0xFF); + mValue[offset] = (byte) ((value >> 8) & 0xFF); break; case FORMAT_SINT32: value = intToSignedBits(value, 32); // Fall-through intended case FORMAT_UINT32: - mValue[offset++] = (byte)(value & 0xFF); - mValue[offset++] = (byte)((value >> 8) & 0xFF); - mValue[offset++] = (byte)((value >> 16) & 0xFF); - mValue[offset] = (byte)((value >> 24) & 0xFF); + mValue[offset++] = (byte) (value & 0xFF); + mValue[offset++] = (byte) ((value >> 8) & 0xFF); + mValue[offset++] = (byte) ((value >> 16) & 0xFF); + mValue[offset] = (byte) ((value >> 24) & 0xFF); break; default: @@ -644,7 +654,7 @@ public class BluetoothGattCharacteristic implements Parcelable { *

        See {@link #setValue(byte[])} for details. * * @param mantissa Mantissa for this characteristic - * @param exponent exponent value for this characteristic + * @param exponent exponent value for this characteristic * @param formatType Float format type used to transform the value parameter * @param offset Offset at which the value should be placed * @return true if the locally stored value has been set @@ -658,18 +668,18 @@ public class BluetoothGattCharacteristic implements Parcelable { case FORMAT_SFLOAT: mantissa = intToSignedBits(mantissa, 12); exponent = intToSignedBits(exponent, 4); - mValue[offset++] = (byte)(mantissa & 0xFF); - mValue[offset] = (byte)((mantissa >> 8) & 0x0F); - mValue[offset] += (byte)((exponent & 0x0F) << 4); + mValue[offset++] = (byte) (mantissa & 0xFF); + mValue[offset] = (byte) ((mantissa >> 8) & 0x0F); + mValue[offset] += (byte) ((exponent & 0x0F) << 4); break; case FORMAT_FLOAT: mantissa = intToSignedBits(mantissa, 24); exponent = intToSignedBits(exponent, 8); - mValue[offset++] = (byte)(mantissa & 0xFF); - mValue[offset++] = (byte)((mantissa >> 8) & 0xFF); - mValue[offset++] = (byte)((mantissa >> 16) & 0xFF); - mValue[offset] += (byte)(exponent & 0xFF); + mValue[offset++] = (byte) (mantissa & 0xFF); + mValue[offset++] = (byte) ((mantissa >> 8) & 0xFF); + mValue[offset++] = (byte) ((mantissa >> 16) & 0xFF); + mValue[offset] += (byte) (exponent & 0xFF); break; default: @@ -717,7 +727,7 @@ public class BluetoothGattCharacteristic implements Parcelable { */ private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) - + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24); + + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24); } /** @@ -725,9 +735,9 @@ public class BluetoothGattCharacteristic implements Parcelable { */ private float bytesToFloat(byte b0, byte b1) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) - + ((unsignedByteToInt(b1) & 0x0F) << 8), 12); + + ((unsignedByteToInt(b1) & 0x0F) << 8), 12); int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4); - return (float)(mantissa * Math.pow(10, exponent)); + return (float) (mantissa * Math.pow(10, exponent)); } /** @@ -735,9 +745,9 @@ public class BluetoothGattCharacteristic implements Parcelable { */ private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) - + (unsignedByteToInt(b1) << 8) - + (unsignedByteToInt(b2) << 16), 24); - return (float)(mantissa * Math.pow(10, b3)); + + (unsignedByteToInt(b1) << 8) + + (unsignedByteToInt(b2) << 16), 24); + return (float) (mantissa * Math.pow(10, b3)); } /** @@ -745,8 +755,8 @@ public class BluetoothGattCharacteristic implements Parcelable { * signed value. */ private int unsignedToSigned(int unsigned, int size) { - if ((unsigned & (1 << size-1)) != 0) { - unsigned = -1 * ((1 << size-1) - (unsigned & ((1 << size-1) - 1))); + if ((unsigned & (1 << size - 1)) != 0) { + unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1))); } return unsigned; } @@ -756,7 +766,7 @@ public class BluetoothGattCharacteristic implements Parcelable { */ private int intToSignedBits(int i, int size) { if (i < 0) { - i = (1 << size-1) + (i & ((1 << size-1) - 1)); + i = (1 << size - 1) + (i & ((1 << size - 1) - 1)); } return i; } diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 1a4fa487a4d..8f6eb68c3e7 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -17,8 +17,9 @@ package android.bluetooth; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Parcelable; + import java.util.UUID; /** @@ -89,30 +90,35 @@ public class BluetoothGattDescriptor implements Parcelable { /** * The UUID of this descriptor. + * * @hide */ protected UUID mUuid; /** * Instance ID for this descriptor. + * * @hide */ protected int mInstance; /** * Permissions for this descriptor + * * @hide */ protected int mPermissions; /** * Back-reference to the characteristic this descriptor belongs to. + * * @hide */ protected BluetoothGattCharacteristic mCharacteristic; /** * The value for this descriptor. + * * @hide */ protected byte[] mValue; @@ -137,7 +143,7 @@ public class BluetoothGattDescriptor implements Parcelable { * @param permissions Permissions for this descriptor */ /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int instance, int permissions) { + int instance, int permissions) { initDescriptor(characteristic, uuid, instance, permissions); } @@ -149,7 +155,7 @@ public class BluetoothGattDescriptor implements Parcelable { } private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int instance, int permissions) { + int instance, int permissions) { mCharacteristic = characteristic; mUuid = uuid; mInstance = instance; @@ -181,13 +187,14 @@ public class BluetoothGattDescriptor implements Parcelable { }; private BluetoothGattDescriptor(Parcel in) { - mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstance = in.readInt(); mPermissions = in.readInt(); } /** * Returns the characteristic this descriptor belongs to. + * * @return The characteristic. */ public BluetoothGattCharacteristic getCharacteristic() { @@ -196,6 +203,7 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Set the back-reference to the associated characteristic + * * @hide */ /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { @@ -228,6 +236,7 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Force the instance ID. + * * @hide */ public void setInstanceId(int instanceId) { @@ -266,8 +275,8 @@ public class BluetoothGattDescriptor implements Parcelable { * remote device. * * @param value New value for this descriptor - * @return true if the locally stored value has been set, false if the - * requested value could not be stored locally. + * @return true if the locally stored value has been set, false if the requested value could not + * be stored locally. */ public boolean setValue(byte[] value) { mValue = value; diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.java b/framework/java/android/bluetooth/BluetoothGattIncludedService.java index 155dc571d23..2a42a789b28 100644 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.java +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.java @@ -16,14 +16,14 @@ package android.bluetooth; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; -import java.util.ArrayList; -import java.util.List; +import android.os.Parcelable; + import java.util.UUID; /** * Represents a Bluetooth GATT Included Service + * * @hide */ public class BluetoothGattIncludedService implements Parcelable { @@ -60,7 +60,7 @@ public class BluetoothGattIncludedService implements Parcelable { out.writeParcelable(new ParcelUuid(mUuid), 0); out.writeInt(mInstanceId); out.writeInt(mServiceType); - } + } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -74,7 +74,7 @@ public class BluetoothGattIncludedService implements Parcelable { }; private BluetoothGattIncludedService(Parcel in) { - mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstanceId = in.readInt(); mServiceType = in.readInt(); } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index eddc278851f..7b86a172834 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -16,10 +16,6 @@ package android.bluetooth; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; -import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -60,300 +56,322 @@ public final class BluetoothGattServer implements BluetoothProfile { * Bluetooth GATT interface callbacks */ private final IBluetoothGattServerCallback mBluetoothGattServerCallback = - new IBluetoothGattServerCallback.Stub() { - /** - * Application interface registered - app is ready to go - * @hide - */ - @Override - public void onServerRegistered(int status, int serverIf) { - if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status - + " serverIf=" + serverIf); - synchronized(mServerIfLock) { - if (mCallback != null) { - mServerIf = serverIf; - mServerIfLock.notify(); - } else { - // registration timeout - Log.e(TAG, "onServerRegistered: mCallback is null"); + new IBluetoothGattServerCallback.Stub() { + /** + * Application interface registered - app is ready to go + * @hide + */ + @Override + public void onServerRegistered(int status, int serverIf) { + if (DBG) { + Log.d(TAG, "onServerRegistered() - status=" + status + + " serverIf=" + serverIf); + } + synchronized (mServerIfLock) { + if (mCallback != null) { + mServerIf = serverIf; + mServerIfLock.notify(); + } else { + // registration timeout + Log.e(TAG, "onServerRegistered: mCallback is null"); + } } } - } - /** - * Server connection state changed - * @hide - */ - @Override - public void onServerConnectionState(int status, int serverIf, - boolean connected, String address) { - if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status - + " serverIf=" + serverIf + " device=" + address); - try { - mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, - connected ? BluetoothProfile.STATE_CONNECTED : - BluetoothProfile.STATE_DISCONNECTED); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); + /** + * Server connection state changed + * @hide + */ + @Override + public void onServerConnectionState(int status, int serverIf, + boolean connected, String address) { + if (DBG) { + Log.d(TAG, "onServerConnectionState() - status=" + status + + " serverIf=" + serverIf + " device=" + address); + } + try { + mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, + connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - } - /** - * Service has been added - * @hide - */ - @Override - public void onServiceAdded(int status, BluetoothGattService service) { - if (DBG) Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId() - + " uuid=" + service.getUuid() + " status=" + status); - - if (mPendingService == null) - return; - - BluetoothGattService tmp = mPendingService; - mPendingService = null; - - // Rewrite newly assigned handles to existing service. - tmp.setInstanceId(service.getInstanceId()); - List temp_chars = tmp.getCharacteristics(); - List svc_chars = service.getCharacteristics(); - for (int i=0; i temp_descs = temp_char.getDescriptors(); - List svc_descs = svc_char.getDescriptors(); - for (int j=0; j temp_chars = tmp.getCharacteristics(); + List svc_chars = service.getCharacteristics(); + for (int i = 0; i < svc_chars.size(); i++) { + BluetoothGattCharacteristic temp_char = temp_chars.get(i); + BluetoothGattCharacteristic svc_char = svc_chars.get(i); - try { - mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } + temp_char.setInstanceId(svc_char.getInstanceId()); - /** - * Remote client descriptor read request. - * @hide - */ - @Override - public void onDescriptorReadRequest(String address, int transId, - int offset, boolean isLong, int handle) { - if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); - if (descriptor == null) { - Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle); - return; - } + List temp_descs = temp_char.getDescriptors(); + List svc_descs = svc_char.getDescriptors(); + for (int j = 0; j < svc_descs.size(); j++) { + temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId()); + } + } + + mServices.add(tmp); - try { - mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); + try { + mCallback.onServiceAdded((int) status, tmp); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - } - /** - * Remote client characteristic write request. - * @hide - */ - @Override - public void onCharacteristicWriteRequest(String address, int transId, - int offset, int length, boolean isPrep, boolean needRsp, - int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); - if (characteristic == null) { - Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle); - return; + /** + * Remote client characteristic read request. + * @hide + */ + @Override + public void onCharacteristicReadRequest(String address, int transId, + int offset, boolean isLong, int handle) { + if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); + if (characteristic == null) { + Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle); + return; + } + + try { + mCallback.onCharacteristicReadRequest(device, transId, offset, + characteristic); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - try { - mCallback.onCharacteristicWriteRequest(device, transId, characteristic, - isPrep, needRsp, offset, value); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); + /** + * Remote client descriptor read request. + * @hide + */ + @Override + public void onDescriptorReadRequest(String address, int transId, + int offset, boolean isLong, int handle) { + if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); + if (descriptor == null) { + Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle); + return; + } + + try { + mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - } + /** + * Remote client characteristic write request. + * @hide + */ + @Override + public void onCharacteristicWriteRequest(String address, int transId, + int offset, int length, boolean isPrep, boolean needRsp, + int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); + if (characteristic == null) { + Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle); + return; + } + + try { + mCallback.onCharacteristicWriteRequest(device, transId, characteristic, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } - /** - * Remote client descriptor write request. - * @hide - */ - @Override - public void onDescriptorWriteRequest(String address, int transId, int offset, - int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); - if (descriptor == null) { - Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle); - return; } - try { - mCallback.onDescriptorWriteRequest(device, transId, descriptor, - isPrep, needRsp, offset, value); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); + /** + * Remote client descriptor write request. + * @hide + */ + @Override + public void onDescriptorWriteRequest(String address, int transId, int offset, + int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { + if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); + if (descriptor == null) { + Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle); + return; + } + + try { + mCallback.onDescriptorWriteRequest(device, transId, descriptor, + isPrep, needRsp, offset, value); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - } - /** - * Execute pending writes. - * @hide - */ - @Override - public void onExecuteWrite(String address, int transId, - boolean execWrite) { - if (DBG) Log.d(TAG, "onExecuteWrite() - " - + "device=" + address + ", transId=" + transId - + "execWrite=" + execWrite); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onExecuteWrite(device, transId, execWrite); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); + /** + * Execute pending writes. + * @hide + */ + @Override + public void onExecuteWrite(String address, int transId, + boolean execWrite) { + if (DBG) { + Log.d(TAG, "onExecuteWrite() - " + + "device=" + address + ", transId=" + transId + + "execWrite=" + execWrite); + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onExecuteWrite(device, transId, execWrite); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } - } - /** - * A notification/indication has been sent. - * @hide - */ - @Override - public void onNotificationSent(String address, int status) { - if (VDBG) Log.d(TAG, "onNotificationSent() - " - + "device=" + address + ", status=" + status); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onNotificationSent(device, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + /** + * A notification/indication has been sent. + * @hide + */ + @Override + public void onNotificationSent(String address, int status) { + if (VDBG) { + Log.d(TAG, "onNotificationSent() - " + + "device=" + address + ", status=" + status); + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onNotificationSent(device, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } } - } - /** - * The MTU for a connection has changed - * @hide - */ - @Override - public void onMtuChanged(String address, int mtu) { - if (DBG) Log.d(TAG, "onMtuChanged() - " - + "device=" + address + ", mtu=" + mtu); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onMtuChanged(device, mtu); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + /** + * The MTU for a connection has changed + * @hide + */ + @Override + public void onMtuChanged(String address, int mtu) { + if (DBG) { + Log.d(TAG, "onMtuChanged() - " + + "device=" + address + ", mtu=" + mtu); + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onMtuChanged(device, mtu); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } } - } - /** - * The PHY for a connection was updated - * @hide - */ - @Override - public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { - if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy - + ", rxPHy=" + rxPhy); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onPhyUpdate(device, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + /** + * The PHY for a connection was updated + * @hide + */ + @Override + public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { + if (DBG) { + Log.d(TAG, + "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyUpdate(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } } - } - /** - * The PHY for a connection was read - * @hide - */ - @Override - public void onPhyRead(String address, int txPhy, int rxPhy, int status) { - if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy - + ", rxPHy=" + rxPhy); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onPhyRead(device, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + /** + * The PHY for a connection was read + * @hide + */ + @Override + public void onPhyRead(String address, int txPhy, int rxPhy, int status) { + if (DBG) { + Log.d(TAG, + "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy + + ", rxPHy=" + rxPhy); + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onPhyRead(device, txPhy, rxPhy, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } } - } - /** - * Callback invoked when the given connection is updated - * @hide - */ - @Override - public void onConnectionUpdated(String address, int interval, int latency, - int timeout, int status) { - if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address + - " interval=" + interval + " latency=" + latency + - " timeout=" + timeout + " status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onConnectionUpdated(device, interval, latency, - timeout, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + /** + * Callback invoked when the given connection is updated + * @hide + */ + @Override + public void onConnectionUpdated(String address, int interval, int latency, + int timeout, int status) { + if (DBG) { + Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); + } + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (device == null) return; + + try { + mCallback.onConnectionUpdated(device, interval, latency, + timeout, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } } - } - }; + }; /** * Create a BluetoothGattServer proxy object. @@ -369,13 +387,15 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Returns a characteristic with given handle. + * * @hide */ /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) { - for(BluetoothGattService svc : mServices) { - for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - if (charac.getInstanceId() == handle) + for (BluetoothGattService svc : mServices) { + for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + if (charac.getInstanceId() == handle) { return charac; + } } } return null; @@ -383,14 +403,16 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Returns a descriptor with given handle. + * * @hide */ /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) { - for(BluetoothGattService svc : mServices) { - for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - for(BluetoothGattDescriptor desc : charac.getDescriptors()) { - if (desc.getInstanceId() == handle) + for (BluetoothGattService svc : mServices) { + for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { + for (BluetoothGattDescriptor desc : charac.getDescriptors()) { + if (desc.getInstanceId() == handle) { return desc; + } } } } @@ -416,10 +438,9 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param callback GATT callback handler that will receive asynchronous - * callbacks. - * @return true, the callback will be called to notify success or failure, - * false on immediate error + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @return true, the callback will be called to notify success or failure, false on immediate + * error */ /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { if (DBG) Log.d(TAG, "registerCallback()"); @@ -430,7 +451,7 @@ public final class BluetoothGattServer implements BluetoothProfile { UUID uuid = UUID.randomUUID(); if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - synchronized(mServerIfLock) { + synchronized (mServerIfLock) { if (mCallback != null) { Log.e(TAG, "App can register callback only once"); return false; @@ -440,7 +461,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); mCallback = null; return false; } @@ -473,19 +494,20 @@ public final class BluetoothGattServer implements BluetoothProfile { mService.unregisterServer(mServerIf); mServerIf = 0; } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } /** * Returns a service by UUID, instance and type. + * * @hide */ /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { - for(BluetoothGattService svc : mServices) { + for (BluetoothGattService svc : mServices) { if (svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { return svc; } } @@ -509,20 +531,22 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param autoConnect Whether to directly connect to the remote device (false) - * or to automatically connect as soon as the remote - * device becomes available (true). + * @param autoConnect Whether to directly connect to the remote device (false) or to + * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ public boolean connect(BluetoothDevice device, boolean autoConnect) { - if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); + if (DBG) { + Log.d(TAG, + "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); + } if (mService == null || mServerIf == 0) return false; try { mService.serverConnect(mServerIf, device.getAddress(), - autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" + autoConnect ? false : true, mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -544,35 +568,34 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.serverDisconnect(mServerIf, device.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } /** * Set the preferred connection PHY for this app. Please note that this is just a * recommendation, whether the PHY change will happen depends on other applications peferences, - * local and remote controller capabilities. Controller can override these settings. - *

        - * {@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even - * if no PHY change happens. It is also triggered when remote device updates the PHY. + * local and remote controller capabilities. Controller can override these settings.

        {@link + * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if + * no PHY change happens. It is also triggered when remote device updates the PHY. * * @param device The remote device to send this response to - * @param txPhy preferred transmitter PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. - * @param rxPhy preferred receiver PHY. Bitwise OR of any of - * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, - * and {@link BluetoothDevice#PHY_LE_CODED_MASK}. + * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link + * BluetoothDevice#PHY_LE_CODED_MASK}. + * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link + * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link + * BluetoothDevice#PHY_LE_CODED_MASK}. * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one - * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, - * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8} + * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or + * {@link BluetoothDevice#PHY_OPTION_S8} */ public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { try { mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -586,7 +609,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.serverReadPhy(mServerIf, device.getAddress()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -597,10 +620,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * is received by one of these callback methods: * *

          - *
        • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} - *
        • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorReadRequest} - *
        • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} + *
        • {@link BluetoothGattServerCallback#onCharacteristicReadRequest} + *
        • {@link BluetoothGattServerCallback#onCharacteristicWriteRequest} + *
        • {@link BluetoothGattServerCallback#onDescriptorReadRequest} + *
        • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} *
        * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -612,15 +635,15 @@ public final class BluetoothGattServer implements BluetoothProfile { * @param value The value of the attribute that was read/written (optional) */ public boolean sendResponse(BluetoothDevice device, int requestId, - int status, int offset, byte[] value) { + int status, int offset, byte[] value) { if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); if (mService == null || mServerIf == 0) return false; try { mService.sendResponse(mServerIf, device.getAddress(), requestId, - status, offset, value); + status, offset, value); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } return true; @@ -639,13 +662,13 @@ public final class BluetoothGattServer implements BluetoothProfile { * * @param device The remote device to receive the notification/indication * @param characteristic The local characteristic that has been updated - * @param confirm true to request confirmation from the client (indication), - * false to send a notification - * @throws IllegalArgumentException + * @param confirm true to request confirmation from the client (indication), false to send a + * notification * @return true, if the notification has been triggered successfully + * @throws IllegalArgumentException */ public boolean notifyCharacteristicChanged(BluetoothDevice device, - BluetoothGattCharacteristic characteristic, boolean confirm) { + BluetoothGattCharacteristic characteristic, boolean confirm) { if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); if (mService == null || mServerIf == 0) return false; @@ -662,7 +685,7 @@ public final class BluetoothGattServer implements BluetoothProfile { characteristic.getInstanceId(), confirm, characteristic.getValue()); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -680,8 +703,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param service Service to be added to the list of services provided - * by this device. + * @param service Service to be added to the list of services provided by this device. * @return true, if the service has been added successfully */ public boolean addService(BluetoothGattService service) { @@ -693,7 +715,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.addService(mServerIf, service); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -713,14 +735,14 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return false; BluetoothGattService intService = getService(service.getUuid(), - service.getInstanceId(), service.getType()); + service.getInstanceId(), service.getType()); if (intService == null) return false; try { mService.removeService(mServerIf, service.getInstanceId()); mServices.remove(intService); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return false; } @@ -739,7 +761,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mService.clearServices(mServerIf); mServices.clear(); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -751,8 +773,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @return List of services. Returns an empty list - * if no services have been added yet. + * @return List of services. Returns an empty list if no services have been added yet. */ public List getServices() { return mServices; @@ -768,8 +789,8 @@ public final class BluetoothGattServer implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param uuid UUID of the requested service - * @return BluetoothGattService if supported, or null if the requested - * service is not offered by this device. + * @return BluetoothGattService if supported, or null if the requested service is not offered by + * this device. */ public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { @@ -802,7 +823,7 @@ public final class BluetoothGattServer implements BluetoothProfile { @Override public List getConnectedDevices() { throw new UnsupportedOperationException - ("Use BluetoothManager#getConnectedDevices instead."); + ("Use BluetoothManager#getConnectedDevices instead."); } /** @@ -815,6 +836,6 @@ public final class BluetoothGattServer implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { throw new UnsupportedOperationException - ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 02307bd9ef9..e72577d1fa0 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -16,8 +16,6 @@ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. */ @@ -28,19 +26,18 @@ public abstract class BluetoothGattServerCallback { * * @param device Remote device that has been connected or disconnected. * @param status Status of the connect or disconnect operation. - * @param newState Returns the new connection state. Can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED} or - * {@link BluetoothProfile#STATE_CONNECTED} + * @param newState Returns the new connection state. Can be one of {@link + * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED} */ public void onConnectionStateChange(BluetoothDevice device, int status, - int newState) { + int newState) { } /** * Indicates whether a local service has been added successfully. * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service - * was added successfully. + * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service was added + * successfully. * @param service The service that has been added */ public void onServiceAdded(int status, BluetoothGattService service) { @@ -58,7 +55,7 @@ public abstract class BluetoothGattServerCallback { * @param characteristic Characteristic to be read */ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) { + int offset, BluetoothGattCharacteristic characteristic) { } /** @@ -70,16 +67,15 @@ public abstract class BluetoothGattServerCallback { * @param device The remote device that has requested the write operation * @param requestId The Id of the request * @param characteristic Characteristic to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. + * @param preparedWrite true, if this write operation should be queued for later execution. * @param responseNeeded true, if the remote device requires a response * @param offset The offset given for the value * @param value The value the client wants to assign to the characteristic */ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** @@ -94,7 +90,7 @@ public abstract class BluetoothGattServerCallback { * @param descriptor Descriptor to be read */ public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { + int offset, BluetoothGattDescriptor descriptor) { } /** @@ -106,16 +102,15 @@ public abstract class BluetoothGattServerCallback { * @param device The remote device that has requested the write operation * @param requestId The Id of the request * @param descriptor Descriptor to be written to. - * @param preparedWrite true, if this write operation should be queued for - * later execution. + * @param preparedWrite true, if this write operation should be queued for later execution. * @param responseNeeded true, if the remote device requires a response * @param offset The offset given for the value * @param value The value the client wants to assign to the descriptor */ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** @@ -126,8 +121,7 @@ public abstract class BluetoothGattServerCallback { * * @param device The remote device that has requested the write operations * @param requestId The Id of the request - * @param execute Whether the pending writes should be executed (true) or - * cancelled (false) + * @param execute Whether the pending writes should be executed (true) or cancelled (false) */ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { } @@ -163,12 +157,12 @@ public abstract class BluetoothGattServerCallback { * of remote device changing the PHY. * * @param device The remote device - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status Status of the PHY update operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { } @@ -177,12 +171,12 @@ public abstract class BluetoothGattServerCallback { * Callback triggered as result of {@link BluetoothGattServer#readPhy} * * @param device The remote device that requested the PHY read - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status Status of the PHY read operation. - * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds. + * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} + * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { } @@ -191,18 +185,18 @@ public abstract class BluetoothGattServerCallback { * Callback indicating the connection parameters were updated. * * @param device The remote device involved - * @param interval Connection interval used on this connection, 1.25ms unit. Valid - * range is from 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid - * range is from 0 to 499 - * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is - * from 10 (0.1s) to 3200 (32s) + * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from + * 6 (7.5ms) to 3200 (4000ms). + * @param latency Slave latency for the connection in number of connection events. Valid range + * is from 0 to 499 + * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 + * (0.1s) to 3200 (32s) * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated - * successfully + * successfully * @hide */ public void onConnectionUpdated(BluetoothDevice gatt, int interval, int latency, int timeout, - int status) { + int status) { } } diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index c888a451e9e..db820d874ad 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,8 +16,9 @@ package android.bluetooth; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Parcelable; + import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -44,30 +45,35 @@ public class BluetoothGattService implements Parcelable { /** * The remote device his service is associated with. * This applies to client applications only. + * * @hide */ protected BluetoothDevice mDevice; /** * The UUID of this service. + * * @hide */ protected UUID mUuid; /** * Instance ID for this service. + * * @hide */ protected int mInstanceId; /** * Handle counter override (for conformance testing). + * * @hide */ protected int mHandles = 0; /** * Service type (Primary/Secondary). + * * @hide */ protected int mServiceType; @@ -93,8 +99,8 @@ public class BluetoothGattService implements Parcelable { * * @param uuid The UUID for this service * @param serviceType The type of this service, - * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or - * {@link BluetoothGattService#SERVICE_TYPE_SECONDARY} + * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} + * or {@link BluetoothGattService#SERVICE_TYPE_SECONDARY} */ public BluetoothGattService(UUID uuid, int serviceType) { mDevice = null; @@ -107,10 +113,11 @@ public class BluetoothGattService implements Parcelable { /** * Create a new BluetoothGattService + * * @hide */ /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid, - int instanceId, int serviceType) { + int instanceId, int serviceType) { mDevice = device; mUuid = uuid; mInstanceId = instanceId; @@ -121,6 +128,7 @@ public class BluetoothGattService implements Parcelable { /** * Create a new BluetoothGattService + * * @hide */ public BluetoothGattService(UUID uuid, int instanceId, int serviceType) { @@ -148,12 +156,12 @@ public class BluetoothGattService implements Parcelable { ArrayList includedServices = new ArrayList(mIncludedServices.size()); - for(BluetoothGattService s : mIncludedServices) { + for (BluetoothGattService s : mIncludedServices) { includedServices.add(new BluetoothGattIncludedService(s.getUuid(), - s.getInstanceId(), s.getType())); + s.getInstanceId(), s.getType())); } out.writeTypedList(includedServices); - } + } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -167,7 +175,7 @@ public class BluetoothGattService implements Parcelable { }; private BluetoothGattService(Parcel in) { - mUuid = ((ParcelUuid)in.readParcelable(null)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstanceId = in.readInt(); mServiceType = in.readInt(); @@ -189,13 +197,14 @@ public class BluetoothGattService implements Parcelable { if (chrcs != null) { for (BluetoothGattIncludedService isvc : inclSvcs) { mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(), - isvc.getInstanceId(), isvc.getType())); + isvc.getInstanceId(), isvc.getType())); } } } /** * Returns the device associated with this service. + * * @hide */ /*package*/ BluetoothDevice getDevice() { @@ -204,6 +213,7 @@ public class BluetoothGattService implements Parcelable { /** * Returns the device associated with this service. + * * @hide */ /*package*/ void setDevice(BluetoothDevice device) { @@ -237,19 +247,22 @@ public class BluetoothGattService implements Parcelable { /** * Get characteristic by UUID and instanceId. + * * @hide */ /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) { - for(BluetoothGattCharacteristic characteristic : mCharacteristics) { + for (BluetoothGattCharacteristic characteristic : mCharacteristics) { if (uuid.equals(characteristic.getUuid()) - && characteristic.getInstanceId() == instanceId) + && characteristic.getInstanceId() == instanceId) { return characteristic; + } } return null; } /** * Force the instance ID. + * * @hide */ public void setInstanceId(int instanceId) { @@ -258,6 +271,7 @@ public class BluetoothGattService implements Parcelable { /** * Get the handle count override (conformance testing. + * * @hide */ /*package*/ int getHandles() { @@ -267,6 +281,7 @@ public class BluetoothGattService implements Parcelable { /** * Force the number of handles to reserve for this service. * This is needed for conformance testing only. + * * @hide */ public void setHandles(int handles) { @@ -275,6 +290,7 @@ public class BluetoothGattService implements Parcelable { /** * Add an included service to the internal map. + * * @hide */ public void addIncludedService(BluetoothGattService includedService) { @@ -313,8 +329,7 @@ public class BluetoothGattService implements Parcelable { /** * Get the list of included GATT services for this service. * - * @return List of included services or empty list if no included services - * were discovered. + * @return List of included services or empty list if no included services were discovered. */ public List getIncludedServices() { return mIncludedServices; @@ -341,30 +356,33 @@ public class BluetoothGattService implements Parcelable { * UUID, the first instance of a characteristic with the given UUID * is returned. * - * @return GATT characteristic object or null if no characteristic with the - * given UUID was found. + * @return GATT characteristic object or null if no characteristic with the given UUID was + * found. */ public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { - for(BluetoothGattCharacteristic characteristic : mCharacteristics) { - if (uuid.equals(characteristic.getUuid())) + for (BluetoothGattCharacteristic characteristic : mCharacteristics) { + if (uuid.equals(characteristic.getUuid())) { return characteristic; + } } return null; } /** * Returns whether the uuid of the service should be advertised. + * * @hide */ public boolean isAdvertisePreferred() { - return mAdvertisePreferred; + return mAdvertisePreferred; } /** * Set whether the service uuid should be advertised. + * * @hide */ public void setAdvertisePreferred(boolean advertisePreferred) { - this.mAdvertisePreferred = advertisePreferred; + this.mAdvertisePreferred = advertisePreferred; } } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c84643fc46b..132f383997b 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -56,9 +56,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, @@ -69,7 +69,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent used to broadcast the change in the Audio Connection state of the @@ -77,9 +77,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, @@ -89,7 +89,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; + "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; /** @@ -98,19 +98,19 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 4 extras and 1 category. *

          - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device - *
        • - *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor - * specific command
        • - *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT - * command type which can be one of {@link #AT_CMD_TYPE_READ}, - * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, - * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}.
        • - *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command - * arguments.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device + *
        • + *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor + * specific command
        • + *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT + * command type which can be one of {@link #AT_CMD_TYPE_READ}, + * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, + * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}.
        • + *
        • {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command + * arguments.
        • *
        * - *

        The category is the Company ID of the vendor defining the + *

        The category is the Company ID of the vendor defining the * vendor-specific command. {@link BluetoothAssignedNumbers} * * For example, for Plantronics specific events @@ -118,9 +118,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        For example, an AT+XEVENT=foo,3 will get translated into *

          - *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
        • - *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
        • - *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
        • + *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
        • + *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
        • + *
        • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
        • *
        *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission * to receive. @@ -191,7 +191,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} * for the companyId */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = + public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid"; /** @@ -201,12 +201,14 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * A vendor-specific AT command + * * @hide */ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL"; /** * A vendor-specific AT command + * * @hide */ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV"; @@ -214,18 +216,21 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Battery level indicator associated with * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV} + * * @hide */ public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1; /** * A vendor-specific AT command + * * @hide */ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT"; /** * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT} + * * @hide */ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY"; @@ -258,17 +263,18 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which - * is supported by the headset ( as indicated by AT+BIND command in the SLC - * sequence) or whose value is changed (indicated by AT+BIEV command)
        • - *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - Remote device.
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which + * is supported by the headset ( as indicated by AT+BIND command in the SLC + * sequence) or whose value is changed (indicated by AT+BIEV command)
        • + *
        • {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - Remote device.
        • *
        *

        {@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators - * are given an assigned number. Below shows the assigned number of Indicator added so far + * are given an assigned number. Below shows the assigned number of Indicator added so far * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. + * * @hide */ public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = @@ -278,6 +284,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} * intents that contains the assigned number of the headset indicator as defined by * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7 + * * @hide */ public static final String EXTRA_HF_INDICATORS_IND_ID = @@ -286,6 +293,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} * intents that contains the value of the Headset indicator that is being sent. + * * @hide */ public static final String EXTRA_HF_INDICATORS_IND_VALUE = @@ -306,22 +314,22 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); doUnbind(); } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothHeadset proxy object. @@ -336,7 +344,7 @@ public final class BluetoothHeadset implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -360,7 +368,7 @@ public final class BluetoothHeadset implements BluetoothProfile { mAdapter.getBluetoothManager().unbindBluetoothProfileService( BluetoothProfile.HEADSET, mConnection); } catch (RemoteException e) { - Log.e(TAG,"Unable to unbind HeadsetService", e); + Log.e(TAG, "Unable to unbind HeadsetService", e); } } } @@ -380,7 +388,7 @@ public final class BluetoothHeadset implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } mServiceListener = null; @@ -405,14 +413,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { @@ -446,19 +453,18 @@ public final class BluetoothHeadset implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -505,7 +511,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -521,7 +527,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * Set priority of the profile * *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} @@ -535,10 +541,10 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + priority != BluetoothProfile.PRIORITY_ON) { + return false; } try { return mService.setPriority(device, priority); @@ -567,7 +573,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -596,18 +602,17 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected of if the - * connected headset doesn't support voice recognition - * or on error, true otherwise + * @return false if there is no headset connected of if the connected headset doesn't support + * voice recognition or on error, true otherwise */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.startVoiceRecognition(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -621,17 +626,16 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected - * or on error, true otherwise + * @return false if there is no headset connected or on error, true otherwise */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.stopVoiceRecognition(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -644,17 +648,16 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return true if SCO is connected, - * false otherwise or on error + * @return true if SCO is connected, false otherwise or on error */ public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { - return mService.isAudioConnected(device); + return mService.isAudioConnected(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -671,18 +674,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms * * @param device the bluetooth headset. - * @return monotonically increasing battery usage hint, or a negative error - * code on error + * @return monotonically increasing battery usage hint, or a negative error code on error * @hide */ public int getBatteryUsageHint(BluetoothDevice device) { if (VDBG) log("getBatteryUsageHint()"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getBatteryUsageHint(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -711,7 +713,9 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && isEnabled()) { try { return mService.acceptIncomingConnect(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -721,6 +725,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Reject the incoming connection. + * * @hide */ public boolean rejectIncomingConnect(BluetoothDevice device) { @@ -728,7 +733,9 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null) { try { return mService.rejectIncomingConnect(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -747,7 +754,9 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && !isDisabled()) { try { return mService.getAudioState(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -763,7 +772,6 @@ public final class BluetoothHeadset implements BluetoothProfile { * Note: This is an internal function and shouldn't be exposed * * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. - * * @hide */ public void setAudioRouteAllowed(boolean allowed) { @@ -771,7 +779,9 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && isEnabled()) { try { mService.setAudioRouteAllowed(allowed); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -789,7 +799,9 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mService != null && isEnabled()) { try { return mService.getAudioRouteAllowed(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -800,9 +812,8 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Force SCO audio to be opened regardless any other restrictions * - * @param forced Whether or not SCO audio connection should be forced: - * True to force SCO audio - * False to use SCO audio in normal manner + * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio + * False to use SCO audio in normal manner * @hide */ public void setForceScoAudio(boolean forced) { @@ -811,7 +822,7 @@ public final class BluetoothHeadset implements BluetoothProfile { try { mService.setForceScoAudio(forced); } catch (RemoteException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); } } else { Log.w(TAG, "Proxy not attached to service"); @@ -824,17 +835,16 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @return true if SCO is connected, - * false otherwise or on error + * @return true if SCO is connected, false otherwise or on error * @hide */ public boolean isAudioOn() { if (VDBG) log("isAudioOn()"); if (mService != null && isEnabled()) { try { - return mService.isAudioOn(); + return mService.isAudioOn(); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -846,9 +856,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * Initiates a connection of headset audio. * It setup SCO channel with remote connected headset device. * - * @return true if successful - * false if there was some error such as - * there is no connected headset + * @return true if successful false if there was some error such as there is no connected + * headset * @hide */ public boolean connectAudio() { @@ -869,9 +878,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * Initiates a disconnection of headset audio. * It tears down the SCO channel from remote headset device. * - * @return true if successful - * false if there was some error such as - * there is no connected SCO channel + * @return true if successful false if there was some error such as there is no connected SCO + * channel * @hide */ public boolean disconnectAudio() { @@ -946,7 +954,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public void phoneStateChanged(int numActive, int numHeld, int callState, String number, - int type) { + int type) { if (mService != null && isEnabled()) { try { mService.phoneStateChanged(numActive, numHeld, callState, number, type); @@ -965,7 +973,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type) { + String number, int type) { if (mService != null && isEnabled()) { try { mService.clccResponse(index, direction, status, mode, mpty, number, type); @@ -981,9 +989,9 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Sends a vendor-specific unsolicited result code to the headset. * - *

        The actual string to be sent is command + ": " + arg. - * For example, if {@code command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} - * is {@code "0"}, the string "+ANDROID: 0" will be sent. + *

        The actual string to be sent is command + ": " + arg. For example, if {@code + * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the + * string "+ANDROID: 0" will be sent. * *

        Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. * @@ -993,7 +1001,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @param command A vendor-specific command. * @param arg The argument that will be attached to the command. * @return {@code false} if there is no headset connected, or if the command is not an allowed - * vendor-specific unsolicited result code, or on error. {@code true} otherwise. + * vendor-specific unsolicited result code, or on error. {@code true} otherwise. * @throws IllegalArgumentException if {@code command} is {@code null}. */ public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, @@ -1021,9 +1029,8 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * enable WBS codec setting. * - * @return true if successful - * false if there was some error such as - * there is no connected headset + * @return true if successful false if there was some error such as there is no connected + * headset * @hide */ public boolean enableWBS() { @@ -1043,9 +1050,8 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * disable WBS codec settting. It set NBS codec. * - * @return true if successful - * false if there was some error such as - * there is no connected headset + * @return true if successful false if there was some error such as there is no connected + * headset * @hide */ public boolean disableWBS() { @@ -1065,8 +1071,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * check if in-band ringing is supported for this platform. * - * @return true if in-band ringing is supported - * false if in-band ringing is not supported + * @return true if in-band ringing is supported false if in-band ringing is not supported * @hide */ public static boolean isInbandRingingSupported(Context context) { @@ -1079,9 +1084,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * HF indicators to the headset * * @param ind_id Assigned Number of the indicator (defined by SIG) - * @param ind_status - * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator - * true-Indicator is enabled, value changes may be sent for this indicator + * @param ind_status possible values- false-Indicator is disabled, no value changes shall be + * sent for this indicator true-Indicator is enabled, value changes may be sent for this + * indicator * @hide */ public void bindResponse(int ind_id, boolean ind_status) { @@ -1098,7 +1103,7 @@ public final class BluetoothHeadset implements BluetoothProfile { } private final IBluetoothProfileServiceConnection mConnection - = new IBluetoothProfileServiceConnection.Stub() { + = new IBluetoothProfileServiceConnection.Stub() { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); @@ -1106,6 +1111,7 @@ public final class BluetoothHeadset implements BluetoothProfile { mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_CONNECTED)); } + @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); @@ -1116,20 +1122,20 @@ public final class BluetoothHeadset implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isDisabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 544b3b95db0..c775cd739e5 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -28,7 +28,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; -import java.util.UUID; /** * Public API to control Hands Free Profile (HFP role only). @@ -38,7 +37,7 @@ import java.util.UUID; *

        * * @hide - * */ + */ public final class BluetoothHeadsetClient implements BluetoothProfile { private static final String TAG = "BluetoothHeadsetClient"; private static final boolean DBG = true; @@ -71,7 +70,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * and not supported ones are not being sent at all.

        */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; /** * Intent sent whenever audio state changes. @@ -89,7 +88,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * indicating wide band speech support.

        */ public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; + "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; /** * Intent sending updates of the Audio Gateway state. @@ -146,7 +145,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Extra with information if connected audio is WBS. *

        Possible values: true, - * false.

        + * false.

        */ public static final String EXTRA_AUDIO_WBS = "android.bluetooth.headsetclient.extra.AUDIO_WBS"; @@ -154,7 +153,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Extra for AG_EVENT indicates network status. *

        Value: 0 - network unavailable, - * 1 - network available

        + * 1 - network available

        */ public static final String EXTRA_NETWORK_STATUS = "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; @@ -167,7 +166,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Extra for AG_EVENT intent indicates roaming state. *

        Value: 0 - no roaming - * 1 - active roaming

        + * 1 - active roaming

        */ public static final String EXTRA_NETWORK_ROAMING = "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; @@ -186,16 +185,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Extra for AG_EVENT intent indicates voice recognition state. *

        Value: - * 0 - voice recognition stopped, - * 1 - voice recognition started.

        + * 0 - voice recognition stopped, + * 1 - voice recognition started.

        */ public static final String EXTRA_VOICE_RECOGNITION = "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; /** * Extra for AG_EVENT intent indicates in band ring state. *

        Value: - * 0 - in band ring tone not supported, or - * 1 - in band ring tone supported.

        + * 0 - in band ring tone not supported, or + * 1 - in band ring tone supported.

        */ public static final String EXTRA_IN_BAND_RING = "android.bluetooth.headsetclient.extra.IN_BAND_RING"; @@ -208,8 +207,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; /** - * Extra for AG_CALL_CHANGED intent indicates the - * {@link BluetoothHeadsetClientCall} object that has changed. + * Extra for AG_CALL_CHANGED intent indicates the + * {@link BluetoothHeadsetClientCall} object that has changed. */ public static final String EXTRA_CALL = "android.bluetooth.headsetclient.extra.CALL"; @@ -319,47 +318,47 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public final static int ACTION_RESULT_ERROR_CME = 7; /* Detailed CME error codes */ - public final static int CME_PHONE_FAILURE = 0; - public final static int CME_NO_CONNECTION_TO_PHONE = 1; - public final static int CME_OPERATION_NOT_ALLOWED = 3; - public final static int CME_OPERATION_NOT_SUPPORTED = 4; - public final static int CME_PHSIM_PIN_REQUIRED = 5; - public final static int CME_PHFSIM_PIN_REQUIRED = 6; - public final static int CME_PHFSIM_PUK_REQUIRED = 7; - public final static int CME_SIM_NOT_INSERTED = 10; - public final static int CME_SIM_PIN_REQUIRED = 11; - public final static int CME_SIM_PUK_REQUIRED = 12; - public final static int CME_SIM_FAILURE = 13; - public final static int CME_SIM_BUSY = 14; - public final static int CME_SIM_WRONG = 15; - public final static int CME_INCORRECT_PASSWORD = 16; - public final static int CME_SIM_PIN2_REQUIRED = 17; - public final static int CME_SIM_PUK2_REQUIRED = 18; - public final static int CME_MEMORY_FULL = 20; - public final static int CME_INVALID_INDEX = 21; - public final static int CME_NOT_FOUND = 22; - public final static int CME_MEMORY_FAILURE = 23; - public final static int CME_TEXT_STRING_TOO_LONG = 24; - public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; - public final static int CME_DIAL_STRING_TOO_LONG = 26; - public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; - public final static int CME_NO_NETWORK_SERVICE = 30; - public final static int CME_NETWORK_TIMEOUT = 31; - public final static int CME_EMERGENCY_SERVICE_ONLY = 32; - public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; - public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34; - public final static int CME_SIP_RESPONSE_CODE = 35; - public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; - public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; - public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; - public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; + public final static int CME_PHONE_FAILURE = 0; + public final static int CME_NO_CONNECTION_TO_PHONE = 1; + public final static int CME_OPERATION_NOT_ALLOWED = 3; + public final static int CME_OPERATION_NOT_SUPPORTED = 4; + public final static int CME_PHSIM_PIN_REQUIRED = 5; + public final static int CME_PHFSIM_PIN_REQUIRED = 6; + public final static int CME_PHFSIM_PUK_REQUIRED = 7; + public final static int CME_SIM_NOT_INSERTED = 10; + public final static int CME_SIM_PIN_REQUIRED = 11; + public final static int CME_SIM_PUK_REQUIRED = 12; + public final static int CME_SIM_FAILURE = 13; + public final static int CME_SIM_BUSY = 14; + public final static int CME_SIM_WRONG = 15; + public final static int CME_INCORRECT_PASSWORD = 16; + public final static int CME_SIM_PIN2_REQUIRED = 17; + public final static int CME_SIM_PUK2_REQUIRED = 18; + public final static int CME_MEMORY_FULL = 20; + public final static int CME_INVALID_INDEX = 21; + public final static int CME_NOT_FOUND = 22; + public final static int CME_MEMORY_FAILURE = 23; + public final static int CME_TEXT_STRING_TOO_LONG = 24; + public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; + public final static int CME_DIAL_STRING_TOO_LONG = 26; + public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; + public final static int CME_NO_NETWORK_SERVICE = 30; + public final static int CME_NETWORK_TIMEOUT = 31; + public final static int CME_EMERGENCY_SERVICE_ONLY = 32; + public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; + public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34; + public final static int CME_SIP_RESPONSE_CODE = 35; + public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; + public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; + public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; - public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; - public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; - public final static int CME_HIDDEN_KEY_REQUIRED = 48; - public final static int CME_EAP_NOT_SUPPORTED = 49; - public final static int CME_INCORRECT_PARAMETERS = 50; + public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; + public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; + public final static int CME_HIDDEN_KEY_REQUIRED = 48; + public final static int CME_EAP_NOT_SUPPORTED = 49; + public final static int CME_INCORRECT_PARAMETERS = 50; /* Action policy for other calls when accepting call */ public static final int CALL_ACCEPT_NONE = 0; @@ -377,30 +376,31 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); - Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); + if (VDBG) Log.d(TAG, "Binding service..."); + Intent intent = new Intent( + IBluetoothHeadsetClient.class.getName()); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. @@ -415,7 +415,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -427,7 +427,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - android.os.Process.myUserHandle())) { + android.os.Process.myUserHandle())) { Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); return false; } @@ -448,7 +448,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -458,7 +458,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -472,11 +472,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * second connection, this implementation will disconnect already connected * device automatically and will process the new one. * - * @param device a remote device we want connect to - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} - * intent. + * @param device a remote device we want connect to + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -496,11 +494,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Disconnects remote device * - * @param device a remote device we want disconnect - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} - * intent. + * @param device a remote device we want disconnect + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -509,8 +505,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.disconnect(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -540,10 +536,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns list of remote devices in a particular state * - * @param states collection of states - * @return list of devices that state matches the states listed in - * states; empty list if nothing matches the - * states + * @param states collection of states + * @return list of devices that state matches the states listed in states; empty + * list if nothing matches the states */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -563,8 +558,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns state of the device * - * @param device a remote device - * @return the state of connection of the device + * @param device a remote device + * @return the state of connection of the device */ @Override public int getConnectionState(BluetoothDevice device) { @@ -593,7 +588,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { - return false; + return false; } try { return mService.setPriority(device, priority); @@ -627,15 +622,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Starts voice recognition. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_AG_EVENT} - * intent. + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature + * is not supported.

        */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); @@ -644,7 +637,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.startVoiceRecognition(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -654,15 +647,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Stops voice recognition. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_AG_EVENT} - * intent. + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature + * is not supported.

        */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); @@ -671,7 +662,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.stopVoiceRecognition(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -681,8 +672,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns list of all calls in any state. * - * @param device remote device - * @return list of calls; empty list if none call exists + * @param device remote device + * @return list of calls; empty list if none call exists */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); @@ -691,7 +682,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.getCurrentCalls(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -701,9 +692,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns list of current values of AG indicators. * - * @param device remote device - * @return bundle of AG indicators; null if device is not in - * CONNECTED state + * @param device remote device + * @return bundle of AG indicators; null if device is not in CONNECTED state */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); @@ -712,7 +702,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.getCurrentAgEvents(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -722,14 +712,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Accepts a call * - * @param device remote device - * @param flag action policy while accepting a call. Possible values - * {@link #CALL_ACCEPT_NONE}, {@link #CALL_ACCEPT_HOLD}, - * {@link #CALL_ACCEPT_TERMINATE} - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE}, + * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE} + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); @@ -738,7 +725,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.acceptCall(device, flag); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -748,11 +735,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Holds a call. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); @@ -761,7 +746,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.holdCall(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -771,15 +756,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Rejects a call. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_REJECT_CALL}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not + * supported.

        */ public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); @@ -788,7 +771,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.rejectCall(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -800,18 +783,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Works only when Extended Call Control is supported by Audio Gateway. * - * @param device remote device - * @param call Handle of call obtained in {@link dial()} or obtained via - * {@link ACTION_CALL_CHANGED}. {@code call} may be null in which - * case we will hangup all active calls. - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @param call Handle of call obtained in {@link dial()} or obtained via {@link + * ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active + * calls. + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_ECC}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not + * supported.

        */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); @@ -820,7 +801,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.terminateCall(device, call); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -832,16 +813,14 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Works only when Extended Call Control is supported by Audio Gateway. * - * @param device remote device - * @param index index of the call to connect in private mode - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @param index index of the call to connect in private mode + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_ECC}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not + * supported.

        */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); @@ -850,7 +829,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.enterPrivateMode(device, index); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -862,15 +841,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * That means connect other calls and disconnect. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent. + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature + * is not supported.

        */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); @@ -879,7 +856,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.explicitCallTransfer(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -889,14 +866,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Places a call with specified number. * - * @param device remote device - * @param number valid phone number - * @return {@link BluetoothHeadsetClientCall} call if command has been - * issued successfully; - * {@link null} otherwise; - * upon completion HFP sends {@link #ACTION_CALL_CHANGED} - * intent in case of success; {@link #ACTION_RESULT} is sent - * otherwise; + * @param device remote device + * @param number valid phone number + * @return {@link BluetoothHeadsetClientCall} call if command has been issued + * successfully; {@link null} otherwise; upon completion HFP sends {@link + * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise; */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); @@ -905,7 +879,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.dial(device, number); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -917,11 +891,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# * - * @param device remote device - * @param code ASCII code - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_RESULT} intent; + * @param device remote device + * @param code ASCII code + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); @@ -930,7 +903,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.sendDTMF(device, code); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -940,15 +913,14 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Get a number corresponding to last voice tag recorded on AG. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_LAST_VTAG} - * or {@link #ACTION_RESULT} intent; + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT} + * intent; * - *

        Feature required for successful execution is being reported by: - * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. - * This method invocation will fail silently when feature is not supported.

        + *

        Feature required for successful execution is being reported by: {@link + * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when + * feature is not supported.

        */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); @@ -957,7 +929,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { try { return mService.getLastVoiceTagNumber(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + Log.e(TAG, Log.getStackTraceString(new Throwable())); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -974,7 +946,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { if (mService != null && isEnabled()) { try { return mService.getAudioState(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -985,16 +959,18 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Sets whether audio routing is allowed. * - * @param device remote device - * @param allowed if routing is allowed to the device - * Note: This is an internal function and shouldn't be exposed + * @param device remote device + * @param allowed if routing is allowed to the device Note: This is an internal function and + * shouldn't be exposed */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); if (mService != null && isEnabled()) { try { mService.setAudioRouteAllowed(device, allowed); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -1003,16 +979,19 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Returns whether audio routing is allowed. - * @param device remote device - * @return whether the command succeeded - * Note: This is an internal function and shouldn't be exposed + * + * @param device remote device + * @return whether the command succeeded Note: This is an internal function and shouldn't be + * exposed */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); if (mService != null && isEnabled()) { try { return mService.getAudioRouteAllowed(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } 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())); @@ -1025,11 +1004,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It setup SCO channel with remote connected Handsfree AG device. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} - * intent; + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { @@ -1050,11 +1027,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * It tears down the SCO channel from remote AG device. * - * @param device remote device - * @return true if command has been issued successfully; - * false otherwise; - * upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} - * intent; + * @param device remote device + * @return true if command has been issued successfully; false + * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { if (mService != null && isEnabled()) { @@ -1073,9 +1048,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Get Audio Gateway features * - * @param device remote device - * @return bundle of AG features; null if no service or - * AG not connected + * @param device remote device + * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { if (mService != null && isEnabled()) { @@ -1103,6 +1077,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { BluetoothHeadsetClient.this); } } + @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); @@ -1114,15 +1089,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 420c079f5fe..949cda0250f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -25,6 +25,7 @@ import java.util.UUID; /** * This class represents a single call, its state and properties. * It implements {@link Parcelable} for inter-process message passing. + * * @hide */ public final class BluetoothHeadsetClientCall implements Parcelable { @@ -98,7 +99,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * *

        Note: This is an internal function and shouldn't be exposed

        * - * @param state new call state. + * @param state new call state. */ public void setState(int state) { mState = state; @@ -109,7 +110,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * *

        Note: This is an internal function and shouldn't be exposed

        * - * @param number String representing phone number. + * @param number String representing phone number. */ public void setNumber(String number) { mNumber = number; @@ -120,8 +121,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * *

        Note: This is an internal function and shouldn't be exposed

        * - * @param multiParty if true sets this call as a part - * of multi party conference. + * @param multiParty if true sets this call as a part of multi party conference. */ public void setMultiParty(boolean multiParty) { mMultiParty = multiParty; @@ -185,8 +185,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { /** * Checks if call is an active call in a conference mode (aka multi party). * - * @return true if call is a multi party call, - * false otherwise. + * @return true if call is a multi party call, false otherwise. */ public boolean isMultiParty() { return mMultiParty; @@ -195,8 +194,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { /** * Checks if this call is an outgoing call. * - * @return true if its outgoing call, - * false otherwise. + * @return true if its outgoing call, false otherwise. */ public boolean isOutgoing() { return mOutgoing; @@ -215,15 +213,33 @@ public final class BluetoothHeadsetClientCall implements Parcelable { builder.append(mUUID); builder.append(", mState: "); switch (mState) { - case CALL_STATE_ACTIVE: builder.append("ACTIVE"); break; - case CALL_STATE_HELD: builder.append("HELD"); break; - case CALL_STATE_DIALING: builder.append("DIALING"); break; - case CALL_STATE_ALERTING: builder.append("ALERTING"); break; - case CALL_STATE_INCOMING: builder.append("INCOMING"); break; - case CALL_STATE_WAITING: builder.append("WAITING"); break; - case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: builder.append("HELD_BY_RESPONSE_AND_HOLD"); break; - case CALL_STATE_TERMINATED: builder.append("TERMINATED"); break; - default: builder.append(mState); break; + case CALL_STATE_ACTIVE: + builder.append("ACTIVE"); + break; + case CALL_STATE_HELD: + builder.append("HELD"); + break; + case CALL_STATE_DIALING: + builder.append("DIALING"); + break; + case CALL_STATE_ALERTING: + builder.append("ALERTING"); + break; + case CALL_STATE_INCOMING: + builder.append("INCOMING"); + break; + case CALL_STATE_WAITING: + builder.append("WAITING"); + break; + case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: + builder.append("HELD_BY_RESPONSE_AND_HOLD"); + break; + case CALL_STATE_TERMINATED: + builder.append("TERMINATED"); + break; + default: + builder.append(mState); + break; } builder.append(", mNumber: "); builder.append(loggable ? mNumber : mNumber.hashCode()); @@ -242,7 +258,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { new Parcelable.Creator() { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { - return new BluetoothHeadsetClientCall((BluetoothDevice)in.readParcelable(null), + return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null), in.readInt(), UUID.fromString(in.readString()), in.readInt(), in.readString(), in.readInt() == 1, in.readInt() == 1); } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 8d77888193b..fa759066ea1 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -36,24 +36,23 @@ import java.util.List; * Service via IPC. * *

        How to connect to a health device which is acting in the source role. - *

      • Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHealth proxy object.
      • - *
      • Create an {@link BluetoothHealth} callback and call - * {@link #registerSinkAppConfiguration} to register an application - * configuration
      • - *
      • Pair with the remote device. This currently needs to be done manually - * from Bluetooth Settings
      • - *
      • Connect to a health device using {@link #connectChannelToSource}. Some - * devices will connect the channel automatically. The {@link BluetoothHealth} - * callback will inform the application of channel state change.
      • - *
      • Use the file descriptor provided with a connected channel to read and - * write data to the health channel.
      • - *
      • The received data needs to be interpreted using a health manager which - * implements the IEEE 11073-xxxxx specifications. - *
      • When done, close the health channel by calling {@link #disconnectChannel} - * and unregister the application configuration calling - * {@link #unregisterAppConfiguration} - * + *
      • Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHealth proxy object.
      • + *
      • Create an {@link BluetoothHealth} callback and call + * {@link #registerSinkAppConfiguration} to register an application + * configuration
      • + *
      • Pair with the remote device. This currently needs to be done manually + * from Bluetooth Settings
      • + *
      • Connect to a health device using {@link #connectChannelToSource}. Some + * devices will connect the channel automatically. The {@link BluetoothHealth} + * callback will inform the application of channel state change.
      • + *
      • Use the file descriptor provided with a connected channel to read and + * write data to the health channel.
      • + *
      • The received data needs to be interpreted using a health manager which + * implements the IEEE 11073-xxxxx specifications. + *
      • When done, close the health channel by calling {@link #disconnectChannel} + * and unregister the application configuration calling + * {@link #unregisterAppConfiguration} */ public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; @@ -103,29 +102,29 @@ public final class BluetoothHealth implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** @@ -137,10 +136,10 @@ public final class BluetoothHealth implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param name The friendly name associated with the application or configuration. - * @param dataType The dataType of the Source role of Health Profile to which - * the sink wants to connect to. - * @param callback A callback to indicate success or failure of the registration and - * all operations done on this application configuration. + * @param dataType The dataType of the Source role of Health Profile to which the sink wants to + * connect to. + * @param callback A callback to indicate success or failure of the registration and all + * operations done on this application configuration. * @return If true, callback will be called. */ public boolean registerSinkAppConfiguration(String name, int dataType, @@ -161,9 +160,8 @@ public final class BluetoothHealth implements BluetoothProfile { * * @param name The friendly name associated with the application or configuration. * @param dataType The dataType of the Source role of Health Profile. - * @param channelType The channel type. Will be one of - * {@link #CHANNEL_TYPE_RELIABLE} or - * {@link #CHANNEL_TYPE_STREAMING} + * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link + * #CHANNEL_TYPE_STREAMING} * @param callback - A callback to indicate success or failure. * @return If true, callback will be called. * @hide @@ -197,7 +195,7 @@ public final class BluetoothHealth implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param config The health app configuration + * @param config The health app configuration * @return Success or failure. */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { @@ -224,8 +222,8 @@ public final class BluetoothHealth implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using - * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } + * @param config The application configuration which has been registered using {@link + * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. */ public boolean connectChannelToSource(BluetoothDevice device, @@ -249,11 +247,11 @@ public final class BluetoothHealth implements BluetoothProfile { * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using - * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } + * @param config The application configuration which has been registered using {@link + * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. * @hide */ @@ -278,11 +276,11 @@ public final class BluetoothHealth implements BluetoothProfile { * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using - * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } + * @param config The application configuration which has been registered using {@link + * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param channelId The channel id associated with the channel * @return If true, the callback associated with the application config will be called. */ @@ -342,9 +340,8 @@ public final class BluetoothHealth implements BluetoothProfile { * local adapter. * * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link + * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ @Override public int getConnectionState(BluetoothDevice device) { @@ -372,6 +369,7 @@ public final class BluetoothHealth implements BluetoothProfile { * state of the local Bluetooth adapter for this profile. This can be used * by applications like status bar which would just like to know the state of the * local adapter. + * * @return List of devices. The list will be empty on error. */ @Override @@ -401,9 +399,8 @@ public final class BluetoothHealth implements BluetoothProfile { * by applications like status bar which would just like to know the state of the * local adapter. * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link + * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ @Override @@ -429,25 +426,25 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - mCallback.onHealthAppConfigurationStatusChange(config, status); + int status) { + mCallback.onHealthAppConfigurationStatusChange(config, status); } @Override public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd, int channelId) { + BluetoothDevice device, int prevState, int newState, + ParcelFileDescriptor fd, int channelId) { mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd, - channelId); + channelId); } } - /** Health Channel Connection State - Disconnected */ - public static final int STATE_CHANNEL_DISCONNECTED = 0; + /** Health Channel Connection State - Disconnected */ + public static final int STATE_CHANNEL_DISCONNECTED = 0; /** Health Channel Connection State - Connecting */ - public static final int STATE_CHANNEL_CONNECTING = 1; + public static final int STATE_CHANNEL_CONNECTING = 1; /** Health Channel Connection State - Connected */ - public static final int STATE_CHANNEL_CONNECTED = 2; + public static final int STATE_CHANNEL_CONNECTED = 2; /** Health Channel Connection State - Disconnecting */ public static final int STATE_CHANNEL_DISCONNECTING = 3; @@ -477,7 +474,7 @@ public final class BluetoothHealth implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -503,7 +500,7 @@ public final class BluetoothHealth implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -513,7 +510,7 @@ public final class BluetoothHealth implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -529,6 +526,7 @@ public final class BluetoothHealth implements BluetoothProfile { mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; @@ -557,8 +555,8 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthCallback callback) { if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || (channelType != CHANNEL_TYPE_RELIABLE && - channelType != CHANNEL_TYPE_STREAMING && - channelType != CHANNEL_TYPE_ANY) || callback == null) { + channelType != CHANNEL_TYPE_STREAMING && + channelType != CHANNEL_TYPE_ANY) || callback == null) { return false; } if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 1717a1e36e1..d406ac2b1ec 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -25,7 +25,6 @@ import android.os.Parcelable; * the {@link BluetoothHealth} class. This class represents an application configuration * that the Bluetooth Health third party application will register to communicate with the * remote Bluetooth health device. - * */ public final class BluetoothHealthAppConfiguration implements Parcelable { private final String mName; @@ -52,12 +51,11 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth#SOURCE_ROLE} or - * {@link BluetoothHealth#SINK_ROLE} + * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} * @hide */ BluetoothHealthAppConfiguration(String name, int dataType, int role, int - channelType) { + channelType) { mName = name; mDataType = dataType; mRole = role; @@ -92,8 +90,8 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { @Override public String toString() { return "BluetoothHealthAppConfiguration [mName = " + mName + - ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + - mChannelType + "]"; + ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + + mChannelType + "]"; } public int describeContents() { @@ -121,8 +119,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { /** * Return the role associated with this application configuration. * - * @return One of {@link BluetoothHealth#SOURCE_ROLE} or - * {@link BluetoothHealth#SINK_ROLE} + * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} */ public int getRole() { return mRole; @@ -131,9 +128,8 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { /** * Return the channel type associated with this application configuration. * - * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or - * {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or - * {@link BluetoothHealth#CHANNEL_TYPE_ANY}. + * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link + * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}. * @hide */ public int getChannelType() { @@ -141,22 +137,22 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { } public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { - String name = in.readString(); - int type = in.readInt(); - int role = in.readInt(); - int channelType = in.readInt(); - return new BluetoothHealthAppConfiguration(name, type, role, - channelType); - } - - @Override - public BluetoothHealthAppConfiguration[] newArray(int size) { - return new BluetoothHealthAppConfiguration[size]; - } - }; + new Parcelable.Creator() { + @Override + public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { + String name = in.readString(); + int type = in.readInt(); + int role = in.readInt(); + int channelType = in.readInt(); + return new BluetoothHealthAppConfiguration(name, type, role, + channelType); + } + + @Override + public BluetoothHealthAppConfiguration[] newArray(int size) { + return new BluetoothHealthAppConfiguration[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeString(mName); diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index 128376f24f3..198d06a8caa 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -33,12 +33,11 @@ public abstract class BluetoothHealthCallback { *

        This callback is called on the binder thread (not on the UI thread) * * @param config Bluetooth Health app configuration - * @param status Success or failure of the registration or unregistration - * calls. Can be one of - * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or - * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or - * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or - * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} + * @param status Success or failure of the registration or unregistration calls. Can be one of + * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or {@link + * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or + * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} + * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} */ @BinderThread public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, @@ -57,15 +56,15 @@ public abstract class BluetoothHealthCallback { * @param prevState The previous state of the channel * @param newState The new state of the channel. * @param fd The Parcel File Descriptor when the channel state is connected. - * @param channelId The id associated with the channel. This id will be used - * in future calls like when disconnecting the channel. + * @param channelId The id associated with the channel. This id will be used in future calls + * like when disconnecting the channel. */ @BinderThread public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, int channelId) { Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + - "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + - "ChannelId:" + channelId); + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + + "ChannelId:" + channelId); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java index 05ba64e981f..2731935a4ea 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java @@ -49,19 +49,19 @@ public final class BluetoothHidDeviceAppConfiguration implements Parcelable { } public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + new Parcelable.Creator() { - @Override - public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { - long hash = in.readLong(); - return new BluetoothHidDeviceAppConfiguration(hash); - } + @Override + public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { + long hash = in.readLong(); + return new BluetoothHidDeviceAppConfiguration(hash); + } - @Override - public BluetoothHidDeviceAppConfiguration[] newArray(int size) { - return new BluetoothHidDeviceAppConfiguration[size]; - } - }; + @Override + public BluetoothHidDeviceAppConfiguration[] newArray(int size) { + return new BluetoothHidDeviceAppConfiguration[size]; + } + }; @Override public void writeToParcel(Parcel out, int flags) { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 0d6530c11a9..d8880a231fa 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -19,8 +19,6 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -import java.util.Random; - /** @hide */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -63,21 +61,22 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + new Parcelable.Creator() { - @Override - public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { + @Override + public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { - return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(), - in.readInt(), - in.readInt(), in.readInt()); - } + return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), + in.readInt(), + in.readInt(), + in.readInt(), in.readInt()); + } - @Override - public BluetoothHidDeviceAppQosSettings[] newArray(int size) { - return new BluetoothHidDeviceAppQosSettings[size]; - } - }; + @Override + public BluetoothHidDeviceAppQosSettings[] newArray(int size) { + return new BluetoothHidDeviceAppQosSettings[size]; + } + }; @Override public void writeToParcel(Parcel out, int flags) { @@ -90,7 +89,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } public int[] toArray() { - return new int[] { + return new int[]{ serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation }; } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index f9a22458195..e17e785718d 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -19,8 +19,6 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -import java.util.Random; - /** @hide */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @@ -54,20 +52,20 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { } public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + new Parcelable.Creator() { - @Override - public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { + @Override + public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { - return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), - in.readString(), in.readByte(), in.createByteArray()); - } + return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), + in.readString(), in.readByte(), in.createByteArray()); + } - @Override - public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { - return new BluetoothHidDeviceAppSdpSettings[size]; - } - }; + @Override + public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { + return new BluetoothHidDeviceAppSdpSettings[size]; + } + }; @Override public void writeToParcel(Parcel out, int flags) { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index f519776276f..d50505c092c 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -33,16 +33,15 @@ public abstract class BluetoothHidDeviceCallback { * , but can be also unsolicited in case e.g. Bluetooth was turned off in * which case application is unregistered automatically. * - * @param pluggedDevice {@link BluetoothDevice} object which represents host - * that currently has Virtual Cable established with device. Only - * valid when application is registered, can be null - * . - * @param config {@link BluetoothHidDeviceAppConfiguration} object which - * represents token required to unregister application using - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} - * . - * @param registered true if application is registered, - * false otherwise. + * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has + * Virtual Cable established with device. Only valid when application is registered, can be + * null . + * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token + * required to unregister application using + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * . + * @param registered true if application is registered, false + * otherwise. */ public void onAppStatusChanged(BluetoothDevice pluggedDevice, BluetoothHidDeviceAppConfiguration config, boolean registered) { @@ -55,8 +54,8 @@ public abstract class BluetoothHidDeviceCallback { * Application can assume than Virtual Cable is established when called with * {@link BluetoothProfile#STATE_CONNECTED} state. * - * @param device {@link BluetoothDevice} object representing host device - * which connection state was changed. + * @param device {@link BluetoothDevice} object representing host device which connection state + * was changed. * @param state Connection state as defined in {@link BluetoothProfile}. */ public void onConnectionStateChanged(BluetoothDevice device, int state) { @@ -69,10 +68,9 @@ public abstract class BluetoothHidDeviceCallback { * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}. * * @param type Requested Report Type. - * @param id Requested Report Id, can be 0 if no Report Id are defined in - * descriptor. - * @param bufferSize Requested buffer size, application shall respond with - * at least given number of bytes. + * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. + * @param bufferSize Requested buffer size, application shall respond with at least given number + * of bytes. */ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index a5a02435e37..a9b2e56bf3e 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -35,12 +35,13 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth Input * Device Profile. * - *

        BluetoothInputDevice is a proxy object for controlling the Bluetooth + *

        BluetoothInputDevice is a proxy object for controlling the Bluetooth * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothInputDevice proxy object. * - *

        Each method is protected with its appropriate permission. - *@hide + *

        Each method is protected with its appropriate permission. + * + * @hide */ public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; @@ -53,9 +54,9 @@ public final class BluetoothInputDevice implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -67,45 +68,46 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; /** * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PROTOCOL_MODE_CHANGED = - "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; + "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; /** * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_HANDSHAKE = - "android.bluetooth.input.profile.action.HANDSHAKE"; + "android.bluetooth.input.profile.action.HANDSHAKE"; /** * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_REPORT = - "android.bluetooth.input.profile.action.REPORT"; + "android.bluetooth.input.profile.action.REPORT"; /** * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; /** * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_IDLE_TIME_CHANGED = - "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; + "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * * @hide */ public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; @@ -174,22 +176,26 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * @hide */ - public static final String EXTRA_PROTOCOL_MODE = "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; + public static final String EXTRA_PROTOCOL_MODE = + "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; /** * @hide */ - public static final String EXTRA_REPORT_TYPE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; + public static final String EXTRA_REPORT_TYPE = + "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; /** * @hide */ - public static final String EXTRA_REPORT_ID = "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; + public static final String EXTRA_REPORT_ID = + "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; /** * @hide */ - public static final String EXTRA_REPORT_BUFFER_SIZE = "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; + public static final String EXTRA_REPORT_BUFFER_SIZE = + "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; /** * @hide @@ -204,12 +210,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * @hide */ - public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = + "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; /** * @hide */ - public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + public static final String EXTRA_IDLE_TIME = + "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; private Context mContext; private ServiceListener mServiceListener; @@ -221,34 +229,33 @@ public final class BluetoothInputDevice implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothInputDevice proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile - * */ /*package*/ BluetoothInputDevice(Context context, ServiceListener l) { mContext = context; @@ -260,7 +267,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -286,7 +293,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -296,9 +303,9 @@ public final class BluetoothInputDevice implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } - } + } } mServiceListener = null; } @@ -319,8 +326,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -359,8 +365,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -432,7 +437,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * Set priority of the profile * *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} @@ -447,8 +452,8 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + priority != BluetoothProfile.PRIORITY_ON) { + return false; } try { return mService.setPriority(device, priority); @@ -494,9 +499,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this); + mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, + BluetoothInputDevice.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; @@ -507,15 +514,15 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } @@ -525,8 +532,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean virtualUnplug(BluetoothDevice device) { @@ -546,15 +552,14 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * Send Get_Protocol_Mode command to the connected HID input device. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, - *true otherwise - * @hide - */ + * Send Get_Protocol_Mode command to the connected HID input device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { @@ -566,7 +571,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return false; } /** @@ -575,8 +580,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { @@ -602,12 +606,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @param reportType Report type * @param reportId Report ID * @param bufferSize Report receiving buffer size - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ - public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { - if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); + public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, + int bufferSize) { + if (VDBG) { + log( + "getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + + "bufferSize=" + bufferSize); + } if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getReport(device, reportType, reportId, bufferSize); @@ -628,8 +636,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @param device Remote Bluetooth Device * @param reportType Report type * @param report Report receiving buffer size - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { @@ -653,8 +660,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * * @param device Remote Bluetooth Device * @param report Report to send - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean sendData(BluetoothDevice device, String report) { @@ -677,8 +683,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean getIdleTime(BluetoothDevice device) { @@ -702,8 +707,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * * @param device Remote Bluetooth Device * @param idleTime Idle time to be set on HID Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { @@ -721,6 +725,6 @@ public final class BluetoothInputDevice implements BluetoothProfile { } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 68d105f1155..0cdcd577671 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -26,8 +26,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -43,9 +43,9 @@ public final class BluetoothInputHost implements BluetoothProfile { * *

        This intent will have 3 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -57,13 +57,12 @@ public final class BluetoothInputHost implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; /** * Constants representing device subclass. * - * @see #registerApp(String, String, String, byte, byte[], - * BluetoothHidDeviceCallback) + * @see #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; @@ -118,7 +117,8 @@ public final class BluetoothInputHost implements BluetoothProfile { private BluetoothAdapter mAdapter; - private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub { + private static class BluetoothHidDeviceCallbackWrapper extends + IBluetoothHidDeviceCallback.Stub { private BluetoothHidDeviceCallback mCallback; @@ -164,36 +164,43 @@ public final class BluetoothInputHost implements BluetoothProfile { } final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - public void onBluetoothStateChange(boolean up) { - Log.d(TAG, "onBluetoothStateChange: up=" + up); - synchronized (mConnection) { - if (!up) { - Log.d(TAG,"Unbinding service..."); - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e); + new IBluetoothStateChangeCallback.Stub() { + + public void onBluetoothStateChange(boolean up) { + Log.d(TAG, "onBluetoothStateChange: up=" + up); + synchronized (mConnection) { + if (!up) { + Log.d(TAG, "Unbinding service..."); + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG, "onBluetoothStateChange: could not unbind service:", + e); + } + } + } else { + try { + if (mService == null) { + Log.d(TAG, "Binding HID Device service..."); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG, + "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", + e); + } catch (SecurityException e) { + Log.e(TAG, + "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", + e); + } } } - } else { - try { - if (mService == null) { - Log.d(TAG,"Binding HID Device service..."); - doBind(); - } - } catch (IllegalStateException e) { - Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); - } catch (SecurityException e) { - Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); - } } - } - } - }; + }; private ServiceConnection mConnection = new ServiceConnection() { @@ -204,7 +211,7 @@ public final class BluetoothInputHost implements BluetoothProfile { if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, - BluetoothInputHost.this); + BluetoothInputHost.this); } } @@ -269,9 +276,9 @@ public final class BluetoothInputHost implements BluetoothProfile { try { mContext.unbindService(mConnection); } catch (IllegalArgumentException e) { - Log.e(TAG,"close: could not unbind HID Dev service: ", e); + Log.e(TAG, "close: could not unbind HID Dev service: ", e); } - } + } } mServiceListener = null; @@ -341,14 +348,11 @@ public final class BluetoothInputHost implements BluetoothProfile { * should be unregistered using * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. * - * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of - * HID Device SDP record. - * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of - * Incoming QoS Settings. - * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of - * Outgoing QoS Settings. - * @param callback {@link BluetoothHidDeviceCallback} object to which - * callback messages will be sent. + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. + * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be + * sent. * @return */ public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, @@ -366,9 +370,9 @@ public final class BluetoothInputHost implements BluetoothProfile { if (mService != null) { try { BluetoothHidDeviceAppConfiguration config = - new BluetoothHidDeviceAppConfiguration(); + new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = - new BluetoothHidDeviceCallbackWrapper(callback); + new BluetoothHidDeviceCallbackWrapper(callback); result = mService.registerApp(config, sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); @@ -385,11 +389,10 @@ public final class BluetoothInputHost implements BluetoothProfile { * new connections will be allowed until registered again using * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)} * - * @param config {@link BluetoothHidDeviceAppConfiguration} object as - * obtained from - * {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, - * BluetoothHidDeviceAppConfiguration, boolean)} - * + * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link + * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, + * BluetoothHidDeviceAppConfiguration, + * boolean)} * @return */ public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { @@ -413,8 +416,8 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * Sends report to remote host using interrupt channel. * - * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id - * are not defined in descriptor. + * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in + * descriptor. * @param data Report data, not including Report Id. * @return */ diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index 03af95337c5..062e4de0a78 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -51,15 +51,14 @@ import java.io.InputStream; * stream is detected or an exception is thrown. * * @return the byte read or -1 if the end of stream has been reached. - * @throws IOException - * if the stream is closed or another IOException occurs. + * @throws IOException if the stream is closed or another IOException occurs. * @since Android 1.5 */ public int read() throws IOException { byte b[] = new byte[1]; int ret = mSocket.read(b, 0, 1); if (ret == 1) { - return (int)b[0] & 0xff; + return (int) b[0] & 0xff; } else { return -1; } @@ -69,21 +68,14 @@ import java.io.InputStream; * Reads at most {@code length} bytes from this stream and stores them in * the byte array {@code b} starting at {@code offset}. * - * @param b - * the byte array in which to store the bytes read. - * @param offset - * the initial position in {@code buffer} to store the bytes - * read from this stream. - * @param length - * the maximum number of bytes to store in {@code b}. - * @return the number of bytes actually read or -1 if the end of the stream - * has been reached. - * @throws IndexOutOfBoundsException - * if {@code offset < 0} or {@code length < 0}, or if - * {@code offset + length} is greater than the length of - * {@code b}. - * @throws IOException - * if the stream is closed or another IOException occurs. + * @param b the byte array in which to store the bytes read. + * @param offset the initial position in {@code buffer} to store the bytes read from this + * stream. + * @param length the maximum number of bytes to store in {@code b}. + * @return the number of bytes actually read or -1 if the end of the stream has been reached. + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code length < 0}, or if {@code + * offset + length} is greater than the length of {@code b}. + * @throws IOException if the stream is closed or another IOException occurs. * @since Android 1.5 */ public int read(byte[] b, int offset, int length) throws IOException { diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index bacce800620..7e3bb05fe02 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -86,17 +86,16 @@ public final class BluetoothManager { * * @param device Remote bluetooth device. * @param profile GATT or GATT_SERVER - * @return State of the profile connection. One of - * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, - * {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING} + * @return State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED}, + * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING} */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device, int profile) { - if (DBG) Log.d(TAG,"getConnectionState()"); + if (DBG) Log.d(TAG, "getConnectionState()"); List connectedDevices = getConnectedDevices(profile); - for(BluetoothDevice connectedDevice : connectedDevices) { + for (BluetoothDevice connectedDevice : connectedDevices) { if (device.equals(connectedDevice)) { return BluetoothProfile.STATE_CONNECTED; } @@ -120,7 +119,7 @@ public final class BluetoothManager { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public List getConnectedDevices(int profile) { - if (DBG) Log.d(TAG,"getConnectedDevices"); + if (DBG) Log.d(TAG, "getConnectedDevices"); if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { throw new IllegalArgumentException("Profile not supported: " + profile); } @@ -133,16 +132,15 @@ public final class BluetoothManager { if (iGatt == null) return connectedDevices; connectedDevices = iGatt.getDevicesMatchingConnectionStates( - new int[] { BluetoothProfile.STATE_CONNECTED }); + new int[]{BluetoothProfile.STATE_CONNECTED}); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } return connectedDevices; } /** - * * Get a list of devices that match any of the given connection * states. * @@ -155,15 +153,14 @@ public final class BluetoothManager { * to know the state of the local adapter. * * @param profile GATT or GATT_SERVER - * @param states Array of states. States can be one of - * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, - * {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING}, + * @param states Array of states. States can be one of {@link BluetoothProfile#STATE_CONNECTED}, + * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public List getDevicesMatchingConnectionStates(int profile, int[] states) { - if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates"); if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { throw new IllegalArgumentException("Profile not supported: " + profile); @@ -177,7 +174,7 @@ public final class BluetoothManager { if (iGatt == null) return devices; devices = iGatt.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } return devices; @@ -189,14 +186,15 @@ public final class BluetoothManager { * as the results of any other GATT server operations. * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer * to conduct GATT server operations. + * * @param context App context * @param callback GATT server callback handler that will receive asynchronous callbacks. * @return BluetoothGattServer instance */ public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback) { + BluetoothGattServerCallback callback) { - return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO)); + return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO)); } /** @@ -205,16 +203,17 @@ public final class BluetoothManager { * as the results of any other GATT server operations. * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer * to conduct GATT server operations. + * * @param context App context * @param callback GATT server callback handler that will receive asynchronous callbacks. - * @param transport preferred transport for GATT connections to remote dual-mode devices - * {@link BluetoothDevice#TRANSPORT_AUTO} or - * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} * @return BluetoothGattServer instance * @hide */ public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback,int transport) { + BluetoothGattServerCallback callback, int transport) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -229,11 +228,11 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt,transport); + BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); Boolean regStatus = mGattServer.registerCallback(callback); - return regStatus? mGattServer : null; + return regStatus ? mGattServer : null; } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); return null; } } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 2e73051ee61..30c0d3c6d04 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,19 +16,23 @@ package android.bluetooth; -import java.util.List; -import java.util.ArrayList; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.*; +import android.os.Binder; +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 * Profile. - *@hide + * + * @hide */ public final class BluetoothMap implements BluetoothProfile { @@ -37,7 +41,7 @@ public final class BluetoothMap implements BluetoothProfile { private static final boolean VDBG = false; public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; private IBluetoothMap mService; private final Context mContext; @@ -45,7 +49,7 @@ public final class BluetoothMap implements BluetoothProfile { private BluetoothAdapter mAdapter; /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; + public static final int STATE_ERROR = -1; public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; @@ -57,29 +61,29 @@ public final class BluetoothMap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothMap proxy object. @@ -94,7 +98,7 @@ public final class BluetoothMap implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } doBind(); @@ -132,7 +136,7 @@ public final class BluetoothMap implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -142,7 +146,7 @@ public final class BluetoothMap implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -151,15 +155,18 @@ public final class BluetoothMap implements BluetoothProfile { /** * Get the current state of the BluetoothMap service. - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Map service. + * + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not + * connected to the Map service. */ public int getState() { if (VDBG) log("getState()"); if (mService != null) { try { return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -169,16 +176,18 @@ public final class BluetoothMap implements BluetoothProfile { /** * Get the currently connected remote Bluetooth device (PCE). - * @return The remote Bluetooth device, or null if not in connected or - * connecting state, or if this proxy object is not connected to - * the Map service. + * + * @return The remote Bluetooth device, or null if not in connected or connecting state, or if + * this proxy object is not connected to the Map service. */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); if (mService != null) { try { return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -196,7 +205,9 @@ public final class BluetoothMap implements BluetoothProfile { if (mService != null) { try { return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -217,18 +228,17 @@ public final class BluetoothMap implements BluetoothProfile { * Initiate disconnect. * * @param device Remote Bluetooth Device - * @return false on error, - * true otherwise + * @return false on error, true otherwise */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -240,18 +250,19 @@ public final class BluetoothMap implements BluetoothProfile { * This is a simple heuristic that tries to guess if a device with the * given class bits might support Map. It is not accurate for all * devices. It tries to err on the side of false positives. + * * @return True if this device might support Map. */ public static boolean doesClassMatchSink(BluetoothClass btClass) { // TODO optimize the rule switch (btClass.getDeviceClass()) { - case BluetoothClass.Device.COMPUTER_DESKTOP: - case BluetoothClass.Device.COMPUTER_LAPTOP: - case BluetoothClass.Device.COMPUTER_SERVER: - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - return true; - default: - return false; + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return true; + default: + return false; } } @@ -301,7 +312,7 @@ public final class BluetoothMap implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -317,7 +328,7 @@ public final class BluetoothMap implements BluetoothProfile { * Set priority of the profile * *

        The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or + * Priority can be one of {@link #PRIORITY_ON} or * {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device @@ -327,10 +338,10 @@ public final class BluetoothMap implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + priority != BluetoothProfile.PRIORITY_ON) { + return false; } try { return mService.setPriority(device, priority); @@ -356,7 +367,7 @@ public final class BluetoothMap implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -376,6 +387,7 @@ public final class BluetoothMap implements BluetoothProfile { mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) log("Proxy object disconnected"); mService = null; @@ -389,17 +401,18 @@ public final class BluetoothMap implements BluetoothProfile { Log.d(TAG, msg); } - private boolean isEnabled() { + private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; log("Bluetooth is Not enabled"); return false; } + private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ccab3cdf0b6..09dd5ad62b4 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); } } diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java index 4459e2c44bd..8447282d3e7 100644 --- a/framework/java/android/bluetooth/BluetoothMasInstance.java +++ b/framework/java/android/bluetooth/BluetoothMasInstance.java @@ -36,7 +36,7 @@ public final class BluetoothMasInstance implements Parcelable { @Override public boolean equals(Object o) { if (o instanceof BluetoothMasInstance) { - return mId == ((BluetoothMasInstance)o).mId; + return mId == ((BluetoothMasInstance) o).mId; } return false; } @@ -58,14 +58,15 @@ public final class BluetoothMasInstance implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothMasInstance createFromParcel(Parcel in) { - return new BluetoothMasInstance(in.readInt(), in.readString(), - in.readInt(), in.readInt()); - } - public BluetoothMasInstance[] newArray(int size) { - return new BluetoothMasInstance[size]; - } - }; + public BluetoothMasInstance createFromParcel(Parcel in) { + return new BluetoothMasInstance(in.readInt(), in.readString(), + in.readInt(), in.readInt()); + } + + public BluetoothMasInstance[] newArray(int size) { + return new BluetoothMasInstance[size]; + } + }; public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); @@ -75,10 +76,10 @@ public final class BluetoothMasInstance implements Parcelable { } public static final class MessageType { - public static final int EMAIL = 0x01; - public static final int SMS_GSM = 0x02; + public static final int EMAIL = 0x01; + public static final int SMS_GSM = 0x02; public static final int SMS_CDMA = 0x04; - public static final int MMS = 0x08; + public static final int MMS = 0x08; } public int getId() { diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index 117dd47c1ab..cecd3dbfff6 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -44,15 +44,13 @@ import java.io.OutputStream; * Writes a single byte to this stream. Only the least significant byte of * the integer {@code oneByte} is written to the stream. * - * @param oneByte - * the byte to be written. - * @throws IOException - * if an error occurs while writing to this stream. + * @param oneByte the byte to be written. + * @throws IOException if an error occurs while writing to this stream. * @since Android 1.0 */ public void write(int oneByte) throws IOException { byte b[] = new byte[1]; - b[0] = (byte)oneByte; + b[0] = (byte) oneByte; mSocket.write(b, 0, 1); } @@ -60,19 +58,12 @@ import java.io.OutputStream; * Writes {@code count} bytes from the byte array {@code buffer} starting * at position {@code offset} to this stream. * - * @param b - * the buffer to be written. - * @param offset - * the start position in {@code buffer} from where to get bytes. - * @param count - * the number of bytes from {@code buffer} to write to this - * stream. - * @throws IOException - * if an error occurs while writing to this stream. - * @throws IndexOutOfBoundsException - * if {@code offset < 0} or {@code count < 0}, or if - * {@code offset + count} is bigger than the length of - * {@code buffer}. + * @param b the buffer to be written. + * @param offset the start position in {@code buffer} from where to get bytes. + * @param count the number of bytes from {@code buffer} to write to this stream. + * @throws IOException if an error occurs while writing to this stream. + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code count < 0}, or if {@code + * offset + count} is bigger than the length of {@code buffer}. * @since Android 1.0 */ public void write(byte[] b, int offset, int count) throws IOException { @@ -84,15 +75,16 @@ import java.io.OutputStream; } mSocket.write(b, offset, count); } + /** * Wait until the data in sending queue is emptied. A polling version * for flush implementation. Use it to ensure the writing data afterwards will * be packed in the new RFCOMM frame. - * @throws IOException - * if an i/o error occurs. + * + * @throws IOException if an i/o error occurs. * @since Android 4.2.3 */ - public void flush() throws IOException { + public void flush() throws IOException { mSocket.flush(); } } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 2a026a91e8e..4c00649c947 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -34,12 +34,13 @@ import java.util.List; * This class provides the APIs to control the Bluetooth Pan * Profile. * - *

        BluetoothPan is a proxy object for controlling the Bluetooth + *

        BluetoothPan is a proxy object for controlling the Bluetooth * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothPan proxy object. * - *

        Each method is protected with its appropriate permission. - *@hide + *

        Each method is protected with its appropriate permission. + * + * @hide */ public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; @@ -52,11 +53,11 @@ public final class BluetoothPan implements BluetoothProfile { * *

        This intent will have 4 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • - *
        • {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is - * bound to.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is + * bound to.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -70,7 +71,7 @@ public final class BluetoothPan implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; /** * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent @@ -94,6 +95,7 @@ public final class BluetoothPan implements BluetoothProfile { /** * Return codes for the connect and disconnect Bluez / Dbus calls. + * * @hide */ public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; @@ -126,7 +128,6 @@ public final class BluetoothPan implements BluetoothProfile { /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile - * */ /*package*/ BluetoothPan(Context context, ServiceListener l) { mContext = context; @@ -135,7 +136,7 @@ public final class BluetoothPan implements BluetoothProfile { try { mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); } catch (RemoteException re) { - Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re); } if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); doBind(); @@ -161,7 +162,7 @@ public final class BluetoothPan implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mStateChangeCallback); } catch (RemoteException re) { - Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re); + Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re); } } @@ -171,7 +172,7 @@ public final class BluetoothPan implements BluetoothProfile { mPanService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -182,38 +183,41 @@ public final class BluetoothPan implements BluetoothProfile { close(); } - final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { - - @Override - public void onBluetoothStateChange(boolean on) { - // Handle enable request to bind again. - Log.d(TAG, "onBluetoothStateChange on: " + on); - if (on) { - try { - if (mPanService == null) { - if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); - doBind(); + final private IBluetoothStateChangeCallback mStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + @Override + public void onBluetoothStateChange(boolean on) { + // Handle enable request to bind again. + Log.d(TAG, "onBluetoothStateChange on: " + on); + if (on) { + try { + if (mPanService == null) { + if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); + doBind(); + } + + } catch (IllegalStateException e) { + Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", + e); + + } catch (SecurityException e) { + Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", + e); + } + } else { + if (VDBG) Log.d(TAG, "Unbinding service..."); + synchronized (mConnection) { + try { + mPanService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } + } } - - } catch (IllegalStateException e) { - Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); - - } catch (SecurityException e) { - Log.e(TAG,"onBluetoothStateChange: could not bind to PAN service: ", e); } - } else { - if (VDBG) Log.d(TAG,"Unbinding service..."); - synchronized (mConnection) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - } - }; + }; /** * Initiate connection to a profile of the remote bluetooth device. @@ -229,14 +233,13 @@ public final class BluetoothPan implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); if (mPanService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mPanService.connect(device); } catch (RemoteException e) { @@ -270,14 +273,13 @@ public final class BluetoothPan implements BluetoothProfile { * permission. * * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise + * @return false on immediate error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mPanService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mPanService.disconnect(device); } catch (RemoteException e) { @@ -329,7 +331,7 @@ public final class BluetoothPan implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mPanService != null && isEnabled() - && isValidDevice(device)) { + && isValidDevice(device)) { try { return mPanService.getConnectionState(device); } catch (RemoteException e) { @@ -373,9 +375,10 @@ public final class BluetoothPan implements BluetoothProfile { if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PAN, - BluetoothPan.this); + BluetoothPan.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); mPanService = null; @@ -386,18 +389,18 @@ public final class BluetoothPan implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; + if (device == null) return false; - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; } private static void log(String msg) { - Log.d(TAG, msg); + Log.d(TAG, msg); } } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index dc01fc7a949..fe7ce1d62cb 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -20,8 +20,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; /** @@ -55,17 +55,18 @@ public class BluetoothPbap { /** int extra for PBAP_STATE_CHANGED_ACTION */ public static final String PBAP_STATE = - "android.bluetooth.pbap.intent.PBAP_STATE"; + "android.bluetooth.pbap.intent.PBAP_STATE"; /** int extra for PBAP_STATE_CHANGED_ACTION */ public static final String PBAP_PREVIOUS_STATE = - "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; + "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; - /** Indicates the state of a pbap connection state has changed. - * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. + /** + * Indicates the state of a pbap connection state has changed. + * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and + * BluetoothIntent.ADDRESS extras. */ public static final String PBAP_STATE_CHANGED_ACTION = - "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; + "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; private IBluetoothPbap mService; private final Context mContext; @@ -73,13 +74,13 @@ public class BluetoothPbap { private BluetoothAdapter mAdapter; /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; + public static final int STATE_ERROR = -1; /** No client currently connected */ public static final int STATE_DISCONNECTED = 0; /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = 1; /** Client is currently connected */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = 2; public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; @@ -114,29 +115,29 @@ public class BluetoothPbap { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothPbap proxy object. @@ -150,7 +151,7 @@ public class BluetoothPbap { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } doBind(); @@ -188,7 +189,7 @@ public class BluetoothPbap { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -198,7 +199,7 @@ public class BluetoothPbap { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -207,15 +208,18 @@ public class BluetoothPbap { /** * Get the current state of the BluetoothPbap service. - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Pbap service. + * + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not + * connected to the Pbap service. */ public int getState() { if (VDBG) log("getState()"); if (mService != null) { try { return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -225,16 +229,18 @@ public class BluetoothPbap { /** * Get the currently connected remote Bluetooth device (PCE). - * @return The remote Bluetooth device, or null if not in connected or - * connecting state, or if this proxy object is not connected to - * the Pbap service. + * + * @return The remote Bluetooth device, or null if not in connected or connecting state, or if + * this proxy object is not connected to the Pbap service. */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); if (mService != null) { try { return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -252,7 +258,9 @@ public class BluetoothPbap { if (mService != null) { try { return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -271,7 +279,9 @@ public class BluetoothPbap { try { mService.disconnect(); return true; - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -284,18 +294,19 @@ public class BluetoothPbap { * This is a simple heuristic that tries to guess if a device with the * given class bits might support PBAP. It is not accurate for all * devices. It tries to err on the side of false positives. + * * @return True if this device might support PBAP. */ public static boolean doesClassMatchSink(BluetoothClass btClass) { // TODO optimize the rule switch (btClass.getDeviceClass()) { - case BluetoothClass.Device.COMPUTER_DESKTOP: - case BluetoothClass.Device.COMPUTER_LAPTOP: - case BluetoothClass.Device.COMPUTER_SERVER: - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - return true; - default: - return false; + case BluetoothClass.Device.COMPUTER_DESKTOP: + case BluetoothClass.Device.COMPUTER_LAPTOP: + case BluetoothClass.Device.COMPUTER_SERVER: + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return true; + default: + return false; } } @@ -307,6 +318,7 @@ public class BluetoothPbap { mServiceListener.onServiceConnected(BluetoothPbap.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) log("Proxy object disconnected"); mService = null; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 9f00e1aaa3a..3d6d002e9fb 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,20 +16,22 @@ package android.bluetooth; -import java.util.List; -import java.util.ArrayList; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.Binder; 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 PBAP Client Profile. - *@hide + * + * @hide */ public final class BluetoothPbapClient implements BluetoothProfile { @@ -38,7 +40,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { private static final boolean VDBG = false; public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; private IBluetoothPbapClient mService; private final Context mContext; @@ -46,7 +48,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { private BluetoothAdapter mAdapter; /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; + public static final int STATE_ERROR = -1; public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; @@ -61,14 +63,14 @@ public final class BluetoothPbapClient implements BluetoothProfile { } if (!up) { if (VDBG) { - Log.d(TAG,"Unbinding service..."); + Log.d(TAG, "Unbinding service..."); } synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { @@ -76,17 +78,17 @@ public final class BluetoothPbapClient implements BluetoothProfile { try { if (mService == null) { if (VDBG) { - Log.d(TAG,"Binding service..."); + Log.d(TAG, "Binding service..."); } doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothPbapClient proxy object. @@ -103,7 +105,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } doBind(); @@ -141,7 +143,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -151,7 +153,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -163,9 +165,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { * Upon successful connection to remote PBAP server the Client will * attempt to automatically download the users phonebook and call log. * - * @param device a remote device we want connect to - * @return true if command has been issued successfully; - * false otherwise; + * @param device a remote device we want connect to + * @return true if command has been issued successfully; false + * otherwise; */ public boolean connect(BluetoothDevice device) { if (DBG) { @@ -189,20 +191,19 @@ public final class BluetoothPbapClient implements BluetoothProfile { * Initiate disconnect. * * @param device Remote Bluetooth Device - * @return false on error, - * true otherwise + * @return false on error, true otherwise */ public boolean disconnect(BluetoothDevice device) { if (DBG) { - log("disconnect(" + device + ")" + new Exception() ); + log("disconnect(" + device + ")" + new Exception()); } if (mService != null && isEnabled() && isValidDevice(device)) { try { mService.disconnect(device); return true; } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) { @@ -291,9 +292,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { } mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, BluetoothPbapClient.this); + mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, + BluetoothPbapClient.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) { log("Proxy object disconnected"); @@ -319,20 +322,20 @@ public final class BluetoothPbapClient implements BluetoothProfile { } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) { - return false; - } - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { - return true; - } - return false; + if (device == null) { + return false; + } + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { + return true; + } + return false; } /** * Set priority of the profile * *

        The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or + * Priority can be one of {@link #PRIORITY_ON} or * {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device @@ -344,10 +347,10 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("setPriority(" + device + ", " + priority + ")"); } if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + priority != BluetoothProfile.PRIORITY_ON) { + return false; } try { return mService.setPriority(device, priority); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index c5b58e97e52..9192ec44ee3 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -46,14 +46,14 @@ public interface BluetoothProfile { * Bluetooth device. */ public static final String EXTRA_PREVIOUS_STATE = - "android.bluetooth.profile.extra.PREVIOUS_STATE"; + "android.bluetooth.profile.extra.PREVIOUS_STATE"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + public static final int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = 1; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = 2; /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; @@ -74,18 +74,21 @@ public interface BluetoothProfile { /** * Input Device Profile + * * @hide */ public static final int INPUT_DEVICE = 4; /** * PAN Profile + * * @hide */ public static final int PAN = 5; /** * PBAP + * * @hide */ public static final int PBAP = 6; @@ -102,6 +105,7 @@ public interface BluetoothProfile { /** * MAP Profile + * * @hide */ public static final int MAP = 9; @@ -114,36 +118,42 @@ public interface BluetoothProfile { /** * A2DP Sink Profile + * * @hide */ public static final int A2DP_SINK = 11; /** * AVRCP Controller Profile + * * @hide */ public static final int AVRCP_CONTROLLER = 12; /** * Headset Client - HFP HF Role + * * @hide */ public static final int HEADSET_CLIENT = 16; /** * PBAP Client + * * @hide */ public static final int PBAP_CLIENT = 17; /** * MAP Messaging Client Equipment (MCE) + * * @hide */ public static final int MAP_CLIENT = 18; /** * Input Host + * * @hide */ static public final int INPUT_HOST = 19; @@ -151,6 +161,7 @@ public interface BluetoothProfile { /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. + * * @hide */ public static final int MAX_PROFILE_ID = 19; @@ -158,13 +169,15 @@ public interface BluetoothProfile { /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile + * * @hide **/ public static final int PRIORITY_AUTO_CONNECT = 1000; /** - * Default priority for devices that allow incoming + * Default priority for devices that allow incoming * and outgoing connections for the profile + * * @hide **/ public static final int PRIORITY_ON = 100; @@ -172,14 +185,16 @@ public interface BluetoothProfile { /** * Default priority for devices that does not allow incoming * connections and outgoing connections for the profile. + * * @hide **/ public static final int PRIORITY_OFF = 0; /** * Default priority when not set or when the device is unpaired + * * @hide - * */ + */ public static final int PRIORITY_UNDEFINED = -1; /** @@ -199,9 +214,8 @@ public interface BluetoothProfile { *

        If none of the devices match any of the given states, * an empty list will be returned. * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, + * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link + * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ @RequiresPermission(Manifest.permission.BLUETOOTH) @@ -211,9 +225,8 @@ public interface BluetoothProfile { * Get the current connection state of the profile * * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link + * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device); @@ -226,18 +239,18 @@ public interface BluetoothProfile { /** * Called to notify the client when the proxy object has been * connected to the service. - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or - * {@link #A2DP} - * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or - * {@link BluetoothA2dp} + * + * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} + * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link + * BluetoothA2dp} */ public void onServiceConnected(int profile, BluetoothProfile proxy); /** * Called to notify the client that this proxy object has been * disconnected from the service. - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or - * {@link #A2DP} + * + * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} */ public void onServiceDisconnected(int profile); } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 89c1bf8f9aa..ea635b239ca 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -16,19 +16,18 @@ package android.bluetooth; -import java.util.ArrayList; -import java.util.List; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.Binder; import android.os.IBinder; -import android.os.ServiceManager; +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 SIM * Access Profile (SAP). @@ -38,6 +37,7 @@ import android.util.Log; * the BluetoothSap proxy object. * *

        Each method is protected with its appropriate permission. + * * @hide */ public final class BluetoothSap implements BluetoothProfile { @@ -51,9 +51,9 @@ public final class BluetoothSap implements BluetoothProfile { * *

        This intent will have 4 extras: *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • *
        * *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of @@ -62,10 +62,11 @@ public final class BluetoothSap implements BluetoothProfile { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. + * * @hide */ public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; private IBluetoothSap mService; private final Context mContext; @@ -74,18 +75,21 @@ public final class BluetoothSap implements BluetoothProfile { /** * There was an error trying to obtain the state. + * * @hide */ public static final int STATE_ERROR = -1; /** * Connection state change succceeded. + * * @hide */ public static final int RESULT_SUCCESS = 1; /** * Connection canceled before completion. + * * @hide */ public static final int RESULT_CANCELED = 2; @@ -95,29 +99,29 @@ public final class BluetoothSap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } else { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG, "Binding service..."); doBind(); } } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } } - }; + }; /** * Create a BluetoothSap proxy object. @@ -132,7 +136,7 @@ public final class BluetoothSap implements BluetoothProfile { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); } catch (RemoteException e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } doBind(); @@ -163,6 +167,7 @@ public final class BluetoothSap implements BluetoothProfile { * Other public functions of BluetoothSap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * * @hide */ public synchronized void close() { @@ -171,7 +176,7 @@ public final class BluetoothSap implements BluetoothProfile { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); } catch (Exception e) { - Log.e(TAG,"",e); + Log.e(TAG, "", e); } } @@ -181,7 +186,7 @@ public final class BluetoothSap implements BluetoothProfile { mService = null; mContext.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG, "", re); } } } @@ -190,8 +195,9 @@ public final class BluetoothSap implements BluetoothProfile { /** * Get the current state of the BluetoothSap service. - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy - * object is currently not connected to the Sap service. + * + * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not + * connected to the Sap service. * @hide */ public int getState() { @@ -199,7 +205,9 @@ public final class BluetoothSap implements BluetoothProfile { if (mService != null) { try { return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -209,9 +217,9 @@ public final class BluetoothSap implements BluetoothProfile { /** * Get the currently connected remote Bluetooth device (PCE). - * @return The remote Bluetooth device, or null if not in connected or - * connecting state, or if this proxy object is not connected to - * the Sap service. + * + * @return The remote Bluetooth device, or null if not in connected or connecting state, or if + * this proxy object is not connected to the Sap service. * @hide */ public BluetoothDevice getClient() { @@ -219,7 +227,9 @@ public final class BluetoothSap implements BluetoothProfile { if (mService != null) { try { return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -231,6 +241,7 @@ public final class BluetoothSap 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 Sap service. + * * @hide */ public boolean isConnected(BluetoothDevice device) { @@ -238,7 +249,9 @@ public final class BluetoothSap implements BluetoothProfile { if (mService != null) { try { return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -249,6 +262,7 @@ public final class BluetoothSap implements BluetoothProfile { /** * Initiate connection. Initiation of outgoing connections is not * supported for SAP server. + * * @hide */ public boolean connect(BluetoothDevice device) { @@ -260,19 +274,18 @@ public final class BluetoothSap implements BluetoothProfile { * Initiate disconnect. * * @param device Remote Bluetooth Device - * @return false on error, - * true otherwise + * @return false on error, true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -328,7 +341,7 @@ public final class BluetoothSap implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -353,10 +366,10 @@ public final class BluetoothSap implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + priority != BluetoothProfile.PRIORITY_ON) { + return false; } try { return mService.setPriority(device, priority); @@ -379,7 +392,7 @@ public final class BluetoothSap implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && - isValidDevice(device)) { + isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -399,6 +412,7 @@ public final class BluetoothSap implements BluetoothProfile { mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); } } + public void onServiceDisconnected(ComponentName className) { if (DBG) log("Proxy object disconnected"); mService = null; @@ -415,19 +429,22 @@ public final class BluetoothSap implements BluetoothProfile { private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { return true; + } log("Bluetooth is Not enabled"); return false; } private boolean isValidDevice(BluetoothDevice device) { - if (device == null) - return false; + if (device == null) { + return false; + } - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) - return true; - return false; + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { + return true; + } + return false; } } diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 4860c938405..7b438bdb4e3 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -75,12 +75,13 @@ public final class BluetoothServerSocket implements Closeable { /** * Construct a socket for incoming connections. - * @param type type of socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param port remote port - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) throws IOException { @@ -93,14 +94,15 @@ public final class BluetoothServerSocket implements Closeable { /** * Construct a socket for incoming connections. - * @param type type of socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param port remote port - * @param mitm enforce man-in-the-middle protection for authentication. + * @param port remote port + * @param mitm enforce man-in-the-middle protection for authentication. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, boolean mitm, boolean min16DigitPin) @@ -108,19 +110,20 @@ public final class BluetoothServerSocket implements Closeable { mChannel = port; mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm, min16DigitPin); - if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { mSocket.setExcludeSdp(true); } } /** * Construct a socket for incoming connections. - * @param type type of socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param uuid uuid - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @param uuid uuid + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid) throws IOException { @@ -136,9 +139,9 @@ public final class BluetoothServerSocket implements Closeable { *

        Once this call returns, it can be called again to accept subsequent * incoming connections. *

        {@link #close} can be used to abort this call from another thread. + * * @return a connected {@link BluetoothSocket} - * @throws IOException on error, for example this call was aborted, or - * timeout + * @throws IOException on error, for example this call was aborted, or timeout */ public BluetoothSocket accept() throws IOException { return accept(-1); @@ -150,9 +153,9 @@ public final class BluetoothServerSocket implements Closeable { *

        Once this call returns, it can be called again to accept subsequent * incoming connections. *

        {@link #close} can be used to abort this call from another thread. + * * @return a connected {@link BluetoothSocket} - * @throws IOException on error, for example this call was aborted, or - * timeout + * @throws IOException on error, for example this call was aborted, or timeout */ public BluetoothSocket accept(int timeout) throws IOException { return mSocket.accept(timeout); @@ -174,16 +177,19 @@ public final class BluetoothServerSocket implements Closeable { mSocket.close(); } - /*package*/ synchronized void setCloseHandler(Handler handler, int message) { + /*package*/ + synchronized void setCloseHandler(Handler handler, int message) { mHandler = handler; mMessage = message; } + /*package*/ void setServiceName(String ServiceName) { mSocket.setServiceName(ServiceName); } /** * Returns the channel on which this socket is bound. + * * @hide */ public int getChannel() { @@ -199,10 +205,10 @@ public final class BluetoothServerSocket implements Closeable { * The bind operation should be conducted through this class * and the resulting port should be kept in mChannel, and * not set from BluetoothAdapter. */ - if(mSocket != null) { - if(mSocket.getPort() != newChannel) { - Log.w(TAG,"The port set is different that the underlying port. mSocket.getPort(): " - + mSocket.getPort() + " requested newChannel: " + newChannel); + if (mSocket != null) { + if (mSocket.getPort() != newChannel) { + Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): " + + mSocket.getPort() + " requested newChannel: " + newChannel); } } mChannel = newChannel; @@ -212,19 +218,16 @@ public final class BluetoothServerSocket implements Closeable { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ServerSocket: Type: "); - switch(mSocket.getConnectionType()) { - case BluetoothSocket.TYPE_RFCOMM: - { + switch (mSocket.getConnectionType()) { + case BluetoothSocket.TYPE_RFCOMM: { sb.append("TYPE_RFCOMM"); break; } - case BluetoothSocket.TYPE_L2CAP: - { + case BluetoothSocket.TYPE_L2CAP: { sb.append("TYPE_L2CAP"); break; } - case BluetoothSocket.TYPE_SCO: - { + case BluetoothSocket.TYPE_SCO: { sb.append("TYPE_SCO"); break; } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 6bf6aa0a9ef..a90dd82b963 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,25 +16,23 @@ package android.bluetooth; -import android.os.ParcelUuid; +import android.net.LocalSocket; import android.os.ParcelFileDescriptor; +import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; -import java.io.BufferedInputStream; import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.Locale; import java.util.UUID; -import android.net.LocalSocket; -import java.nio.Buffer; -import java.nio.ByteOrder; -import java.nio.ByteBuffer; /** * A connected or connecting Bluetooth socket. * @@ -106,9 +104,9 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_ENCRYPT = 1; /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; - /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; - /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; - /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; + /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; + /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; + /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ @@ -151,15 +149,16 @@ public final class BluetoothSocket implements Closeable { /** * Construct a BluetoothSocket. - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param device remote device that this socket can connect to - * @param port remote port - * @param uuid SDP uuid - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @param device remote device that this socket can connect to + * @param port remote port + * @param uuid SDP uuid + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { @@ -168,21 +167,22 @@ public final class BluetoothSocket implements Closeable { /** * Construct a BluetoothSocket. - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted - * @param device remote device that this socket can connect to - * @param port remote port - * @param uuid SDP uuid - * @param mitm enforce man-in-the-middle protection. + * @param device remote device that this socket can connect to + * @param port remote port + * @param uuid SDP uuid + * @param mitm enforce man-in-the-middle protection. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) - throws IOException { + throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { @@ -190,9 +190,11 @@ public final class BluetoothSocket implements Closeable { throw new IOException("Invalid RFCOMM channel: " + port); } } - if (uuid != null) + if (uuid != null) { mUuid = uuid; - else mUuid = new ParcelUuid(new UUID(0, 0)); + } else { + mUuid = new ParcelUuid(new UUID(0, 0)); + } mType = type; mAuth = auth; mAuthMitm = mitm; @@ -214,6 +216,7 @@ public final class BluetoothSocket implements Closeable { mInputStream = new BluetoothInputStream(this); mOutputStream = new BluetoothOutputStream(this); } + private BluetoothSocket(BluetoothSocket s) { if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType); mUuid = s.mUuid; @@ -231,12 +234,13 @@ public final class BluetoothSocket implements Closeable { mAuthMitm = s.mAuthMitm; mMin16DigitPin = s.mMin16DigitPin; } + private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds)); - if(fds == null || fds.length != 1) { + if (fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds)); as.close(); throw new IOException("bt socket acept failed"); @@ -250,16 +254,18 @@ public final class BluetoothSocket implements Closeable { as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); return as; } + /** * Construct a BluetoothSocket from address. Used by native code. - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated + * + * @param type type of socket + * @param fd fd to use for connected socket, or -1 for a new socket + * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted * @param address remote device that this socket can connect to - * @param port remote port - * @throws IOException On error, for example Bluetooth not available, or - * insufficient privileges + * @param port remote port + * @throws IOException On error, for example Bluetooth not available, or insufficient + * privileges */ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, int port) throws IOException { @@ -275,23 +281,30 @@ public final class BluetoothSocket implements Closeable { super.finalize(); } } + private int getSecurityFlags() { int flags = 0; - if(mAuth) + if (mAuth) { flags |= SEC_FLAG_AUTH; - if(mEncrypt) + } + if (mEncrypt) { flags |= SEC_FLAG_ENCRYPT; - if(mExcludeSdp) + } + if (mExcludeSdp) { flags |= BTSOCK_FLAG_NO_SDP; - if(mAuthMitm) + } + if (mAuthMitm) { flags |= SEC_FLAG_AUTH_MITM; - if(mMin16DigitPin) + } + if (mMin16DigitPin) { flags |= SEC_FLAG_AUTH_16_DIGIT; + } return flags; } /** * Get the remote device this socket is connecting, or connected, to. + * * @return remote device */ public BluetoothDevice getRemoteDevice() { @@ -303,6 +316,7 @@ public final class BluetoothSocket implements Closeable { *

        The input stream will be returned even if the socket is not yet * connected, but operations on that stream will throw IOException until * the associated socket is connected. + * * @return InputStream */ public InputStream getInputStream() throws IOException { @@ -314,6 +328,7 @@ public final class BluetoothSocket implements Closeable { *

        The output stream will be returned even if the socket is not yet * connected, but operations on that stream will throw IOException until * the associated socket is connected. + * * @return OutputStream */ public OutputStream getOutputStream() throws IOException { @@ -323,8 +338,8 @@ public final class BluetoothSocket implements Closeable { /** * Get the connection status of this socket, ie, whether there is an active connection with * remote device. - * @return true if connected - * false if not connected + * + * @return true if connected false if not connected */ public boolean isConnected() { return mSocketState == SocketState.CONNECTED; @@ -349,6 +364,7 @@ public final class BluetoothSocket implements Closeable { * {@link BluetoothAdapter#cancelDiscovery()} even if it * did not directly request a discovery, just to be sure. *

        {@link #close} can be used to abort this call from another thread. + * * @throws IOException on error, for example connection failure */ public void connect() throws IOException { @@ -361,8 +377,7 @@ public final class BluetoothSocket implements Closeable { if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); - synchronized(this) - { + synchronized (this) { if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mPfd == null) throw new IOException("bt socket connect failed"); @@ -372,14 +387,15 @@ public final class BluetoothSocket implements Closeable { mSocketOS = mSocket.getOutputStream(); } int channel = readInt(mSocketIS); - if (channel <= 0) + if (channel <= 0) { throw new IOException("bt socket connect failed"); + } mPort = channel; waitSocketSignal(mSocketIS); - synchronized(this) - { - if (mSocketState == SocketState.CLOSED) + synchronized (this) { + if (mSocketState == SocketState.CLOSED) { throw new IOException("bt socket closed"); + } mSocketState = SocketState.CONNECTED; } } catch (RemoteException e) { @@ -410,11 +426,13 @@ public final class BluetoothSocket implements Closeable { // read out port number try { - synchronized(this) { - if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + - mPfd); - if(mSocketState != SocketState.INIT) return EBADFD; - if(mPfd == null) return -1; + synchronized (this) { + if (DBG) { + Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + + mPfd); + } + if (mSocketState != SocketState.INIT) return EBADFD; + if (mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); if (fd == null) { Log.e(TAG, "bindListen(), null file descriptor"); @@ -429,9 +447,10 @@ public final class BluetoothSocket implements Closeable { } if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); int channel = readInt(mSocketIS); - synchronized(this) { - if(mSocketState == SocketState.INIT) + synchronized (this) { + if (mSocketState == SocketState.INIT) { mSocketState = SocketState.LISTENING; + } } if (DBG) Log.d(TAG, "channel: " + channel); if (mPort <= -1) { @@ -455,19 +474,21 @@ public final class BluetoothSocket implements Closeable { /*package*/ BluetoothSocket accept(int timeout) throws IOException { BluetoothSocket acceptedSocket; - if (mSocketState != SocketState.LISTENING) + if (mSocketState != SocketState.LISTENING) { throw new IOException("bt socket is not in listen state"); - if(timeout > 0) { + } + if (timeout > 0) { Log.d(TAG, "accept() set timeout (ms):" + timeout); - mSocket.setSoTimeout(timeout); + mSocket.setSoTimeout(timeout); } String RemoteAddr = waitSocketSignal(mSocketIS); - if(timeout > 0) + if (timeout > 0) { mSocket.setSoTimeout(0); - synchronized(this) - { - if (mSocketState != SocketState.LISTENING) + } + synchronized (this) { + if (mSocketState != SocketState.LISTENING) { throw new IOException("bt socket is not in listen state"); + } acceptedSocket = acceptSocket(RemoteAddr); //quick drop the reference of the file handle } @@ -478,12 +499,13 @@ public final class BluetoothSocket implements Closeable { if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); } + /** * Wait until the data in sending queue is emptied. A polling version * for flush implementation. Used to ensure the writing data afterwards will * be packed in new RFCOMM frame. - * @throws IOException - * if an i/o error occurs. + * + * @throws IOException if an i/o error occurs. */ /*package*/ void flush() throws IOException { if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); @@ -494,11 +516,12 @@ public final class BluetoothSocket implements Closeable { /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - if(mType == TYPE_L2CAP) - { + if (mType == TYPE_L2CAP) { int bytesToRead = length; - if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length - + "mL2capBuffer= " + mL2capBuffer); + if (VDBG) { + Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length + + "mL2capBuffer= " + mL2capBuffer); + } if (mL2capBuffer == null) { createL2capRxBuffer(); } @@ -511,16 +534,19 @@ public final class BluetoothSocket implements Closeable { if (bytesToRead > mL2capBuffer.remaining()) { bytesToRead = mL2capBuffer.remaining(); } - if(VDBG) Log.v(TAG, "get(): offset: " + offset - + " bytesToRead: " + bytesToRead); + if (VDBG) { + Log.v(TAG, "get(): offset: " + offset + + " bytesToRead: " + bytesToRead); + } mL2capBuffer.get(b, offset, bytesToRead); ret = bytesToRead; - }else { + } else { if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length); ret = mSocketIS.read(b, offset, length); } - if (ret < 0) + if (ret < 0) { throw new IOException("bt socket closed, read return: " + ret); + } if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); return ret; } @@ -532,48 +558,49 @@ public final class BluetoothSocket implements Closeable { // splitting the write into multiple smaller writes. // Rfcomm uses dynamic allocation, and should not have any bindings // to the actual message length. - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - if (mType == TYPE_L2CAP) { - if(length <= mMaxTxPacketSize) { - mSocketOS.write(b, offset, length); - } else { - if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); + if (mType == TYPE_L2CAP) { + if (length <= mMaxTxPacketSize) { + mSocketOS.write(b, offset, length); + } else { + if (DBG) { + Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" + "Packet will be divided into SDU packets of size " + mMaxTxPacketSize); - int tmpOffset = offset; - int bytesToWrite = length; - while (bytesToWrite > 0) { - int tmpLength = (bytesToWrite > mMaxTxPacketSize) - ? mMaxTxPacketSize - : bytesToWrite; - mSocketOS.write(b, tmpOffset, tmpLength); - tmpOffset += tmpLength; - bytesToWrite -= tmpLength; - } } - } else { - mSocketOS.write(b, offset, length); + int tmpOffset = offset; + int bytesToWrite = length; + while (bytesToWrite > 0) { + int tmpLength = (bytesToWrite > mMaxTxPacketSize) + ? mMaxTxPacketSize + : bytesToWrite; + mSocketOS.write(b, tmpOffset, tmpLength); + tmpOffset += tmpLength; + bytesToWrite -= tmpLength; + } } - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; + } else { + mSocketOS.write(b, offset, length); + } + // There is no good way to confirm since the entire process is asynchronous anyway + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; } @Override public void close() throws IOException { Log.d(TAG, "close() this: " + this + ", channel: " + mPort + - ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + - "mSocket: " + mSocket + ", mSocketState: " + mSocketState); - if(mSocketState == SocketState.CLOSED) + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + + "mSocket: " + mSocket + ", mSocketState: " + mSocketState); + if (mSocketState == SocketState.CLOSED) { return; - else - { - synchronized(this) - { - if(mSocketState == SocketState.CLOSED) + } else { + synchronized (this) { + if (mSocketState == SocketState.CLOSED) { return; - mSocketState = SocketState.CLOSED; - if(mSocket != null) { + } + mSocketState = SocketState.CLOSED; + if (mSocket != null) { if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); mSocket.shutdownOutput(); @@ -584,7 +611,7 @@ public final class BluetoothSocket implements Closeable { mPfd.close(); mPfd = null; } - } + } } } @@ -599,9 +626,10 @@ public final class BluetoothSocket implements Closeable { * Get the maximum supported Transmit packet size for the underlying transport. * Use this to optimize the writes done to the output socket, to avoid sending * half full packets. + * * @return the maximum supported Transmit packet size for the underlying transport. */ - public int getMaxTransmitPacketSize(){ + public int getMaxTransmitPacketSize() { return mMaxTxPacketSize; } @@ -610,14 +638,16 @@ public final class BluetoothSocket implements Closeable { * Use this to optimize the reads done on the input stream, as any call to read * will return a maximum of this amount of bytes - or for some transports a * multiple of this value. + * * @return the maximum supported Receive packet size for the underlying transport. */ - public int getMaxReceivePacketSize(){ + public int getMaxReceivePacketSize() { return mMaxRxPacketSize; } /** * Get the type of the underlying connection. + * * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} */ public int getConnectionType() { @@ -627,67 +657,79 @@ public final class BluetoothSocket implements Closeable { /** * Change if a SDP entry should be automatically created. * Must be called before calling .bind, for the call to have any effect. - * @param mExcludeSdp

      • TRUE - do not auto generate SDP record. - *
      • FALSE - default - auto generate SPP SDP record. + * + * @param mExcludeSdp
      • TRUE - do not auto generate SDP record.
      • FALSE - default - auto + * generate SPP SDP record. * @hide */ public void setExcludeSdp(boolean excludeSdp) { this.mExcludeSdp = excludeSdp; } - private String convertAddr(final byte[] addr) { + private String convertAddr(final byte[] addr) { return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", - addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]); + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } + private String waitSocketSignal(InputStream is) throws IOException { - byte [] sig = new byte[SOCK_SIGNAL_SIZE]; + byte[] sig = new byte[SOCK_SIGNAL_SIZE]; int ret = readAll(is, sig); - if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + - " bytes signal ret: " + ret); + if (VDBG) { + Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + + " bytes signal ret: " + ret); + } ByteBuffer bb = ByteBuffer.wrap(sig); /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); - if(size != SOCK_SIGNAL_SIZE) + if (size != SOCK_SIGNAL_SIZE) { throw new IOException("Connection failure, wrong signal size: " + size); - byte [] addr = new byte[6]; + } + byte[] addr = new byte[6]; bb.get(addr); int channel = bb.getInt(); int status = bb.getInt(); mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value String RemoteAddr = convertAddr(addr); - if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " - + RemoteAddr + ", channel: " + channel + ", status: " + status - + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize); - if(status != 0) + if (VDBG) { + Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + + RemoteAddr + ", channel: " + channel + ", status: " + status + + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize); + } + if (status != 0) { throw new IOException("Connection failure, status: " + status); + } return RemoteAddr; } - private void createL2capRxBuffer(){ - if(mType == TYPE_L2CAP) { + private void createL2capRxBuffer() { + if (mType == TYPE_L2CAP) { // Allocate the buffer to use for reads. - if(VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); + if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); - if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); + if (VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request - if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + - mL2capBuffer.remaining()); + if (VDBG) { + Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + + mL2capBuffer.remaining()); + } } } private int readAll(InputStream is, byte[] b) throws IOException { int left = b.length; - while(left > 0) { + while (left > 0) { int ret = is.read(b, b.length - left, left); - if(ret <= 0) - throw new IOException("read failed, socket might closed or timeout, read ret: " - + ret); + if (ret <= 0) { + throw new IOException("read failed, socket might closed or timeout, read ret: " + + ret); + } left -= ret; - if(left != 0) + if (left != 0) { Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + - ", expect size: " + b.length); + ", expect size: " + b.length); + } } return b.length; } @@ -704,7 +746,7 @@ public final class BluetoothSocket implements Closeable { private int fillL2capRxBuffer() throws IOException { mL2capBuffer.rewind(); int ret = mSocketIS.read(mL2capBuffer.array()); - if(ret == -1) { + if (ret == -1) { // reached end of stream - return -1 mL2capBuffer.limit(0); return -1; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 243579a31ce..3b49abdee09 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -25,9 +25,10 @@ import java.util.HashSet; import java.util.UUID; /** -* Static helper methods and constants to decode the ParcelUuid of remote devices. -* @hide -*/ + * Static helper methods and constants to decode the ParcelUuid of remote devices. + * + * @hide + */ public final class BluetoothUuid { /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs @@ -76,9 +77,9 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid SAP = + public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); - + public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); @@ -90,8 +91,8 @@ public final class BluetoothUuid { public static final int UUID_BYTES_128_BIT = 16; public static final ParcelUuid[] RESERVED_UUIDS = { - AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; + AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, + ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -136,15 +137,19 @@ public final class BluetoothUuid { public static boolean isBnep(ParcelUuid uuid) { return uuid.equals(BNEP); } + public static boolean isMap(ParcelUuid uuid) { return uuid.equals(MAP); } + public static boolean isMns(ParcelUuid uuid) { return uuid.equals(MNS); } + public static boolean isMas(ParcelUuid uuid) { return uuid.equals(MAS); } + public static boolean isSap(ParcelUuid uuid) { return uuid.equals(SAP); } @@ -156,13 +161,15 @@ public final class BluetoothUuid { * @param uuid */ public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { - if ((uuidArray == null || uuidArray.length == 0) && uuid == null) + if ((uuidArray == null || uuidArray.length == 0) && uuid == null) { return true; + } - if (uuidArray == null) + if (uuidArray == null) { return false; + } - for (ParcelUuid element: uuidArray) { + for (ParcelUuid element : uuidArray) { if (element.equals(uuid)) return true; } return false; @@ -173,7 +180,6 @@ public final class BluetoothUuid { * * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids - * */ public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; @@ -186,8 +192,8 @@ public final class BluetoothUuid { return uuidA.length == 0 ? true : false; } - HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); - for (ParcelUuid uuid: uuidB) { + HashSet uuidSet = new HashSet(Arrays.asList(uuidA)); + for (ParcelUuid uuid : uuidB) { if (uuidSet.contains(uuid)) return true; } return false; @@ -199,7 +205,6 @@ public final class BluetoothUuid { * * @param uuidA - Array of ParcelUuidsA * @param uuidB - Array of ParcelUuidsB - * */ public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; @@ -210,8 +215,8 @@ public final class BluetoothUuid { if (uuidB == null) return true; - HashSet uuidSet = new HashSet (Arrays.asList(uuidA)); - for (ParcelUuid uuid: uuidB) { + HashSet uuidSet = new HashSet(Arrays.asList(uuidA)); + for (ParcelUuid uuid : uuidB) { if (!uuidSet.contains(uuid)) return false; } return true; @@ -221,13 +226,14 @@ public final class BluetoothUuid { * Extract the Service Identifier or the actual uuid from the Parcel Uuid. * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, * this function will return 110B + * * @param parcelUuid * @return the service identifier. */ public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; - return (int)value; + return (int) value; } /** @@ -264,7 +270,7 @@ public final class BluetoothUuid { shortUuid = uuidBytes[0] & 0xFF; shortUuid += (uuidBytes[1] & 0xFF) << 8; } else { - shortUuid = uuidBytes[0] & 0xFF ; + shortUuid = uuidBytes[0] & 0xFF; shortUuid += (uuidBytes[1] & 0xFF) << 8; shortUuid += (uuidBytes[2] & 0xFF) << 16; shortUuid += (uuidBytes[3] & 0xFF) << 24; @@ -275,8 +281,8 @@ public final class BluetoothUuid { } /** - * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID, - * Note returned value is little endian (Bluetooth). + * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or + * 128-bit UUID, Note returned value is little endian (Bluetooth). * * @param uuid uuid to parse. * @return shortest representation of {@code uuid} as bytes. @@ -290,18 +296,18 @@ public final class BluetoothUuid { if (is16BitUuid(uuid)) { byte[] uuidBytes = new byte[UUID_BYTES_16_BIT]; int uuidVal = getServiceIdentifierFromParcelUuid(uuid); - uuidBytes[0] = (byte)(uuidVal & 0xFF); - uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8); + uuidBytes[0] = (byte) (uuidVal & 0xFF); + uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); return uuidBytes; } if (is32BitUuid(uuid)) { byte[] uuidBytes = new byte[UUID_BYTES_32_BIT]; int uuidVal = getServiceIdentifierFromParcelUuid(uuid); - uuidBytes[0] = (byte)(uuidVal & 0xFF); - uuidBytes[1] = (byte)((uuidVal & 0xFF00) >> 8); - uuidBytes[2] = (byte)((uuidVal & 0xFF0000) >> 16); - uuidBytes[3] = (byte)((uuidVal & 0xFF000000) >> 24); + uuidBytes[0] = (byte) (uuidVal & 0xFF); + uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); + uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16); + uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24); return uuidBytes; } diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 9e87230c686..505cc5667de 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -19,8 +19,6 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; - /** * Out Of Band Data for Bluetooth device pairing. * @@ -77,7 +75,8 @@ public class OobData implements Parcelable { this.leSecureConnectionsRandom = leSecureConnectionsRandom; } - public OobData() { } + public OobData() { + } private OobData(Parcel in) { leBluetoothDeviceAddress = in.createByteArray(); diff --git a/framework/java/android/bluetooth/SdpMasRecord.java b/framework/java/android/bluetooth/SdpMasRecord.java index fa164c0fa9d..b202e536ba0 100644 --- a/framework/java/android/bluetooth/SdpMasRecord.java +++ b/framework/java/android/bluetooth/SdpMasRecord.java @@ -26,20 +26,21 @@ public class SdpMasRecord implements Parcelable { private final int mSupportedFeatures; private final int mSupportedMessageTypes; private final String mServiceName; + public static final class MessageType { - public static final int EMAIL = 0x01; - public static final int SMS_GSM = 0x02; + public static final int EMAIL = 0x01; + public static final int SMS_GSM = 0x02; public static final int SMS_CDMA = 0x04; - public static final int MMS = 0x08; + public static final int MMS = 0x08; } public SdpMasRecord(int mas_instance_id, - int l2cap_psm, - int rfcomm_channel_number, - int profile_version, - int supported_features, - int supported_message_types, - String service_name){ + int l2cap_psm, + int rfcomm_channel_number, + int profile_version, + int supported_features, + int supported_message_types, + String service_name) { this.mMasInstanceId = mas_instance_id; this.mL2capPsm = l2cap_psm; this.mRfcommChannelNumber = rfcomm_channel_number; @@ -49,7 +50,7 @@ public class SdpMasRecord implements Parcelable { this.mServiceName = service_name; } - public SdpMasRecord(Parcel in){ + public SdpMasRecord(Parcel in) { this.mMasInstanceId = in.readInt(); this.mL2capPsm = in.readInt(); this.mRfcommChannelNumber = in.readInt(); @@ -58,6 +59,7 @@ public class SdpMasRecord implements Parcelable { this.mSupportedMessageTypes = in.readInt(); this.mServiceName = in.readString(); } + @Override public int describeContents() { // TODO Auto-generated method stub @@ -87,11 +89,11 @@ public class SdpMasRecord implements Parcelable { public int getSupportedMessageTypes() { return mSupportedMessageTypes; } - + public boolean msgSupported(int msg) { return (mSupportedMessageTypes & msg) != 0; } - + public String getServiceName() { return mServiceName; } @@ -108,29 +110,30 @@ public class SdpMasRecord implements Parcelable { dest.writeString(this.mServiceName); } + @Override - public String toString(){ + public String toString() { String ret = "Bluetooth MAS SDP Record:\n"; - if(mMasInstanceId != -1){ + if (mMasInstanceId != -1) { ret += "Mas Instance Id: " + mMasInstanceId + "\n"; } - if(mRfcommChannelNumber != -1){ + if (mRfcommChannelNumber != -1) { ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; } - if(mL2capPsm != -1){ + if (mL2capPsm != -1) { ret += "L2CAP PSM: " + mL2capPsm + "\n"; } - if(mServiceName != null){ + if (mServiceName != null) { ret += "Service Name: " + mServiceName + "\n"; } - if(mProfileVersion != -1){ + if (mProfileVersion != -1) { ret += "Profile version: " + mProfileVersion + "\n"; } - if(mSupportedMessageTypes != -1){ + if (mSupportedMessageTypes != -1) { ret += "Supported msg types: " + mSupportedMessageTypes + "\n"; } - if(mSupportedFeatures != -1){ + if (mSupportedFeatures != -1) { ret += "Supported features: " + mSupportedFeatures + "\n"; } return ret; @@ -140,6 +143,7 @@ public class SdpMasRecord implements Parcelable { public SdpMasRecord createFromParcel(Parcel in) { return new SdpMasRecord(in); } + public SdpRecord[] newArray(int size) { return new SdpRecord[size]; } diff --git a/framework/java/android/bluetooth/SdpMnsRecord.java b/framework/java/android/bluetooth/SdpMnsRecord.java index c02bb5a1841..94ae6354425 100644 --- a/framework/java/android/bluetooth/SdpMnsRecord.java +++ b/framework/java/android/bluetooth/SdpMnsRecord.java @@ -29,7 +29,7 @@ public class SdpMnsRecord implements Parcelable { int rfcomm_channel_number, int profile_version, int supported_features, - String service_name){ + String service_name) { this.mL2capPsm = l2cap_psm; this.mRfcommChannelNumber = rfcomm_channel_number; this.mSupportedFeatures = supported_features; @@ -37,13 +37,14 @@ public class SdpMnsRecord implements Parcelable { this.mProfileVersion = profile_version; } - public SdpMnsRecord(Parcel in){ - this.mRfcommChannelNumber = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mServiceName = in.readString(); - this.mSupportedFeatures = in.readInt(); - this.mProfileVersion = in.readInt(); + public SdpMnsRecord(Parcel in) { + this.mRfcommChannelNumber = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mServiceName = in.readString(); + this.mSupportedFeatures = in.readInt(); + this.mProfileVersion = in.readInt(); } + @Override public int describeContents() { // TODO Auto-generated method stub @@ -80,23 +81,23 @@ public class SdpMnsRecord implements Parcelable { dest.writeInt(mProfileVersion); } - public String toString(){ + public String toString() { String ret = "Bluetooth MNS SDP Record:\n"; - if(mRfcommChannelNumber != -1){ + if (mRfcommChannelNumber != -1) { ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; } - if(mL2capPsm != -1){ + if (mL2capPsm != -1) { ret += "L2CAP PSM: " + mL2capPsm + "\n"; } - if(mServiceName != null){ + if (mServiceName != null) { ret += "Service Name: " + mServiceName + "\n"; } - if(mSupportedFeatures != -1){ + if (mSupportedFeatures != -1) { ret += "Supported features: " + mSupportedFeatures + "\n"; } - if(mProfileVersion != -1){ - ret += "Profile_version: " + mProfileVersion+"\n"; + if (mProfileVersion != -1) { + ret += "Profile_version: " + mProfileVersion + "\n"; } return ret; } @@ -105,6 +106,7 @@ public class SdpMnsRecord implements Parcelable { public SdpMnsRecord createFromParcel(Parcel in) { return new SdpMnsRecord(in); } + public SdpMnsRecord[] newArray(int size) { return new SdpMnsRecord[size]; } diff --git a/framework/java/android/bluetooth/SdpOppOpsRecord.java b/framework/java/android/bluetooth/SdpOppOpsRecord.java index e0e4007a215..23e301cc06f 100644 --- a/framework/java/android/bluetooth/SdpOppOpsRecord.java +++ b/framework/java/android/bluetooth/SdpOppOpsRecord.java @@ -14,14 +14,15 @@ */ package android.bluetooth; -import java.util.Arrays; - import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; + /** * Data representation of a Object Push Profile Server side SDP record. */ + /** @hide */ public class SdpOppOpsRecord implements Parcelable { @@ -67,13 +68,13 @@ public class SdpOppOpsRecord implements Parcelable { return 0; } - public SdpOppOpsRecord(Parcel in){ + public SdpOppOpsRecord(Parcel in) { this.mRfcommChannel = in.readInt(); this.mL2capPsm = in.readInt(); this.mProfileVersion = in.readInt(); this.mServiceName = in.readString(); int arrayLength = in.readInt(); - if(arrayLength > 0) { + if (arrayLength > 0) { byte[] bytes = new byte[arrayLength]; in.readByteArray(bytes); this.mFormatsList = bytes; @@ -88,7 +89,7 @@ public class SdpOppOpsRecord implements Parcelable { dest.writeInt(mL2capPsm); dest.writeInt(mProfileVersion); dest.writeString(mServiceName); - if(mFormatsList!= null && mFormatsList.length > 0) { + if (mFormatsList != null && mFormatsList.length > 0) { dest.writeInt(mFormatsList.length); dest.writeByteArray(mFormatsList); } else { @@ -96,7 +97,7 @@ public class SdpOppOpsRecord implements Parcelable { } } - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n"); sb.append(" RFCOMM Chan Number: ").append(mRfcommChannel); sb.append("\n L2CAP PSM: ").append(mL2capPsm); @@ -110,6 +111,7 @@ public class SdpOppOpsRecord implements Parcelable { public SdpOppOpsRecord createFromParcel(Parcel in) { return new SdpOppOpsRecord(in); } + public SdpOppOpsRecord[] newArray(int size) { return new SdpOppOpsRecord[size]; } diff --git a/framework/java/android/bluetooth/SdpPseRecord.java b/framework/java/android/bluetooth/SdpPseRecord.java index 2c159ccb83c..5885f174121 100644 --- a/framework/java/android/bluetooth/SdpPseRecord.java +++ b/framework/java/android/bluetooth/SdpPseRecord.java @@ -32,7 +32,7 @@ public class SdpPseRecord implements Parcelable { int profile_version, int supported_features, int supported_repositories, - String service_name){ + String service_name) { this.mL2capPsm = l2cap_psm; this.mRfcommChannelNumber = rfcomm_channel_number; this.mProfileVersion = profile_version; @@ -41,14 +41,15 @@ public class SdpPseRecord implements Parcelable { this.mServiceName = service_name; } - public SdpPseRecord(Parcel in){ - this.mRfcommChannelNumber = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mProfileVersion = in.readInt(); - this.mSupportedFeatures = in.readInt(); - this.mSupportedRepositories = in.readInt(); - this.mServiceName = in.readString(); + public SdpPseRecord(Parcel in) { + this.mRfcommChannelNumber = in.readInt(); + this.mL2capPsm = in.readInt(); + this.mProfileVersion = in.readInt(); + this.mSupportedFeatures = in.readInt(); + this.mSupportedRepositories = in.readInt(); + this.mServiceName = in.readString(); } + @Override public int describeContents() { // TODO Auto-generated method stub @@ -78,6 +79,7 @@ public class SdpPseRecord implements Parcelable { public int getSupportedRepositories() { return mSupportedRepositories; } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mRfcommChannelNumber); @@ -89,25 +91,25 @@ public class SdpPseRecord implements Parcelable { } - public String toString(){ + public String toString() { String ret = "Bluetooth MNS SDP Record:\n"; - if(mRfcommChannelNumber != -1){ + if (mRfcommChannelNumber != -1) { ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; } - if(mL2capPsm != -1){ + if (mL2capPsm != -1) { ret += "L2CAP PSM: " + mL2capPsm + "\n"; } - if(mProfileVersion != -1){ + if (mProfileVersion != -1) { ret += "profile version: " + mProfileVersion + "\n"; } - if(mServiceName != null){ + if (mServiceName != null) { ret += "Service Name: " + mServiceName + "\n"; } - if(mSupportedFeatures != -1){ + if (mSupportedFeatures != -1) { ret += "Supported features: " + mSupportedFeatures + "\n"; } - if(mSupportedRepositories != -1){ + if (mSupportedRepositories != -1) { ret += "Supported repositories: " + mSupportedRepositories + "\n"; } @@ -118,6 +120,7 @@ public class SdpPseRecord implements Parcelable { public SdpPseRecord createFromParcel(Parcel in) { return new SdpPseRecord(in); } + public SdpPseRecord[] newArray(int size) { return new SdpPseRecord[size]; } diff --git a/framework/java/android/bluetooth/SdpRecord.java b/framework/java/android/bluetooth/SdpRecord.java index 6f1065e38d2..714ecd8ac09 100644 --- a/framework/java/android/bluetooth/SdpRecord.java +++ b/framework/java/android/bluetooth/SdpRecord.java @@ -21,7 +21,7 @@ import android.os.Parcelable; import java.util.Arrays; /** @hide */ -public class SdpRecord implements Parcelable{ +public class SdpRecord implements Parcelable { private final byte[] mRawData; private final int mRawSize; @@ -32,12 +32,12 @@ public class SdpRecord implements Parcelable{ + ", rawSize=" + mRawSize + "]"; } - public SdpRecord(int size_record, byte[] record){ + public SdpRecord(int size_record, byte[] record) { this.mRawData = record; this.mRawSize = size_record; } - public SdpRecord(Parcel in){ + public SdpRecord(Parcel in) { this.mRawSize = in.readInt(); this.mRawData = new byte[mRawSize]; in.readByteArray(this.mRawData); @@ -56,6 +56,7 @@ public class SdpRecord implements Parcelable{ } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public SdpRecord createFromParcel(Parcel in) { return new SdpRecord(in); diff --git a/framework/java/android/bluetooth/SdpSapsRecord.java b/framework/java/android/bluetooth/SdpSapsRecord.java index 84a29b92c00..1904118e592 100644 --- a/framework/java/android/bluetooth/SdpSapsRecord.java +++ b/framework/java/android/bluetooth/SdpSapsRecord.java @@ -41,7 +41,7 @@ public class SdpSapsRecord implements Parcelable { @Override public int describeContents() { - return 0; + return 0; } public int getRfcommCannelNumber() { @@ -84,6 +84,7 @@ public class SdpSapsRecord implements Parcelable { public SdpSapsRecord createFromParcel(Parcel in) { return new SdpSapsRecord(in); } + public SdpRecord[] newArray(int size) { return new SdpRecord[size]; } diff --git a/framework/java/android/bluetooth/UidTraffic.java b/framework/java/android/bluetooth/UidTraffic.java index 78013cc0cb0..f74ac75863f 100644 --- a/framework/java/android/bluetooth/UidTraffic.java +++ b/framework/java/android/bluetooth/UidTraffic.java @@ -20,6 +20,7 @@ import android.os.Parcelable; /** * Record of data traffic (in bytes) by an application identified by its UID. + * * @hide */ public class UidTraffic implements Cloneable, Parcelable { diff --git a/framework/java/android/bluetooth/le/AdvertiseCallback.java b/framework/java/android/bluetooth/le/AdvertiseCallback.java index 706f469dab0..4fa8c4f2f53 100644 --- a/framework/java/android/bluetooth/le/AdvertiseCallback.java +++ b/framework/java/android/bluetooth/le/AdvertiseCallback.java @@ -58,7 +58,7 @@ public abstract class AdvertiseCallback { * that the advertising has been started successfully. * * @param settingsInEffect The actual settings used for advertising, which may be different from - * what has been requested. + * what has been requested. */ public void onStartSuccess(AdvertiseSettings settingsInEffect) { } @@ -67,7 +67,7 @@ public abstract class AdvertiseCallback { * Callback when advertising could not be started. * * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start - * failures. + * failures. */ public void onStartFailure(int errorCode) { } diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index bde2d2f890f..7c506dbba51 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -119,10 +119,11 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && - BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && + BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) + && BluetoothLeUtils.equals(mServiceData, other.mServiceData) && - mIncludeDeviceName == other.mIncludeDeviceName && - mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; + mIncludeDeviceName == other.mIncludeDeviceName && + mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override @@ -160,12 +161,12 @@ public final class AdvertiseData implements Parcelable { public static final Parcelable.Creator CREATOR = new Creator() { - @Override + @Override public AdvertiseData[] newArray(int size) { return new AdvertiseData[size]; } - @Override + @Override public AdvertiseData createFromParcel(Parcel in) { Builder builder = new Builder(); ArrayList uuids = in.createTypedArrayList(ParcelUuid.CREATOR); @@ -222,7 +223,7 @@ public final class AdvertiseData implements Parcelable { * @param serviceDataUuid 16-bit UUID of the service the data is associated with * @param serviceData Service data * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is - * empty. + * empty. */ public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null || serviceData == null) { @@ -242,8 +243,8 @@ public final class AdvertiseData implements Parcelable { * * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. * @param manufacturerSpecificData Manufacturer specific data - * @throws IllegalArgumentException If the {@code manufacturerId} is negative or - * {@code manufacturerSpecificData} is null. + * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code + * manufacturerSpecificData} is null. */ public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { if (manufacturerId < 0) { diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 62c68a45eb0..430f9bd97e5 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -121,9 +121,9 @@ public final class AdvertiseSettings implements Parcelable { @Override public String toString() { return "Settings [mAdvertiseMode=" + mAdvertiseMode - + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel - + ", mAdvertiseConnectable=" + mAdvertiseConnectable - + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]"; + + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel + + ", mAdvertiseConnectable=" + mAdvertiseConnectable + + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]"; } @Override @@ -141,12 +141,12 @@ public final class AdvertiseSettings implements Parcelable { public static final Parcelable.Creator CREATOR = new Creator() { - @Override + @Override public AdvertiseSettings[] newArray(int size) { return new AdvertiseSettings[size]; } - @Override + @Override public AdvertiseSettings createFromParcel(Parcel in) { return new AdvertiseSettings(in); } @@ -164,10 +164,10 @@ public final class AdvertiseSettings implements Parcelable { /** * Set advertise mode to control the advertising power and latency. * - * @param advertiseMode Bluetooth LE Advertising mode, can only be one of - * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_POWER}, - * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, or - * {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}. + * @param advertiseMode Bluetooth LE Advertising mode, can only be one of {@link + * AdvertiseSettings#ADVERTISE_MODE_LOW_POWER}, + * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, + * or {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}. * @throws IllegalArgumentException If the advertiseMode is invalid. */ public Builder setAdvertiseMode(int advertiseMode) { @@ -183,10 +183,10 @@ public final class AdvertiseSettings implements Parcelable { * Set advertise TX power level to control the transmission power level for the advertising. * * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_LOW}, - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} or - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}. + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, {@link + * AdvertiseSettings#ADVERTISE_TX_POWER_LOW}, + * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} + * or {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}. * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. */ public Builder setTxPowerLevel(int txPowerLevel) { @@ -201,8 +201,8 @@ public final class AdvertiseSettings implements Parcelable { /** * Set whether the advertisement type should be connectable or non-connectable. * - * @param connectable Controls whether the advertisment type will be connectable (true) - * or non-connectable (false). + * @param connectable Controls whether the advertisment type will be connectable (true) or + * non-connectable (false). */ public Builder setConnectable(boolean connectable) { mConnectable = connectable; @@ -211,14 +211,15 @@ public final class AdvertiseSettings implements Parcelable { /** * Limit advertising to a given amount of time. - * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. - * A value of 0 will disable the time limit. + * + * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. A value + * of 0 will disable the time limit. * @throws IllegalArgumentException If the provided timeout is over 180000 ms. */ public Builder setTimeout(int timeoutMillis) { if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" - + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)"); + + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)"); } mTimeoutMillis = timeoutMillis; return this; diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 1bc211cb83a..b1c122c9e04 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -19,7 +19,6 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; -import android.bluetooth.le.IAdvertisingSetCallback; import android.os.RemoteException; import android.util.Log; @@ -41,19 +40,19 @@ public final class AdvertisingSet { private int advertiserId; /* package */ AdvertisingSet(int advertiserId, - IBluetoothManager bluetoothManager) { + IBluetoothManager bluetoothManager) { this.advertiserId = advertiserId; try { - this.gatt = bluetoothManager.getBluetoothGatt(); + this.gatt = bluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - throw new IllegalStateException("Failed to get Bluetooth"); + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + throw new IllegalStateException("Failed to get Bluetooth"); } } /* package */ void setAdvertiserId(int advertiserId) { - this.advertiserId = advertiserId; + this.advertiserId = advertiserId; } /** @@ -63,18 +62,17 @@ public final class AdvertisingSet { * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param enable whether the advertising should be enabled (true), or disabled (false) - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to - * 65535 (655,350 ms) + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 + * (655,350 ms) * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended - * advertising, even if the duration has not expired. Valid range is - * from 1 to 255. + * controller shall attempt to send prior to terminating the extended advertising, even if the + * duration has not expired. Valid range is from 1 to 255. */ public void enableAdvertising(boolean enable, int duration, int maxExtendedAdvertisingEvents) { try { gatt.enableAdvertisingSet(this.advertiserId, enable, duration, - maxExtendedAdvertisingEvents); + maxExtendedAdvertisingEvents); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -87,11 +85,10 @@ public final class AdvertisingSet { *

        * Advertising data must be empty if non-legacy scannable advertising is used. * - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * advertisement is connectable, three bytes will be added for flags. If the - * update takes place when the advertising set is enabled, the data can be - * maximum 251 bytes long. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, + * three bytes will be added for flags. If the update takes place when the advertising set is + * enabled, the data can be maximum 251 bytes long. */ public void setAdvertisingData(AdvertiseData advertiseData) { try { @@ -107,9 +104,8 @@ public final class AdvertisingSet { * is delivered through {@code callback.onScanResponseDataSet()}. * * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * update takes place when the advertising set is enabled, the data can be - * maximum 251 bytes long. + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place + * when the advertising set is enabled, the data can be maximum 251 bytes long. */ public void setScanResponseData(AdvertiseData scanResponse) { try { @@ -153,10 +149,9 @@ public final class AdvertisingSet { * immediately, the operation status is delivered through * {@code callback.onPeriodicAdvertisingDataSet()}. * - * @param periodicData Periodic advertising data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * update takes place when the periodic advertising is enabled for this set, - * the data can be maximum 251 bytes long. + * @param periodicData Periodic advertising data. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the + * periodic advertising is enabled for this set, the data can be maximum 251 bytes long. */ public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { @@ -170,7 +165,8 @@ public final class AdvertisingSet { * Used to enable/disable periodic advertising. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. * - * @param enable whether the periodic advertising should be enabled (true), or disabled (false). + * @param enable whether the periodic advertising should be enabled (true), or disabled + * (false). */ public void setPeriodicAdvertisingEnabled(boolean enable) { try { @@ -186,9 +182,10 @@ public final class AdvertisingSet { * should ever use it. * * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. + * * @hide */ - public void getOwnAddress(){ + public void getOwnAddress() { try { gatt.getOwnAddress(this.advertiserId); } catch (RemoteException e) { @@ -201,7 +198,7 @@ public final class AdvertisingSet { * * @hide */ - public int getAdvertiserId(){ - return advertiserId; + public int getAdvertiserId() { + return advertiserId; } } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index c3c16a479e7..fa502d37ff3 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -16,8 +16,6 @@ package android.bluetooth.le; -import android.bluetooth.BluetoothDevice; - /** * Bluetooth LE advertising set callbacks, used to deliver advertising operation * status. @@ -65,7 +63,8 @@ public abstract class AdvertisingSetCallback { * @param txPower tx power that will be used for this set. * @param status Status of the operation. */ - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {} + public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) { + } /** * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} @@ -73,16 +72,19 @@ public abstract class AdvertisingSetCallback { * * @param advertisingSet The advertising set. */ - public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {} + public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) { + } /** - * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} indicating - * result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is advertising. + * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} + * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is + * advertising. * * @param advertisingSet The advertising set. * @param status Status of the operation. */ - public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {} + public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating @@ -91,7 +93,8 @@ public abstract class AdvertisingSetCallback { * @param advertisingSet The advertising set. * @param status Status of the operation. */ - public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {} + public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating @@ -100,7 +103,8 @@ public abstract class AdvertisingSetCallback { * @param advertisingSet The advertising set. * @param status Status of the operation. */ - public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {} + public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters} @@ -111,7 +115,8 @@ public abstract class AdvertisingSetCallback { * @param status Status of the operation. */ public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int txPower, int status) {} + int txPower, int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} @@ -122,7 +127,8 @@ public abstract class AdvertisingSetCallback { */ public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int status) {} + int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData} @@ -132,7 +138,8 @@ public abstract class AdvertisingSetCallback { * @param status Status of the operation. */ public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet, - int status) {} + int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled} @@ -142,7 +149,8 @@ public abstract class AdvertisingSetCallback { * @param status Status of the operation. */ public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, - int status) {} + int status) { + } /** * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()} @@ -153,5 +161,6 @@ public abstract class AdvertisingSetCallback { * @param address advertising set bluetooth address. * @hide */ - public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) {} + public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) { + } } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index e9747d8205b..f795861d19a 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -31,9 +31,9 @@ import android.os.Parcelable; public final class AdvertisingSetParameters implements Parcelable { /** - * Advertise on low frequency, around every 1000ms. This is the default and - * preferred advertising mode as it consumes the least power. - */ + * Advertise on low frequency, around every 1000ms. This is the default and + * preferred advertising mode as it consumes the least power. + */ public static final int INTERVAL_HIGH = 1600; /** @@ -108,9 +108,9 @@ public final class AdvertisingSetParameters implements Parcelable { private final int txPowerLevel; private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, - boolean isAnonymous, boolean includeTxPower, - int primaryPhy, int secondaryPhy, - int interval, int txPowerLevel) { + boolean isAnonymous, boolean includeTxPower, + int primaryPhy, int secondaryPhy, + int interval, int txPowerLevel) { this.connectable = connectable; this.scannable = scannable; this.isLegacy = isLegacy; @@ -137,63 +137,81 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Returns whether the advertisement will be connectable. */ - public boolean isConnectable() { return connectable; } + public boolean isConnectable() { + return connectable; + } /** * Returns whether the advertisement will be scannable. */ - public boolean isScannable() { return scannable; } + public boolean isScannable() { + return scannable; + } /** * Returns whether the legacy advertisement will be used. */ - public boolean isLegacy() { return isLegacy; } + public boolean isLegacy() { + return isLegacy; + } /** * Returns whether the advertisement will be anonymous. */ - public boolean isAnonymous() { return isAnonymous; } + public boolean isAnonymous() { + return isAnonymous; + } /** * Returns whether the TX Power will be included. */ - public boolean includeTxPower() { return includeTxPower; } + public boolean includeTxPower() { + return includeTxPower; + } /** * Returns the primary advertising phy. */ - public int getPrimaryPhy() { return primaryPhy; } + public int getPrimaryPhy() { + return primaryPhy; + } /** * Returns the secondary advertising phy. */ - public int getSecondaryPhy() { return secondaryPhy; } + public int getSecondaryPhy() { + return secondaryPhy; + } /** * Returns the advertising interval. */ - public int getInterval() { return interval; } + public int getInterval() { + return interval; + } /** * Returns the TX power level for advertising. */ - public int getTxPowerLevel() { return txPowerLevel; } + public int getTxPowerLevel() { + return txPowerLevel; + } @Override public String toString() { return "AdvertisingSetParameters [connectable=" + connectable - + ", isLegacy=" + isLegacy - + ", isAnonymous=" + isAnonymous - + ", includeTxPower=" + includeTxPower - + ", primaryPhy=" + primaryPhy - + ", secondaryPhy=" + secondaryPhy - + ", interval=" + interval - + ", txPowerLevel=" + txPowerLevel + "]"; + + ", isLegacy=" + isLegacy + + ", isAnonymous=" + isAnonymous + + ", includeTxPower=" + includeTxPower + + ", primaryPhy=" + primaryPhy + + ", secondaryPhy=" + secondaryPhy + + ", interval=" + interval + + ", txPowerLevel=" + txPowerLevel + "]"; } @Override public int describeContents() { - return 0; + return 0; } @Override @@ -210,17 +228,17 @@ public final class AdvertisingSetParameters implements Parcelable { } public static final Parcelable.Creator CREATOR = - new Creator() { - @Override - public AdvertisingSetParameters[] newArray(int size) { - return new AdvertisingSetParameters[size]; - } - - @Override - public AdvertisingSetParameters createFromParcel(Parcel in) { - return new AdvertisingSetParameters(in); - } - }; + new Creator() { + @Override + public AdvertisingSetParameters[] newArray(int size) { + return new AdvertisingSetParameters[size]; + } + + @Override + public AdvertisingSetParameters createFromParcel(Parcel in) { + return new AdvertisingSetParameters(in); + } + }; /** * Builder class for {@link AdvertisingSetParameters}. @@ -242,8 +260,9 @@ public final class AdvertisingSetParameters implements Parcelable { * non-connectable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param connectable Controls whether the advertisement type will be - * connectable (true) or non-connectable (false). + * + * @param connectable Controls whether the advertisement type will be connectable (true) or + * non-connectable (false). */ public Builder setConnectable(boolean connectable) { this.connectable = connectable; @@ -254,8 +273,9 @@ public final class AdvertisingSetParameters implements Parcelable { * Set whether the advertisement type should be scannable. * Legacy advertisements can be both connectable and scannable. Non-legacy * advertisements can be only scannable or only connectable. - * @param scannable Controls whether the advertisement type will be - * scannable (true) or non-scannable (false). + * + * @param scannable Controls whether the advertisement type will be scannable (true) or + * non-scannable (false). */ public Builder setScannable(boolean scannable) { this.scannable = scannable; @@ -291,8 +311,7 @@ public final class AdvertisingSetParameters implements Parcelable { * * This is used only if legacy mode is not used. * - * @param includeTxPower whether TX power should be included in extended - * header + * @param includeTxPower whether TX power should be included in extended header */ public Builder setIncludeTxPower(boolean includeTxPower) { this.includeTxPower = includeTxPower; @@ -306,15 +325,15 @@ public final class AdvertisingSetParameters implements Parcelable { * * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is * supported on this device. - * @param primaryPhy Primary advertising physical channel, can only be - * {@link BluetoothDevice#PHY_LE_1M} or - * {@link BluetoothDevice#PHY_LE_CODED}. + * + * @param primaryPhy Primary advertising physical channel, can only be {@link + * BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the primaryPhy is invalid. */ public Builder setPrimaryPhy(int primaryPhy) { if (primaryPhy != BluetoothDevice.PHY_LE_1M && - primaryPhy != BluetoothDevice.PHY_LE_CODED) { - throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); + primaryPhy != BluetoothDevice.PHY_LE_CODED) { + throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); } this.primaryPhy = primaryPhy; return this; @@ -329,17 +348,16 @@ public final class AdvertisingSetParameters implements Parcelable { * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is * supported on this device. * - * @param secondaryPhy Secondary advertising physical channel, can only be - * one of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M} or - * {@link BluetoothDevice#PHY_LE_CODED}. + * @param secondaryPhy Secondary advertising physical channel, can only be one of {@link + * BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link + * BluetoothDevice#PHY_LE_CODED}. * @throws IllegalArgumentException If the secondaryPhy is invalid. */ public Builder setSecondaryPhy(int secondaryPhy) { if (secondaryPhy != BluetoothDevice.PHY_LE_1M && - secondaryPhy != BluetoothDevice.PHY_LE_2M && - secondaryPhy != BluetoothDevice.PHY_LE_CODED) { - throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); + secondaryPhy != BluetoothDevice.PHY_LE_2M && + secondaryPhy != BluetoothDevice.PHY_LE_CODED) { + throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); } this.secondaryPhy = secondaryPhy; return this; @@ -348,17 +366,15 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set advertising interval. * - * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid - * range is from 160 (100ms) to 16777215 (10,485.759375 s). - * Recommended values are: - * {@link AdvertisingSetParameters#INTERVAL_LOW}, - * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or - * {@link AdvertisingSetParameters#INTERVAL_HIGH}. + * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from + * 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link + * AdvertisingSetParameters#INTERVAL_LOW}, {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, + * or {@link AdvertisingSetParameters#INTERVAL_HIGH}. * @throws IllegalArgumentException If the interval is invalid. */ public Builder setInterval(int interval) { if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { - throw new IllegalArgumentException("unknown interval " + interval); + throw new IllegalArgumentException("unknown interval " + interval); } this.interval = interval; return this; @@ -366,19 +382,19 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Set the transmission power level for the advertising. - * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in - * dBm. The valid range is [-127, 1] Recommended values are: - * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, - * {@link AdvertisingSetParameters#TX_POWER_LOW}, - * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or - * {@link AdvertisingSetParameters#TX_POWER_HIGH}. * + * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid + * range is [-127, 1] Recommended values are: + * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_LOW}, + * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, + * or {@link AdvertisingSetParameters#TX_POWER_HIGH}. * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. */ public Builder setTxPowerLevel(int txPowerLevel) { if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { throw new IllegalArgumentException("unknown txPowerLevel " + - txPowerLevel); + txPowerLevel); } this.txPowerLevel = txPowerLevel; return this; @@ -386,6 +402,7 @@ public final class AdvertisingSetParameters implements Parcelable { /** * Build the {@link AdvertisingSetParameters} object. + * * @throws IllegalStateException if invalid combination of parameters is used. */ public AdvertisingSetParameters build() { @@ -396,28 +413,28 @@ public final class AdvertisingSetParameters implements Parcelable { if (connectable == true && scannable == false) { throw new IllegalStateException( - "Legacy advertisement can't be connectable and non-scannable"); + "Legacy advertisement can't be connectable and non-scannable"); } if (includeTxPower) { throw new IllegalStateException( - "Legacy advertising can't include TX power level in header"); + "Legacy advertising can't include TX power level in header"); } } else { if (connectable && scannable) { throw new IllegalStateException( - "Advertising can't be both connectable and scannable"); + "Advertising can't be both connectable and scannable"); } if (isAnonymous && connectable) { throw new IllegalStateException( - "Advertising can't be both connectable and anonymous"); + "Advertising can't be both connectable and anonymous"); } } return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, - includeTxPower, primaryPhy, - secondaryPhy, interval, txPowerLevel); + includeTxPower, primaryPhy, + secondaryPhy, interval, txPowerLevel); } } } \ No newline at end of file diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 44c2667f2d8..08128deda5c 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -18,7 +18,6 @@ package android.bluetooth.le; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; @@ -31,7 +30,6 @@ import android.util.Log; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.UUID; /** * This class provides a way to perform Bluetooth LE advertise operations, such as starting and @@ -152,13 +150,13 @@ public final class BluetoothLeAdvertiser { int duration = 0; int timeoutMillis = settings.getTimeout(); if (timeoutMillis > 0) { - duration = (timeoutMillis < 10) ? 1 : timeoutMillis/10; + duration = (timeoutMillis < 10) ? 1 : timeoutMillis / 10; } AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); mLegacyAdvertisers.put(callback, wrapped); startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, - duration, 0, wrapped); + duration, 0, wrapped); } } @@ -166,7 +164,7 @@ public final class BluetoothLeAdvertiser { return new AdvertisingSetCallback() { @Override public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, - int status) { + int status) { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { postStartFailure(callback, status); return; @@ -178,10 +176,10 @@ public final class BluetoothLeAdvertiser { /* Legacy advertiser is disabled on timeout */ @Override public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, - int status) { + int status) { if (enabled == true) { Log.e(TAG, "Legacy advertiser should be only disabled on timeout," + - " but was enabled!"); + " but was enabled!"); return; } @@ -218,28 +216,28 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        + * * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * advertisement is connectable, three bytes will be added for flags. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, + * three bytes will be added for flags. * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param callback Callback for advertising set. * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use - * Periodic Advertising feature is made when it's not supported by the - * controller. + * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising + * feature is made when it's not supported by the controller. */ public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, AdvertisingSetCallback callback) { - startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, 0, callback, new Handler(Looper.getMainLooper())); + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback) { + startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, + periodicData, 0, 0, callback, new Handler(Looper.getMainLooper())); } /** @@ -247,30 +245,30 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        + * * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * advertisement is connectable, three bytes will be added for flags. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, + * three bytes will be added for flags. * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param callback Callback for advertising set. * @param handler thread upon which the callbacks will be invoked. * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use - * Periodic Advertising feature is made when it's not supported by the - * controller. + * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising + * feature is made when it's not supported by the controller. */ public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, AdvertisingSetCallback callback, - Handler handler) { + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, AdvertisingSetCallback callback, + Handler handler) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, 0, callback, handler); + periodicData, 0, 0, callback, handler); } /** @@ -278,37 +276,36 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        + * * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * advertisement is connectable, three bytes will be added for flags. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, + * three bytes will be added for flags. * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to - * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 + * (655,350 ms). 0 means advertising should continue until stopped. * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended - * advertising, even if the duration has not expired. Valid range is - * from 1 to 255. 0 means no maximum. + * controller shall attempt to send prior to terminating the extended advertising, even if the + * duration has not expired. Valid range is from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use - * Periodic Advertising feature is made when it's not supported by the - * controller. + * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising + * feature is made when it's not supported by the controller. */ public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int duration, - int maxExtendedAdvertisingEvents, - AdvertisingSetCallback callback) { + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, + AdvertisingSetCallback callback) { startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, callback, - new Handler(Looper.getMainLooper())); + periodicData, duration, maxExtendedAdvertisingEvents, callback, + new Handler(Looper.getMainLooper())); } /** @@ -316,39 +313,39 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through * {@code callback.onAdvertisingSetStarted()}. *

        + * * @param parameters Advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the - * advertisement is connectable, three bytes will be added for flags. + * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, + * three bytes will be added for flags. * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed - * {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to - * 65535 (655,350 ms). 0 means advertising should continue until stopped. + * not be started. + * @param periodicData Periodic advertising data. Size must not exceed {@link + * BluetoothAdapter#getLeMaximumAdvertisingDataLength} + * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 + * (655,350 ms). 0 means advertising should continue until stopped. * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended - * advertising, even if the duration has not expired. Valid range is - * from 1 to 255. 0 means no maximum. + * controller shall attempt to send prior to terminating the extended advertising, even if the + * duration has not expired. Valid range is from 1 to 255. 0 means no maximum. * @param callback Callback for advertising set. * @param handler Thread upon which the callbacks will be invoked. * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use - * Periodic Advertising feature is made when it's not supported by the - * controller, or when maxExtendedAdvertisingEvents is used on a controller - * that doesn't support the LE Extended Advertising + * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising + * feature is made when it's not supported by the controller, or when + * maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended + * Advertising */ public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int duration, - int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback, - Handler handler) { + AdvertiseData advertiseData, AdvertiseData scanResponse, + PeriodicAdvertisingParameters periodicParameters, + AdvertiseData periodicData, int duration, + int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback, + Handler handler) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); + throw new IllegalArgumentException("callback cannot be null"); } boolean isConnectable = parameters.isConnectable(); @@ -370,7 +367,7 @@ public final class BluetoothLeAdvertiser { } if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) - || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) { + || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) { throw new IllegalArgumentException("Unsupported secondary PHY selected"); } @@ -390,20 +387,20 @@ public final class BluetoothLeAdvertiser { boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); if (periodicParameters != null && !supportPeriodic) { throw new IllegalArgumentException( - "Controller does not support LE Periodic Advertising"); + "Controller does not support LE Periodic Advertising"); } } if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) { throw new IllegalArgumentException( - "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); + "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); } if (maxExtendedAdvertisingEvents != 0 && - !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { + !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { throw new IllegalArgumentException( - "Can't use maxExtendedAdvertisingEvents with controller that don't support " + - "LE Extended Advertising"); + "Can't use maxExtendedAdvertisingEvents with controller that don't support " + + "LE Extended Advertising"); } if (duration < 0 || duration > 65535) { @@ -412,26 +409,28 @@ public final class BluetoothLeAdvertiser { IBluetoothGatt gatt; try { - gatt = mBluetoothManager.getBluetoothGatt(); + gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + postStartSetFailure(handler, callback, + AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } IAdvertisingSetCallback wrapped = wrap(callback, handler); if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) { throw new IllegalArgumentException( - "callback instance already associated with advertising"); + "callback instance already associated with advertising"); } try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, wrapped); + periodicData, duration, maxExtendedAdvertisingEvents, wrapped); } catch (RemoteException e) { - Log.e(TAG, "Failed to start advertising set - ", e); - postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; + Log.e(TAG, "Failed to start advertising set - ", e); + postStartSetFailure(handler, callback, + AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; } } @@ -441,7 +440,7 @@ public final class BluetoothLeAdvertiser { */ public void stopAdvertisingSet(AdvertisingSetCallback callback) { if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); + throw new IllegalArgumentException("callback cannot be null"); } IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback); @@ -453,9 +452,9 @@ public final class BluetoothLeAdvertiser { try { gatt = mBluetoothManager.getBluetoothGatt(); gatt.stopAdvertisingSet(wrapped); - } catch (RemoteException e) { + } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising - ", e); - } + } } /** @@ -539,7 +538,7 @@ public final class BluetoothLeAdvertiser { } AdvertisingSet advertisingSet = - new AdvertisingSet(advertiserId, mBluetoothManager); + new AdvertisingSet(advertiserId, mBluetoothManager); mAdvertisingSets.put(advertiserId, advertisingSet); callback.onAdvertisingSetStarted(advertisingSet, txPower, status); } @@ -650,13 +649,13 @@ public final class BluetoothLeAdvertiser { } private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback, - final int error) { + final int error) { handler.post(new Runnable() { - @Override - public void run() { - callback.onAdvertisingSetStarted(null, 0, error); - } - }); + @Override + public void run() { + callback.onAdvertisingSetStarted(null, 0, error); + } + }); } private void postStartFailure(final AdvertiseCallback callback, final int error) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 1eac395bd06..41c4f5ad5df 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -74,6 +74,7 @@ public final class BluetoothLeScanner { /** * Optional extra indicating the callback type, which will be one of * CALLBACK_TYPE_* constants in {@link ScanSettings}. + * * @see ScanCallback#onScanResult(int, ScanResult) */ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; @@ -162,13 +163,13 @@ public final class BluetoothLeScanner { * specify on behalf of which application(s) the work is being done. * * @param workSource {@link WorkSource} identifying the application(s) for which to blame for - * the scan. + * the scan. * @param callback Callback used to deliver scan results. * @hide */ @SystemApi @RequiresPermission(allOf = { - Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); } @@ -180,22 +181,22 @@ public final class BluetoothLeScanner { * @param filters {@link ScanFilter}s for finding exact BLE devices. * @param settings Settings for the scan. * @param workSource {@link WorkSource} identifying the application(s) for which to blame for - * the scan. + * the scan. * @param callback Callback used to deliver scan results. * @hide */ @SystemApi @RequiresPermission(allOf = { - Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS }) + Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(List filters, ScanSettings settings, - final WorkSource workSource, final ScanCallback callback) { + final WorkSource workSource, final ScanCallback callback) { startScan(filters, settings, workSource, callback, null, null); } private int startScan(List filters, ScanSettings settings, - final WorkSource workSource, final ScanCallback callback, - final PendingIntent callbackIntent, - List> resultStorages) { + final WorkSource workSource, final ScanCallback callback, + final PendingIntent callbackIntent, + List> resultStorages) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null && callbackIntent == null) { throw new IllegalArgumentException("callback is null"); @@ -218,11 +219,11 @@ public final class BluetoothLeScanner { } if (!isSettingsConfigAllowedForScan(settings)) { return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); + ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); } if (!isHardwareResourcesAvailableForScan(settings)) { return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); + ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); } if (!isSettingsAndFilterComboAllowed(settings, filters)) { return postCallbackErrorOrReturn(callback, @@ -285,7 +286,7 @@ public final class BluetoothLeScanner { * will be delivered through the {@code callback}. * * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one - * used to start scan. + * used to start scan. */ public void flushPendingScanResults(ScanCallback callback) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); @@ -486,12 +487,13 @@ public final class BluetoothLeScanner { // Check null in case the scan has been stopped synchronized (this) { - if (mScannerId <= 0) + if (mScannerId <= 0) { return; + } } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { - @Override + @Override public void run() { if (onFound) { mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, @@ -510,8 +512,9 @@ public final class BluetoothLeScanner { Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); } synchronized (this) { - if (mScannerId <= 0) + if (mScannerId <= 0) { return; + } } postCallbackError(mScanCallback, errorCode); } @@ -549,11 +552,11 @@ public final class BluetoothLeScanner { } private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, - List filterList) { + List filterList) { final int callbackType = settings.getCallbackType(); // If onlost/onfound is requested, a non-empty filter is expected if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH - | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { + | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { if (filterList == null) { return false; } diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java index c40256b8906..afec72bc36e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -128,11 +128,12 @@ public class BluetoothLeUtils { /** * Ensure Bluetooth is turned on. * - * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not - * {@link BluetoothAdapter#STATE_ON}. + * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link + * BluetoothAdapter#STATE_ON}. */ static void checkAdapterStateOn(BluetoothAdapter adapter) { - if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) { + if (adapter == null + || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); } } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java index 364b575b4d8..14ac911fcb7 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -22,8 +22,8 @@ import android.bluetooth.BluetoothDevice; * Bluetooth LE periodic advertising callbacks, used to deliver periodic * advertising operation status. * - * @see PeriodicAdvertisingManager#createSync * @hide + * @see PeriodicAdvertisingManager#createSync */ public abstract class PeriodicAdvertisingCallback { @@ -40,7 +40,7 @@ public abstract class PeriodicAdvertisingCallback { public static final int SYNC_NO_RESPONSE = 1; /** - * Sync failed to be established because controller can't support more syncs. + * Sync failed to be established because controller can't support more syncs. */ public static final int SYNC_NO_RESOURCES = 2; @@ -51,28 +51,31 @@ public abstract class PeriodicAdvertisingCallback { * @param syncHandle handle used to identify this synchronization. * @param device remote device. * @param advertisingSid synchronized advertising set id. - * @param skip The number of periodic advertising packets that can be skipped - * after a successful receive in force. @see PeriodicAdvertisingManager#createSync - * @param timeout Synchronization timeout for the periodic advertising in force. One - * unit is 10ms. @see PeriodicAdvertisingManager#createSync + * @param skip The number of periodic advertising packets that can be skipped after a successful + * receive in force. @see PeriodicAdvertisingManager#createSync + * @param timeout Synchronization timeout for the periodic advertising in force. One unit is + * 10ms. @see PeriodicAdvertisingManager#createSync * @param timeout * @param status operation status. */ public void onSyncEstablished(int syncHandle, BluetoothDevice device, - int advertisingSid, int skip, int timeout, - int status) {} + int advertisingSid, int skip, int timeout, + int status) { + } /** * Callback when periodic advertising report is received. * * @param report periodic advertising report. */ - public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {} + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { + } /** * Callback when periodic advertising synchronization was lost. * * @param syncHandle handle used to identify this synchronization. */ - public void onSyncLost(int syncHandle) {} + public void onSyncLost(int syncHandle) { + } } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index d9c2d8819a3..5e7f4c08bdf 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; + import java.util.IdentityHashMap; import java.util.Map; @@ -37,202 +38,207 @@ import java.util.Map; *

        * Note: Most of the methods here require * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * * @hide */ public final class PeriodicAdvertisingManager { - private static final String TAG = "PeriodicAdvertisingManager"; - - private static final int SKIP_MIN = 0; - private static final int SKIP_MAX = 499; - private static final int TIMEOUT_MIN = 10; - private static final int TIMEOUT_MAX = 16384; - - private static final int SYNC_STARTING = -1; - - private final IBluetoothManager mBluetoothManager; - private BluetoothAdapter mBluetoothAdapter; - - /* maps callback, to callback wrapper and sync handle */ - Map callbackWrappers; - - /** - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. - * - * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. - * @hide - */ - public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - callbackWrappers = new IdentityHashMap<>(); - } - - /** - * Synchronize with periodic advertising pointed to by the {@code scanResult}. - * The {@code scanResult} used must contain a valid advertisingSid. First - * call to registerSync will use the {@code skip} and {@code timeout} provided. - * Subsequent calls from other apps, trying to sync with same set will reuse - * existing sync, thus {@code skip} and {@code timeout} values will not take - * effect. The values in effect will be returned in - * {@link PeriodicAdvertisingCallback#onSyncEstablished}. - * - * @param scanResult Scan result containing advertisingSid. - * @param skip The number of periodic advertising packets that can be skipped - * after a successful receive. Must be between 0 and 499. - * @param timeout Synchronization timeout for the periodic advertising. One - * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). - * @param callback Callback used to deliver all operations status. - * @throws IllegalArgumentException if {@code scanResult} is null or {@code - * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. - */ - public void registerSync(ScanResult scanResult, int skip, int timeout, - PeriodicAdvertisingCallback callback) { - registerSync(scanResult, skip, timeout, callback, null); - } - - /** - * Synchronize with periodic advertising pointed to by the {@code scanResult}. - * The {@code scanResult} used must contain a valid advertisingSid. First - * call to registerSync will use the {@code skip} and {@code timeout} provided. - * Subsequent calls from other apps, trying to sync with same set will reuse - * existing sync, thus {@code skip} and {@code timeout} values will not take - * effect. The values in effect will be returned in - * {@link PeriodicAdvertisingCallback#onSyncEstablished}. - * - * @param scanResult Scan result containing advertisingSid. - * @param skip The number of periodic advertising packets that can be skipped - * after a successful receive. Must be between 0 and 499. - * @param timeout Synchronization timeout for the periodic advertising. One - * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s). - * @param callback Callback used to deliver all operations status. - * @param handler thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException if {@code scanResult} is null or {@code - * skip} is invalid or {@code timeout} is invalid or {@code callback} is null. - */ - public void registerSync(ScanResult scanResult, int skip, int timeout, - PeriodicAdvertisingCallback callback, Handler handler) { - if (callback == null) { - throw new IllegalArgumentException("callback can't be null"); - } + private static final String TAG = "PeriodicAdvertisingManager"; - if (scanResult == null) { - throw new IllegalArgumentException("scanResult can't be null"); - } + private static final int SKIP_MIN = 0; + private static final int SKIP_MAX = 499; + private static final int TIMEOUT_MIN = 10; + private static final int TIMEOUT_MAX = 16384; - if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { - throw new IllegalArgumentException("scanResult must contain a valid sid"); - } + private static final int SYNC_STARTING = -1; - if (skip < SKIP_MIN || skip > SKIP_MAX) { - throw new IllegalArgumentException( - "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); - } + private final IBluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; - if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { - throw new IllegalArgumentException( - "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); - } + /* maps callback, to callback wrapper and sync handle */ + Map callbackWrappers; - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), - skip, timeout, - PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); - return; + /** + * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. + * + * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. + * @hide + */ + public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { + mBluetoothManager = bluetoothManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + callbackWrappers = new IdentityHashMap<>(); } - if (handler == null) - handler = new Handler(Looper.getMainLooper()); - - IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); - callbackWrappers.put(callback, wrapped); - - try { - gatt.registerSync(scanResult, skip, timeout, wrapped); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register sync - ", e); - return; - } - } - - /** - * Cancel pending attempt to create sync, or terminate existing sync. - * - * @param callback Callback used to deliver all operations status. - * @throws IllegalArgumentException if {@code callback} is null, or not a properly - * registered callback. - */ - public void unregisterSync(PeriodicAdvertisingCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback can't be null"); + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped after a successful + * receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must + * be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or + * {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback) { + registerSync(scanResult, skip, timeout, callback, null); } - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - return; + /** + * Synchronize with periodic advertising pointed to by the {@code scanResult}. + * The {@code scanResult} used must contain a valid advertisingSid. First + * call to registerSync will use the {@code skip} and {@code timeout} provided. + * Subsequent calls from other apps, trying to sync with same set will reuse + * existing sync, thus {@code skip} and {@code timeout} values will not take + * effect. The values in effect will be returned in + * {@link PeriodicAdvertisingCallback#onSyncEstablished}. + * + * @param scanResult Scan result containing advertisingSid. + * @param skip The number of periodic advertising packets that can be skipped after a successful + * receive. Must be between 0 and 499. + * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must + * be between 10 (100ms) and 16384 (163.84s). + * @param callback Callback used to deliver all operations status. + * @param handler thread upon which the callbacks will be invoked. + * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or + * {@code timeout} is invalid or {@code callback} is null. + */ + public void registerSync(ScanResult scanResult, int skip, int timeout, + PeriodicAdvertisingCallback callback, Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + if (scanResult == null) { + throw new IllegalArgumentException("scanResult can't be null"); + } + + if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { + throw new IllegalArgumentException("scanResult must contain a valid sid"); + } + + if (skip < SKIP_MIN || skip > SKIP_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { + throw new IllegalArgumentException( + "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), + skip, timeout, + PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); + return; + } + + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } + + IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); + callbackWrappers.put(callback, wrapped); + + try { + gatt.registerSync(scanResult, skip, timeout, wrapped); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register sync - ", e); + return; + } } - IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); - if (wrapper == null) { - throw new IllegalArgumentException("callback was not properly registered"); + /** + * Cancel pending attempt to create sync, or terminate existing sync. + * + * @param callback Callback used to deliver all operations status. + * @throws IllegalArgumentException if {@code callback} is null, or not a properly registered + * callback. + */ + public void unregisterSync(PeriodicAdvertisingCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + return; + } + + IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); + if (wrapper == null) { + throw new IllegalArgumentException("callback was not properly registered"); + } + + try { + gatt.unregisterSync(wrapper); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel sync creation - ", e); + return; + } } - try { - gatt.unregisterSync(wrapper); - } catch (RemoteException e) { - Log.e(TAG, "Failed to cancel sync creation - ", e); - return; + private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, + Handler handler) { + return new IPeriodicAdvertisingCallback.Stub() { + public void onSyncEstablished(int syncHandle, BluetoothDevice device, + int advertisingSid, int skip, int timeout, int status) { + + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, + timeout, + status); + + if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { + // App can still unregister the sync until notified it failed. Remove + // callback + // after app was notifed. + callbackWrappers.remove(callback); + } + } + }); + } + + public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onPeriodicAdvertisingReport(report); + } + }); + } + + public void onSyncLost(int syncHandle) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncLost(syncHandle); + // App can still unregister the sync until notified it's lost. Remove callback after + // app was notifed. + callbackWrappers.remove(callback); + } + }); + } + }; } - } - - private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { - return new IPeriodicAdvertisingCallback.Stub() { - public void onSyncEstablished(int syncHandle, BluetoothDevice device, - int advertisingSid, int skip, int timeout, int status) { - - handler.post(new Runnable() { - @Override - public void run() { - callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout, - status); - - if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { - // App can still unregister the sync until notified it failed. Remove callback - // after app was notifed. - callbackWrappers.remove(callback); - } - } - }); - } - - public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onPeriodicAdvertisingReport(report); - } - }); - } - - public void onSyncLost(int syncHandle) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onSyncLost(syncHandle); - // App can still unregister the sync until notified it's lost. Remove callback after - // app was notifed. - callbackWrappers.remove(callback); - } - }); - } - }; - } } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index cf8f08fddd9..5bd47756c23 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -45,13 +45,17 @@ public final class PeriodicAdvertisingParameters implements Parcelable { /** * Returns whether the TX Power will be included. */ - public boolean getIncludeTxPower() { return includeTxPower; } + public boolean getIncludeTxPower() { + return includeTxPower; + } /** * Returns the periodic advertising interval, in 1.25ms unit. * Valid values are from 80 (100ms) to 65519 (81.89875s). */ - public int getInterval() { return interval; } + public int getInterval() { + return interval; + } @Override public int describeContents() { @@ -65,18 +69,18 @@ public final class PeriodicAdvertisingParameters implements Parcelable { } public static final Parcelable - .Creator CREATOR = - new Creator() { - @Override - public PeriodicAdvertisingParameters[] newArray(int size) { - return new PeriodicAdvertisingParameters[size]; - } - - @Override - public PeriodicAdvertisingParameters createFromParcel(Parcel in) { - return new PeriodicAdvertisingParameters(in); - } - }; + .Creator CREATOR = + new Creator() { + @Override + public PeriodicAdvertisingParameters[] newArray(int size) { + return new PeriodicAdvertisingParameters[size]; + } + + @Override + public PeriodicAdvertisingParameters createFromParcel(Parcel in) { + return new PeriodicAdvertisingParameters(in); + } + }; public static final class Builder { private boolean includeTxPower = false; @@ -95,12 +99,13 @@ public final class PeriodicAdvertisingParameters implements Parcelable { * Set advertising interval for periodic advertising, in 1.25ms unit. * Valid values are from 80 (100ms) to 65519 (81.89875s). * Value from range [interval, interval+20ms] will be picked as the actual value. + * * @throws IllegalArgumentException If the interval is invalid. */ public Builder setInterval(int interval) { if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN + - "-" + INTERVAL_MAX + ")"); + "-" + INTERVAL_MAX + ")"); } this.interval = interval; return this; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 51b93cbd64d..9f1099b125d 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -24,6 +24,7 @@ import java.util.Objects; /** * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. + * * @hide */ public final class PeriodicAdvertisingReport implements Parcelable { @@ -53,10 +54,9 @@ public final class PeriodicAdvertisingReport implements Parcelable { /** * Constructor of periodic advertising result. - * */ public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi, - int dataStatus, ScanRecord data) { + int dataStatus, ScanRecord data) { this.syncHandle = syncHandle; this.txPower = txPower; this.rssi = rssi; @@ -157,29 +157,30 @@ public final class PeriodicAdvertisingReport implements Parcelable { } PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj; return (syncHandle == other.syncHandle) && - (txPower == other.txPower) && - (rssi == other.rssi) && - (dataStatus == other.dataStatus) && - Objects.equals(data, other.data) && - (timestampNanos == other.timestampNanos); + (txPower == other.txPower) && + (rssi == other.rssi) && + (dataStatus == other.dataStatus) && + Objects.equals(data, other.data) && + (timestampNanos == other.timestampNanos); } @Override public String toString() { - return "PeriodicAdvertisingReport{syncHandle=" + syncHandle + - ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus + - ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}'; + return "PeriodicAdvertisingReport{syncHandle=" + syncHandle + + ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus + + ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}'; } - public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public PeriodicAdvertisingReport createFromParcel(Parcel source) { - return new PeriodicAdvertisingReport(source); - } - - @Override - public PeriodicAdvertisingReport[] newArray(int size) { - return new PeriodicAdvertisingReport[size]; - } - }; + public static final Parcelable.Creator CREATOR = + new Creator() { + @Override + public PeriodicAdvertisingReport createFromParcel(Parcel source) { + return new PeriodicAdvertisingReport(source); + } + + @Override + public PeriodicAdvertisingReport[] newArray(int size) { + return new PeriodicAdvertisingReport[size]; + } + }; } diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java index 748f97d2bc7..75139b48ce2 100644 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -80,14 +80,14 @@ public final class ResultStorageDescriptor implements Parcelable { public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public ResultStorageDescriptor createFromParcel(Parcel source) { - return new ResultStorageDescriptor(source); - } + @Override + public ResultStorageDescriptor createFromParcel(Parcel source) { + return new ResultStorageDescriptor(source); + } - @Override - public ResultStorageDescriptor[] newArray(int size) { - return new ResultStorageDescriptor[size]; - } - }; + @Override + public ResultStorageDescriptor[] newArray(int size) { + return new ResultStorageDescriptor[size]; + } + }; } diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index aff2e909502..fcbc2c74f0d 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -46,6 +46,7 @@ public abstract class ScanCallback { /** * Fails to start scan as it is out of hardware resources. + * * @hide */ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; @@ -55,10 +56,9 @@ public abstract class ScanCallback { /** * Callback when a BLE advertisement has been found. * - * @param callbackType Determines how this callback was triggered. Could be one of - * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, - * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or - * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} + * @param callbackType Determines how this callback was triggered. Could be one of {@link + * ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or + * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} * @param result A Bluetooth LE scan result. */ public void onScanResult(int callbackType, ScanResult result) { diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 457096bf843..f91cb7559d3 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -71,7 +71,7 @@ public final class ScanFilter implements Parcelable { private final byte[] mManufacturerDataMask; /** @hide */ - public static final ScanFilter EMPTY = new ScanFilter.Builder().build() ; + public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, @@ -148,67 +148,67 @@ public final class ScanFilter implements Parcelable { public static final Creator CREATOR = new Creator() { - @Override - public ScanFilter[] newArray(int size) { - return new ScanFilter[size]; - } - - @Override - public ScanFilter createFromParcel(Parcel in) { - Builder builder = new Builder(); - if (in.readInt() == 1) { - builder.setDeviceName(in.readString()); - } - if (in.readInt() == 1) { - builder.setDeviceAddress(in.readString()); - } - if (in.readInt() == 1) { - ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.setServiceUuid(uuid); - if (in.readInt() == 1) { - ParcelUuid uuidMask = in.readParcelable( - ParcelUuid.class.getClassLoader()); - builder.setServiceUuid(uuid, uuidMask); - } - } - if (in.readInt() == 1) { - ParcelUuid servcieDataUuid = - in.readParcelable(ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - if (in.readInt() == 0) { - builder.setServiceData(servcieDataUuid, serviceData); - } else { - int serviceDataMaskLength = in.readInt(); - byte[] serviceDataMask = new byte[serviceDataMaskLength]; - in.readByteArray(serviceDataMask); - builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); - } - } - } + @Override + public ScanFilter[] newArray(int size) { + return new ScanFilter[size]; + } - int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - if (in.readInt() == 0) { - builder.setManufacturerData(manufacturerId, manufacturerData); - } else { - int manufacturerDataMaskLength = in.readInt(); - byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; - in.readByteArray(manufacturerDataMask); - builder.setManufacturerData(manufacturerId, manufacturerData, - manufacturerDataMask); - } + @Override + public ScanFilter createFromParcel(Parcel in) { + Builder builder = new Builder(); + if (in.readInt() == 1) { + builder.setDeviceName(in.readString()); + } + if (in.readInt() == 1) { + builder.setDeviceAddress(in.readString()); + } + if (in.readInt() == 1) { + ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); + builder.setServiceUuid(uuid); + if (in.readInt() == 1) { + ParcelUuid uuidMask = in.readParcelable( + ParcelUuid.class.getClassLoader()); + builder.setServiceUuid(uuid, uuidMask); + } + } + if (in.readInt() == 1) { + ParcelUuid servcieDataUuid = + in.readParcelable(ParcelUuid.class.getClassLoader()); + if (in.readInt() == 1) { + int serviceDataLength = in.readInt(); + byte[] serviceData = new byte[serviceDataLength]; + in.readByteArray(serviceData); + if (in.readInt() == 0) { + builder.setServiceData(servcieDataUuid, serviceData); + } else { + int serviceDataMaskLength = in.readInt(); + byte[] serviceDataMask = new byte[serviceDataMaskLength]; + in.readByteArray(serviceDataMask); + builder.setServiceData( + servcieDataUuid, serviceData, serviceDataMask); } + } + } - return builder.build(); + int manufacturerId = in.readInt(); + if (in.readInt() == 1) { + int manufacturerDataLength = in.readInt(); + byte[] manufacturerData = new byte[manufacturerDataLength]; + in.readByteArray(manufacturerData); + if (in.readInt() == 0) { + builder.setManufacturerData(manufacturerId, manufacturerData); + } else { + int manufacturerDataMaskLength = in.readInt(); + byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; + in.readByteArray(manufacturerDataMask); + builder.setManufacturerData(manufacturerId, manufacturerData, + manufacturerDataMask); } - }; + } + + return builder.build(); + } + }; /** * Returns the filter set the device name field of Bluetooth advertisement data. @@ -288,7 +288,7 @@ public final class ScanFilter implements Parcelable { // Scan record is null but there exist filters on it. if (scanRecord == null && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null - || mServiceData != null)) { + || mServiceData != null)) { return false; } @@ -386,12 +386,12 @@ public final class ScanFilter implements Parcelable { @Override public int hashCode() { return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, - Arrays.hashCode(mManufacturerData), - Arrays.hashCode(mManufacturerDataMask), - mServiceDataUuid, - Arrays.hashCode(mServiceData), - Arrays.hashCode(mServiceDataMask), - mServiceUuid, mServiceUuidMask); + Arrays.hashCode(mManufacturerData), + Arrays.hashCode(mManufacturerDataMask), + mServiceDataUuid, + Arrays.hashCode(mServiceData), + Arrays.hashCode(mServiceDataMask), + mServiceUuid, mServiceUuidMask); } @Override @@ -417,6 +417,7 @@ public final class ScanFilter implements Parcelable { /** * Checks if the scanfilter is empty + * * @hide */ public boolean isAllFieldsEmpty() { @@ -454,8 +455,8 @@ public final class ScanFilter implements Parcelable { * Set filter on device address. * * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated using - * {@link BluetoothAdapter#checkBluetoothAddress}. + * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link + * BluetoothAdapter#checkBluetoothAddress}. * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { @@ -480,8 +481,8 @@ public final class ScanFilter implements Parcelable { * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the * bit in {@code serviceUuid}, and 0 to ignore that bit. * - * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but - * {@code uuidMask} is not {@code null}. + * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code + * uuidMask} is not {@code null}. */ public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) { if (mUuidMask != null && mServiceUuid == null) { @@ -513,9 +514,9 @@ public final class ScanFilter implements Parcelable { *

        * The {@code serviceDataMask} must have the same length of the {@code serviceData}. * - * @throws IllegalArgumentException If {@code serviceDataUuid} is null or - * {@code serviceDataMask} is {@code null} while {@code serviceData} is not or - * {@code serviceDataMask} and {@code serviceData} has different length. + * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code + * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code + * serviceDataMask} and {@code serviceData} has different length. */ public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask) { @@ -563,10 +564,9 @@ public final class ScanFilter implements Parcelable { *

        * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}. * - * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or - * {@code manufacturerData} is null while {@code manufacturerDataMask} is not, - * or {@code manufacturerData} and {@code manufacturerDataMask} have different - * length. + * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code + * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code + * manufacturerData} and {@code manufacturerDataMask} have different length. */ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 914e8fd9e07..9e5a29aa344 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -231,9 +231,9 @@ public final class ScanRecord { case DATA_TYPE_SERVICE_DATA_128_BIT: int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) { - serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; + serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) { - serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; + serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; } byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, @@ -276,7 +276,8 @@ public final class ScanRecord { @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString( + mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index e552398e9e2..774382243b8 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -98,7 +98,8 @@ public final class ScanResult implements Parcelable { * @param scanRecord Scan record including both advertising data and scan response data. * @param rssi Received signal strength. * @param timestampNanos Timestamp at which the scan result was observed. - * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)} + * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, + * ScanRecord, long)} */ public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos) { @@ -129,8 +130,8 @@ public final class ScanResult implements Parcelable { * @param timestampNanos Timestamp at which the scan result was observed. */ public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy, - int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, - ScanRecord scanRecord, long timestampNanos) { + int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, + ScanRecord scanRecord, long timestampNanos) { mDevice = device; mEventType = eventType; mPrimaryPhy = primaryPhy; @@ -254,7 +255,9 @@ public final class ScanResult implements Parcelable { * Can be one of {@link BluetoothDevice#PHY_LE_1M} or * {@link BluetoothDevice#PHY_LE_CODED}. */ - public int getPrimaryPhy() { return mPrimaryPhy; } + public int getPrimaryPhy() { + return mPrimaryPhy; + } /** * Returns the secondary Physical Layer @@ -264,21 +267,27 @@ public final class ScanResult implements Parcelable { * or {@link ScanResult#PHY_UNUSED} - if the advertisement * was not received on a secondary physical channel. */ - public int getSecondaryPhy() { return mSecondaryPhy; } + public int getSecondaryPhy() { + return mSecondaryPhy; + } /** * Returns the advertising set id. * May return {@link ScanResult#SID_NOT_PRESENT} if * no set id was is present. */ - public int getAdvertisingSid() { return mAdvertisingSid; } + public int getAdvertisingSid() { + return mAdvertisingSid; + } /** * Returns the transmit power in dBm. * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT} * indicates that the TX power is not present. */ - public int getTxPower() { return mTxPower; } + public int getTxPower() { + return mTxPower; + } /** * Returns the periodic advertising interval in units of 1.25ms. @@ -293,9 +302,9 @@ public final class ScanResult implements Parcelable { @Override public int hashCode() { return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos, - mEventType, mPrimaryPhy, mSecondaryPhy, - mAdvertisingSid, mTxPower, - mPeriodicAdvertisingInterval); + mEventType, mPrimaryPhy, mSecondaryPhy, + mAdvertisingSid, mTxPower, + mPeriodicAdvertisingInterval); } @Override @@ -308,33 +317,33 @@ public final class ScanResult implements Parcelable { } ScanResult other = (ScanResult) obj; return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.equals(mScanRecord, other.mScanRecord) && - (mTimestampNanos == other.mTimestampNanos) && - mEventType == other.mEventType && - mPrimaryPhy == other.mPrimaryPhy && - mSecondaryPhy == other.mSecondaryPhy && - mAdvertisingSid == other.mAdvertisingSid && - mTxPower == other.mTxPower && - mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; + Objects.equals(mScanRecord, other.mScanRecord) && + (mTimestampNanos == other.mTimestampNanos) && + mEventType == other.mEventType && + mPrimaryPhy == other.mPrimaryPhy && + mSecondaryPhy == other.mSecondaryPhy && + mAdvertisingSid == other.mAdvertisingSid && + mTxPower == other.mTxPower && + mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; } @Override public String toString() { - return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + - Objects.toString(mScanRecord) + ", rssi=" + mRssi + - ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + - ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + - ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + - ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; + return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + + Objects.toString(mScanRecord) + ", rssi=" + mRssi + + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; } public static final Parcelable.Creator CREATOR = new Creator() { - @Override + @Override public ScanResult createFromParcel(Parcel source) { return new ScanResult(source); } - @Override + @Override public ScanResult[] newArray(int size) { return new ScanResult[size]; } diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 36e48e9bd0b..d2792e0aa45 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -202,8 +202,8 @@ public final class ScanSettings implements Parcelable { } private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayMillis, int matchMode, - int numOfMatchesPerFilter, boolean legacy, int phy) { + long reportDelayMillis, int matchMode, + int numOfMatchesPerFilter, boolean legacy, int phy) { mScanMode = scanMode; mCallbackType = callbackType; mScanResultType = scanResultType; @@ -244,16 +244,16 @@ public final class ScanSettings implements Parcelable { public static final Parcelable.Creator CREATOR = new Creator() { - @Override - public ScanSettings[] newArray(int size) { - return new ScanSettings[size]; - } + @Override + public ScanSettings[] newArray(int size) { + return new ScanSettings[size]; + } - @Override - public ScanSettings createFromParcel(Parcel in) { - return new ScanSettings(in); - } - }; + @Override + public ScanSettings createFromParcel(Parcel in) { + return new ScanSettings(in); + } + }; /** * Builder for {@link ScanSettings}. @@ -264,7 +264,7 @@ public final class ScanSettings implements Parcelable { private int mScanResultType = SCAN_RESULT_TYPE_FULL; private long mReportDelayMillis = 0; private int mMatchMode = MATCH_MODE_AGGRESSIVE; - private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; + private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; private boolean mLegacy = true; private int mPhy = PHY_LE_ALL_SUPPORTED; @@ -272,8 +272,7 @@ public final class ScanSettings implements Parcelable { * Set scan mode for Bluetooth LE scan. * * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER}, - * {@link ScanSettings#SCAN_MODE_BALANCED} or - * {@link ScanSettings#SCAN_MODE_LOW_LATENCY}. + * {@link ScanSettings#SCAN_MODE_BALANCED} or {@link ScanSettings#SCAN_MODE_LOW_LATENCY}. * @throws IllegalArgumentException If the {@code scanMode} is invalid. */ public Builder setScanMode(int scanMode) { @@ -312,9 +311,8 @@ public final class ScanSettings implements Parcelable { /** * Set scan result type for Bluetooth LE scan. * - * @param scanResultType Type for scan result, could be either - * {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or - * {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}. + * @param scanResultType Type for scan result, could be either {@link + * ScanSettings#SCAN_RESULT_TYPE_FULL} or {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}. * @throws IllegalArgumentException If the {@code scanResultType} is invalid. * @hide */ @@ -333,8 +331,8 @@ public final class ScanSettings implements Parcelable { * Set report delay timestamp for Bluetooth LE scan. * * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of - * results immediately. Values > 0 causes the scan results to be queued up and - * delivered after the requested delay or when the internal buffers fill up. + * results immediately. Values > 0 causes the scan results to be queued up and delivered + * after the requested delay or when the internal buffers fill up. * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. */ public Builder setReportDelay(long reportDelayMillis) { @@ -349,9 +347,9 @@ public final class ScanSettings implements Parcelable { * Set the number of matches for Bluetooth LE scan filters hardware match * * @param numOfMatches The num of matches can be one of - * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or - * {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or - * {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} + * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} + * or {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or {@link + * ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} * @throws IllegalArgumentException If the {@code matchMode} is invalid. */ public Builder setNumOfMatches(int numOfMatches) { @@ -366,9 +364,8 @@ public final class ScanSettings implements Parcelable { /** * Set match mode for Bluetooth LE scan filters hardware match * - * @param matchMode The match mode can be one of - * {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or - * {@link ScanSettings#MATCH_MODE_STICKY} + * @param matchMode The match mode can be one of {@link ScanSettings#MATCH_MODE_AGGRESSIVE} + * or {@link ScanSettings#MATCH_MODE_STICKY} * @throws IllegalArgumentException If the {@code matchMode} is invalid. */ public Builder setMatchMode(int matchMode) { @@ -402,10 +399,8 @@ public final class ScanSettings implements Parcelable { * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}. * Selecting an unsupported phy will result in failure to start scan. * - * @param phy Can be one of - * {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_CODED} or - * {@link ScanSettings#PHY_LE_ALL_SUPPORTED} + * @param phy Can be one of {@link BluetoothDevice#PHY_LE_1M}, {@link + * BluetoothDevice#PHY_LE_CODED} or {@link ScanSettings#PHY_LE_ALL_SUPPORTED} */ public Builder setPhy(int phy) { mPhy = phy; @@ -417,8 +412,8 @@ public final class ScanSettings implements Parcelable { */ public ScanSettings build() { return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayMillis, mMatchMode, - mNumOfMatchesPerFilter, mLegacy, mPhy); + mReportDelayMillis, mMatchMode, + mNumOfMatchesPerFilter, mLegacy, mPhy); } } } diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java index 685b1748f9a..a753aa6fef1 100644 --- a/framework/java/android/bluetooth/le/TruncatedFilter.java +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.SystemApi; + import java.util.List; /** -- GitLab From 9e045d26d0128826b40520f523307d8d16473779 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 22 Aug 2017 21:21:23 -0700 Subject: [PATCH 0825/1408] 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/BluetoothA2dp.java | 22 ++- .../android/bluetooth/BluetoothA2dpSink.java | 20 +- .../BluetoothActivityEnergyInfo.java | 8 +- .../android/bluetooth/BluetoothAdapter.java | 6 +- .../bluetooth/BluetoothAudioConfig.java | 7 +- .../bluetooth/BluetoothAvrcpController.java | 8 +- .../BluetoothAvrcpPlayerSettings.java | 10 +- .../android/bluetooth/BluetoothClass.java | 2 + .../bluetooth/BluetoothCodecConfig.java | 58 +++--- .../bluetooth/BluetoothCodecStatus.java | 19 +- .../android/bluetooth/BluetoothDevice.java | 52 ++--- .../java/android/bluetooth/BluetoothGatt.java | 60 +++--- .../BluetoothGattCharacteristic.java | 9 +- .../bluetooth/BluetoothGattDescriptor.java | 9 +- .../BluetoothGattIncludedService.java | 6 +- .../bluetooth/BluetoothGattServer.java | 24 +-- .../BluetoothGattServerCallback.java | 2 +- .../bluetooth/BluetoothGattService.java | 8 +- .../android/bluetooth/BluetoothHeadset.java | 51 +++-- .../bluetooth/BluetoothHeadsetClient.java | 184 ++++++++---------- .../bluetooth/BluetoothHeadsetClientCall.java | 6 + .../android/bluetooth/BluetoothHealth.java | 22 +-- .../BluetoothHealthAppConfiguration.java | 13 +- .../bluetooth/BluetoothHealthCallback.java | 6 +- .../BluetoothHidDeviceAppQosSettings.java | 21 +- .../BluetoothHidDeviceAppSdpSettings.java | 10 +- .../bluetooth/BluetoothHidDeviceCallback.java | 5 +- .../bluetooth/BluetoothInputDevice.java | 9 +- .../android/bluetooth/BluetoothInputHost.java | 5 +- .../bluetooth/BluetoothInputStream.java | 2 +- .../java/android/bluetooth/BluetoothMap.java | 18 +- .../android/bluetooth/BluetoothMapClient.java | 18 +- .../bluetooth/BluetoothMasInstance.java | 6 +- .../bluetooth/BluetoothOutputStream.java | 2 +- .../java/android/bluetooth/BluetoothPan.java | 11 +- .../java/android/bluetooth/BluetoothPbap.java | 2 +- .../bluetooth/BluetoothPbapClient.java | 9 +- .../android/bluetooth/BluetoothProfile.java | 6 +- .../java/android/bluetooth/BluetoothSap.java | 18 +- .../bluetooth/BluetoothServerSocket.java | 4 +- .../android/bluetooth/BluetoothSocket.java | 33 ++-- .../java/android/bluetooth/BluetoothUuid.java | 10 +- framework/java/android/bluetooth/OobData.java | 47 ++--- .../java/android/bluetooth/SdpMasRecord.java | 59 +++--- .../java/android/bluetooth/SdpMnsRecord.java | 30 +-- .../android/bluetooth/SdpOppOpsRecord.java | 23 +-- .../java/android/bluetooth/SdpPseRecord.java | 37 ++-- .../java/android/bluetooth/SdpRecord.java | 16 +- .../java/android/bluetooth/SdpSapsRecord.java | 22 +-- .../java/android/bluetooth/UidTraffic.java | 7 +- .../android/bluetooth/le/AdvertiseData.java | 12 +- .../bluetooth/le/AdvertiseSettings.java | 2 +- .../android/bluetooth/le/AdvertisingSet.java | 30 +-- .../bluetooth/le/AdvertisingSetCallback.java | 6 +- .../le/AdvertisingSetParameters.java | 175 ++++++++--------- .../bluetooth/le/BluetoothLeAdvertiser.java | 32 ++- .../bluetooth/le/BluetoothLeScanner.java | 15 +- .../bluetooth/le/BluetoothLeUtils.java | 7 +- .../le/PeriodicAdvertisingManager.java | 16 +- .../le/PeriodicAdvertisingParameters.java | 34 ++-- .../le/PeriodicAdvertisingReport.java | 76 ++++---- .../bluetooth/le/ResultStorageDescriptor.java | 4 +- .../java/android/bluetooth/le/ScanFilter.java | 24 +-- .../java/android/bluetooth/le/ScanRecord.java | 4 +- .../java/android/bluetooth/le/ScanResult.java | 31 +-- .../android/bluetooth/le/ScanSettings.java | 12 +- 66 files changed, 748 insertions(+), 774 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7d6879d5a61..7841b83cf92 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -187,7 +187,7 @@ public final class BluetoothA2dp implements BluetoothProfile { private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -274,6 +274,7 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + @Override public void finalize() { // The empty finalize needs to be kept or the // cts signature tests would fail. @@ -304,8 +305,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (DBG) log("connect(" + device + ")"); try { mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { return mService.connect(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -347,8 +347,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (DBG) log("disconnect(" + device + ")"); try { mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { return mService.disconnect(device); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); @@ -364,6 +363,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { @@ -384,6 +384,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { @@ -404,6 +405,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { @@ -443,8 +445,8 @@ public final class BluetoothA2dp implements BluetoothProfile { mServiceLock.readLock().lock(); if (mService != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { return false; } return mService.setPriority(device, priority); @@ -758,9 +760,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { try { - if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && - value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED && - value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { + if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN + && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED + && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); return; } diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 78dab1b2a4c..611531c4f7c 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -120,15 +120,15 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * This extra represents the current audio configuration of the A2DP source device. * {@see BluetoothAudioConfig} */ - public static final String EXTRA_AUDIO_CONFIG - = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; + public static final String EXTRA_AUDIO_CONFIG = + "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; private Context mContext; private ServiceListener mServiceListener; private IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -212,6 +212,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } } + @Override public void finalize() { close(); } @@ -239,8 +240,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { @@ -279,8 +279,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -295,6 +294,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { @@ -312,6 +312,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { @@ -329,6 +330,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() @@ -389,8 +391,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { return false; } try { diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index da2aba765b6..43b79db6f35 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -87,6 +87,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { } }; + + @Override @SuppressWarnings("unchecked") public void writeToParcel(Parcel out, int flags) { out.writeLong(mTimestamp); @@ -98,6 +100,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { out.writeTypedArray(mUidTraffic, flags); } + @Override public int describeContents() { return 0; } @@ -158,8 +161,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { * @return if the record is valid */ public boolean isValid() { - return ((mControllerTxTimeMs >= 0) && - (mControllerRxTimeMs >= 0) && - (mControllerIdleTimeMs >= 0)); + return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0) + && (mControllerIdleTimeMs >= 0)); } } diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1b9ce88de08..70591d4d058 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2191,7 +2191,7 @@ public final class BluetoothAdapter { } } - final private IBluetoothManagerCallback mManagerCallback = + private final IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); @@ -2255,7 +2255,7 @@ public final class BluetoothAdapter { * @hide */ public boolean enableNoAutoConnect() { - if (isEnabled() == true) { + if (isEnabled()) { if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); return true; } @@ -2376,7 +2376,7 @@ public final class BluetoothAdapter { return mManagerService; } - final private ArrayList mProxyServiceStateCallbacks = + private final ArrayList mProxyServiceStateCallbacks = new ArrayList(); /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java index 238bf842b73..a4410563c99 100644 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.java +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java @@ -42,9 +42,8 @@ public final class BluetoothAudioConfig implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothAudioConfig) { BluetoothAudioConfig bac = (BluetoothAudioConfig) o; - return (bac.mSampleRate == mSampleRate && - bac.mChannelConfig == mChannelConfig && - bac.mAudioFormat == mAudioFormat); + return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig + && bac.mAudioFormat == mAudioFormat); } return false; } @@ -60,6 +59,7 @@ public final class BluetoothAudioConfig implements Parcelable { + ",mAudioFormat:" + mAudioFormat + "}"; } + @Override public int describeContents() { return 0; } @@ -78,6 +78,7 @@ public final class BluetoothAudioConfig implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mSampleRate); out.writeInt(mChannelConfig); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 1a0ae149f1c..7528aa97210 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -84,7 +84,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -168,6 +168,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { } } + @Override public void finalize() { close(); } @@ -175,6 +176,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { @@ -192,6 +194,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { @@ -209,6 +212,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() @@ -261,7 +265,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return false; } - /* + /** * Send Group Navigation Command to Remote. * possible keycode values: next_grp, previous_grp defined above */ diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java index 036d36d0e5d..3d3d80e2b4d 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java @@ -82,14 +82,14 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { /** * All track repeat/shuffle. * - * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}. */ public static final int STATE_ALL_TRACK = 0x03; /** * Group repeat/shuffle. * - * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}. + * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}. */ public static final int STATE_GROUP = 0x04; @@ -103,10 +103,12 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { */ private Map mSettingsValue = new HashMap(); + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mSettings); out.writeInt(mSettingsValue.size()); @@ -116,8 +118,8 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { } } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) { return new BluetoothAvrcpPlayerSettings(in); } diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 2f6a79352b3..57e4abb1298 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -81,6 +81,7 @@ public final class BluetoothClass implements Parcelable { return Integer.toHexString(mClass); } + @Override public int describeContents() { return 0; } @@ -96,6 +97,7 @@ public final class BluetoothClass implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mClass); } diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 1e463f7baf6..e3a6e064725 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -92,15 +92,15 @@ public final class BluetoothCodecConfig implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothCodecConfig) { BluetoothCodecConfig other = (BluetoothCodecConfig) o; - return (other.mCodecType == mCodecType && - other.mCodecPriority == mCodecPriority && - other.mSampleRate == mSampleRate && - other.mBitsPerSample == mBitsPerSample && - other.mChannelMode == mChannelMode && - other.mCodecSpecific1 == mCodecSpecific1 && - other.mCodecSpecific2 == mCodecSpecific2 && - other.mCodecSpecific3 == mCodecSpecific3 && - other.mCodecSpecific4 == mCodecSpecific4); + return (other.mCodecType == mCodecType + && other.mCodecPriority == mCodecPriority + && other.mSampleRate == mSampleRate + && other.mBitsPerSample == mBitsPerSample + && other.mChannelMode == mChannelMode + && other.mCodecSpecific1 == mCodecSpecific1 + && other.mCodecSpecific2 == mCodecSpecific2 + && other.mCodecSpecific3 == mCodecSpecific3 + && other.mCodecSpecific4 == mCodecSpecific4); } return false; } @@ -118,9 +118,9 @@ public final class BluetoothCodecConfig implements Parcelable { * @return true if the object contains valid codec configuration, otherwise false. */ public boolean isValid() { - return (mSampleRate != SAMPLE_RATE_NONE) && - (mBitsPerSample != BITS_PER_SAMPLE_NONE) && - (mChannelMode != CHANNEL_MODE_NONE); + return (mSampleRate != SAMPLE_RATE_NONE) + && (mBitsPerSample != BITS_PER_SAMPLE_NONE) + && (mChannelMode != CHANNEL_MODE_NONE); } /** @@ -188,21 +188,22 @@ public final class BluetoothCodecConfig implements Parcelable { channelModeStr = appendCapabilityToString(channelModeStr, "STEREO"); } - return "{codecName:" + getCodecName() + - ",mCodecType:" + mCodecType + - ",mCodecPriority:" + mCodecPriority + - ",mSampleRate:" + String.format("0x%x", mSampleRate) + - "(" + sampleRateStr + ")" + - ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + - "(" + bitsPerSampleStr + ")" + - ",mChannelMode:" + String.format("0x%x", mChannelMode) + - "(" + channelModeStr + ")" + - ",mCodecSpecific1:" + mCodecSpecific1 + - ",mCodecSpecific2:" + mCodecSpecific2 + - ",mCodecSpecific3:" + mCodecSpecific3 + - ",mCodecSpecific4:" + mCodecSpecific4 + "}"; + return "{codecName:" + getCodecName() + + ",mCodecType:" + mCodecType + + ",mCodecPriority:" + mCodecPriority + + ",mSampleRate:" + String.format("0x%x", mSampleRate) + + "(" + sampleRateStr + ")" + + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + + "(" + bitsPerSampleStr + ")" + + ",mChannelMode:" + String.format("0x%x", mChannelMode) + + "(" + channelModeStr + ")" + + ",mCodecSpecific1:" + mCodecSpecific1 + + ",mCodecSpecific2:" + mCodecSpecific2 + + ",mCodecSpecific3:" + mCodecSpecific3 + + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; } + @Override public int describeContents() { return 0; } @@ -231,6 +232,7 @@ public final class BluetoothCodecConfig implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mCodecType); out.writeInt(mCodecPriority); @@ -396,8 +398,8 @@ public final class BluetoothCodecConfig implements Parcelable { * @return true if the audio feeding parameters are same, otherwise false */ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { - return (other != null && other.mSampleRate == mSampleRate && - other.mBitsPerSample == mBitsPerSample && - other.mChannelMode == mChannelMode); + return (other != null && other.mSampleRate == mSampleRate + && other.mBitsPerSample == mBitsPerSample + && other.mChannelMode == mChannelMode); } } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 61e29416973..7ae4cb70623 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -56,11 +56,10 @@ public final class BluetoothCodecStatus implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothCodecStatus) { BluetoothCodecStatus other = (BluetoothCodecStatus) o; - return (Objects.equals(other.mCodecConfig, mCodecConfig) && - Objects.equals(other.mCodecsLocalCapabilities, - mCodecsLocalCapabilities) && - Objects.equals(other.mCodecsSelectableCapabilities, - mCodecsSelectableCapabilities)); + return (Objects.equals(other.mCodecConfig, mCodecConfig) + && Objects.equals(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) + && Objects.equals(other.mCodecsSelectableCapabilities, + mCodecsSelectableCapabilities)); } return false; } @@ -73,12 +72,13 @@ public final class BluetoothCodecStatus implements Parcelable { @Override public String toString() { - return "{mCodecConfig:" + mCodecConfig + - ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + - ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + - "}"; + return "{mCodecConfig:" + mCodecConfig + + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + "}"; } + @Override public int describeContents() { return 0; } @@ -103,6 +103,7 @@ public final class BluetoothCodecStatus implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); out.writeTypedArray(mCodecsLocalCapabilities, 0); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 537d913b7dc..3ab2c4a8f44 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -721,13 +721,13 @@ public final class BluetoothDevice implements Parcelable { synchronized (BluetoothDevice.class) { if (sService == null) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - sService = adapter.getBluetoothService(mStateChangeCallback); + sService = adapter.getBluetoothService(sStateChangeCallback); } } return sService; } - static IBluetoothManagerCallback mStateChangeCallback = new IBluetoothManagerCallback.Stub() { + static IBluetoothManagerCallback sStateChangeCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) throws RemoteException { @@ -796,6 +796,7 @@ public final class BluetoothDevice implements Parcelable { return mAddress; } + @Override public int describeContents() { return 0; } @@ -811,6 +812,7 @@ public final class BluetoothDevice implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); } @@ -969,9 +971,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - Log.i(TAG, "createBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, TRANSPORT_AUTO); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1004,9 +1006,9 @@ public final class BluetoothDevice implements Parcelable { throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); } try { - Log.i(TAG, "createBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, transport); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1085,9 +1087,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - Log.i(TAG, "cancelBondProcess() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); + Log.i(TAG, "cancelBondProcess() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.cancelBondProcess(this); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1111,9 +1113,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - Log.i(TAG, "removeBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); + Log.i(TAG, "removeBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.removeBond(this); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1143,8 +1145,8 @@ public final class BluetoothDevice implements Parcelable { } catch (NullPointerException npe) { // Handle case where bluetooth service proxy // is already null. - Log.e(TAG, "NullPointerException for getBondState() of device (" + - getAddress() + ")", npe); + Log.e(TAG, "NullPointerException for getBondState() of device (" + + getAddress() + ")", npe); } return BOND_NONE; } @@ -1225,7 +1227,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public ParcelUuid[] getUuids() { - if (sService == null || isBluetoothEnabled() == false) { + if (sService == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); return null; } @@ -1253,7 +1255,7 @@ public final class BluetoothDevice implements Parcelable { @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { IBluetooth service = sService; - if (service == null || isBluetoothEnabled() == false) { + if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } @@ -1384,7 +1386,7 @@ public final class BluetoothDevice implements Parcelable { boolean isBluetoothEnabled() { boolean ret = false; BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.isEnabled() == true) { + if (adapter != null && adapter.isEnabled()) { ret = true; } return ret; @@ -1536,7 +1538,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createRfcommSocket(int channel) throws IOException { - if (isBluetoothEnabled() == false) { + if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); throw new IOException(); } @@ -1627,7 +1629,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { - if (isBluetoothEnabled() == false) { + if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); throw new IOException(); } @@ -1665,7 +1667,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { - if (isBluetoothEnabled() == false) { + if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); throw new IOException(); } @@ -1688,8 +1690,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { - - if (isBluetoothEnabled() == false) { + if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); throw new IOException(); } @@ -1708,8 +1709,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public BluetoothSocket createScoSocket() throws IOException { - - if (isBluetoothEnabled() == false) { + if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); throw new IOException(); } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 8a3650c8698..759d772920b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -352,8 +352,8 @@ public final class BluetoothGatt implements BluetoothProfile { || status == GATT_INSUFFICIENT_ENCRYPTION) && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) + ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.readCharacteristic(mClientIf, address, handle, authReq); mAuthRetryState++; return; @@ -412,8 +412,8 @@ public final class BluetoothGatt implements BluetoothProfile { || status == GATT_INSUFFICIENT_ENCRYPTION) && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) + ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), authReq, characteristic.getValue()); @@ -495,8 +495,8 @@ public final class BluetoothGatt implements BluetoothProfile { || status == GATT_INSUFFICIENT_ENCRYPTION) && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) + ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.readDescriptor(mClientIf, address, handle, authReq); mAuthRetryState++; return; @@ -543,8 +543,8 @@ public final class BluetoothGatt implements BluetoothProfile { || status == GATT_INSUFFICIENT_ENCRYPTION) && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? - AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; + final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) + ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, authReq, descriptor.getValue()); mAuthRetryState++; @@ -601,8 +601,8 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public void onReadRemoteRssi(String address, int rssi, int status) { if (VDBG) { - Log.d(TAG, "onReadRemoteRssi() - Device=" + address + - " rssi=" + rssi + " status=" + status); + Log.d(TAG, "onReadRemoteRssi() - Device=" + address + + " rssi=" + rssi + " status=" + status); } if (!address.equals(mDevice.getAddress())) { return; @@ -624,8 +624,8 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public void onConfigureMTU(String address, int mtu, int status) { if (DBG) { - Log.d(TAG, "onConfigureMTU() - Device=" + address + - " mtu=" + mtu + " status=" + status); + Log.d(TAG, "onConfigureMTU() - Device=" + address + + " mtu=" + mtu + " status=" + status); } if (!address.equals(mDevice.getAddress())) { return; @@ -649,9 +649,9 @@ public final class BluetoothGatt implements BluetoothProfile { public void onConnectionUpdated(String address, int interval, int latency, int timeout, int status) { if (DBG) { - Log.d(TAG, "onConnectionUpdated() - Device=" + address + - " interval=" + interval + " latency=" + latency + - " timeout=" + timeout + " status=" + status); + Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); } if (!address.equals(mDevice.getAddress())) { return; @@ -704,10 +704,10 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, int instanceId, int type) { for (BluetoothGattService svc : mServices) { - if (svc.getDevice().equals(device) && - svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { + if (svc.getDevice().equals(device) + && svc.getType() == type + && svc.getInstanceId() == instanceId + && svc.getUuid().equals(uuid)) { return svc; } } @@ -1043,8 +1043,7 @@ public final class BluetoothGatt implements BluetoothProfile { */ public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(mDevice) && - service.getUuid().equals(uuid)) { + if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { return service; } } @@ -1065,8 +1064,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the read operation was initiated successfully */ public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { - if ((characteristic.getProperties() & - BluetoothGattCharacteristic.PROPERTY_READ) == 0) { + if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) { return false; } @@ -1145,8 +1143,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 - && (characteristic.getProperties() & - BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + && (characteristic.getProperties() + & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { return false; } @@ -1480,8 +1478,8 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws IllegalArgumentException If the parameters are outside of their specified range. */ public boolean requestConnectionPriority(int connectionPriority) { - if (connectionPriority < CONNECTION_PRIORITY_BALANCED || - connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { + if (connectionPriority < CONNECTION_PRIORITY_BALANCED + || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { throw new IllegalArgumentException("connectionPriority not within valid range"); } @@ -1517,8 +1515,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override public List getConnectedDevices() { - throw new UnsupportedOperationException - ("Use BluetoothManager#getConnectedDevices instead."); + throw new UnsupportedOperationException( + "Use BluetoothManager#getConnectedDevices instead."); } /** @@ -1530,7 +1528,7 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - throw new UnsupportedOperationException - ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); + throw new UnsupportedOperationException( + "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 48168046524..2c124034b78 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -283,13 +283,12 @@ public class BluetoothGattCharacteristic implements Parcelable { } } - /** - * @hide - */ + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { out.writeParcelable(new ParcelUuid(mUuid), 0); out.writeInt(mInstance); @@ -300,8 +299,8 @@ public class BluetoothGattCharacteristic implements Parcelable { out.writeTypedList(mDescriptors); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public BluetoothGattCharacteristic createFromParcel(Parcel in) { return new BluetoothGattCharacteristic(in); } diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 8f6eb68c3e7..217a5abf1fe 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -162,21 +162,20 @@ public class BluetoothGattDescriptor implements Parcelable { mPermissions = permissions; } - /** - * @hide - */ + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { out.writeParcelable(new ParcelUuid(mUuid), 0); out.writeInt(mInstance); out.writeInt(mPermissions); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public BluetoothGattDescriptor createFromParcel(Parcel in) { return new BluetoothGattDescriptor(in); } diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.java b/framework/java/android/bluetooth/BluetoothGattIncludedService.java index 2a42a789b28..bccf20ef96b 100644 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.java +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.java @@ -52,18 +52,20 @@ public class BluetoothGattIncludedService implements Parcelable { mServiceType = serviceType; } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { out.writeParcelable(new ParcelUuid(mUuid), 0); out.writeInt(mInstanceId); out.writeInt(mServiceType); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public BluetoothGattIncludedService createFromParcel(Parcel in) { return new BluetoothGattIncludedService(in); } diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 7b86a172834..4ed250043ae 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -356,9 +356,9 @@ public final class BluetoothGattServer implements BluetoothProfile { public void onConnectionUpdated(String address, int interval, int latency, int timeout, int status) { if (DBG) { - Log.d(TAG, "onConnectionUpdated() - Device=" + address + - " interval=" + interval + " latency=" + latency + - " timeout=" + timeout + " status=" + status); + Log.d(TAG, "onConnectionUpdated() - Device=" + address + + " interval=" + interval + " latency=" + latency + + " timeout=" + timeout + " status=" + status); } BluetoothDevice device = mAdapter.getRemoteDevice(address); if (device == null) return; @@ -505,9 +505,9 @@ public final class BluetoothGattServer implements BluetoothProfile { */ /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { for (BluetoothGattService svc : mServices) { - if (svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { + if (svc.getType() == type + && svc.getInstanceId() == instanceId + && svc.getUuid().equals(uuid)) { return svc; } } @@ -543,8 +543,8 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return false; try { - mService.serverConnect(mServerIf, device.getAddress(), - autoConnect ? false : true, mTransport); // autoConnect is inverse of "isDirect" + // autoConnect is inverse of "isDirect" + mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -822,8 +822,8 @@ public final class BluetoothGattServer implements BluetoothProfile { */ @Override public List getConnectedDevices() { - throw new UnsupportedOperationException - ("Use BluetoothManager#getConnectedDevices instead."); + throw new UnsupportedOperationException( + "Use BluetoothManager#getConnectedDevices instead."); } /** @@ -835,7 +835,7 @@ public final class BluetoothGattServer implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - throw new UnsupportedOperationException - ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); + throw new UnsupportedOperationException( + "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index e72577d1fa0..22eba351b36 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -184,7 +184,7 @@ public abstract class BluetoothGattServerCallback { /** * Callback indicating the connection parameters were updated. * - * @param device The remote device involved + * @param gatt The remote device involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). * @param latency Slave latency for the connection in number of connection events. Valid range diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index db820d874ad..ce1dc1ce631 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -163,8 +163,8 @@ public class BluetoothGattService implements Parcelable { out.writeTypedList(includedServices); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public BluetoothGattService createFromParcel(Parcel in) { return new BluetoothGattService(in); } @@ -217,7 +217,7 @@ public class BluetoothGattService implements Parcelable { * @hide */ /*package*/ void setDevice(BluetoothDevice device) { - this.mDevice = device; + mDevice = device; } /** @@ -383,6 +383,6 @@ public class BluetoothGattService implements Parcelable { * @hide */ public void setAdvertisePreferred(boolean advertisePreferred) { - this.mAdvertisePreferred = advertisePreferred; + mAdvertisePreferred = advertisePreferred; } } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 132f383997b..be1ce63cadc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -309,7 +309,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private IBluetoothHeadset mService; private BluetoothAdapter mAdapter; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -418,8 +418,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { @@ -458,8 +457,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -474,6 +472,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { @@ -491,6 +490,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { @@ -508,10 +508,10 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -540,10 +540,9 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("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 { @@ -572,8 +571,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -607,8 +605,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.startVoiceRecognition(device); } catch (RemoteException e) { @@ -630,8 +627,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.stopVoiceRecognition(device); } catch (RemoteException e) { @@ -652,8 +648,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.isAudioConnected(device); } catch (RemoteException e) { @@ -679,8 +674,7 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getBatteryUsageHint(BluetoothDevice device) { if (VDBG) log("getBatteryUsageHint()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getBatteryUsageHint(device); } catch (RemoteException e) { @@ -1012,8 +1006,7 @@ public final class BluetoothHeadset implements BluetoothProfile { if (command == null) { throw new IllegalArgumentException("command is null"); } - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.sendVendorSpecificResultCode(device, command, arg); } catch (RemoteException e) { @@ -1083,16 +1076,16 @@ public final class BluetoothHeadset implements BluetoothProfile { * Send Headset the BIND response from AG to report change in the status of the * HF indicators to the headset * - * @param ind_id Assigned Number of the indicator (defined by SIG) - * @param ind_status possible values- false-Indicator is disabled, no value changes shall be + * @param indId Assigned Number of the indicator (defined by SIG) + * @param indStatus possible values- false-Indicator is disabled, no value changes shall be * sent for this indicator true-Indicator is enabled, value changes may be sent for this * indicator * @hide */ - public void bindResponse(int ind_id, boolean ind_status) { + public void bindResponse(int indId, boolean indStatus) { if (mService != null && isEnabled()) { try { - mService.bindResponse(ind_id, ind_status); + mService.bindResponse(indId, indStatus); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1102,8 +1095,8 @@ public final class BluetoothHeadset implements BluetoothProfile { } } - private final IBluetoothProfileServiceConnection mConnection - = new IBluetoothProfileServiceConnection.Stub() { + private final IBluetoothProfileServiceConnection mConnection = + new IBluetoothProfileServiceConnection.Stub() { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index c775cd739e5..7ed2d2e98f7 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -250,53 +250,53 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * AG feature: three way calling. */ - public final static String EXTRA_AG_FEATURE_3WAY_CALLING = + public static final String EXTRA_AG_FEATURE_3WAY_CALLING = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; /** * AG feature: voice recognition. */ - public final static String EXTRA_AG_FEATURE_VOICE_RECOGNITION = + public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; /** * AG feature: fetching phone number for voice tagging procedure. */ - public final static String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = + public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; /** * AG feature: ability to reject incoming call. */ - public final static String EXTRA_AG_FEATURE_REJECT_CALL = + public static final String EXTRA_AG_FEATURE_REJECT_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; /** * AG feature: enhanced call handling (terminate specific call, private consultation). */ - public final static String EXTRA_AG_FEATURE_ECC = + public static final String EXTRA_AG_FEATURE_ECC = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; /** * AG feature: response and hold. */ - public final static String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = + public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; /** * AG call handling feature: accept held or waiting call in three way calling scenarios. */ - public final static String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = + public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; /** * AG call handling feature: release held or waiting call in three way calling scenarios. */ - public final static String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = + public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; /** * AG call handling feature: release active call and accept held or waiting call in three way * calling scenarios. */ - public final static String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = + public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; /** * AG call handling feature: merge two calls, held and active - multi party conference mode. */ - public final static String EXTRA_AG_FEATURE_MERGE = + public static final String EXTRA_AG_FEATURE_MERGE = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; /** * AG call handling feature: merge calls and disconnect from multi party @@ -304,61 +304,61 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Note that this feature needs to be supported by mobile network operator * as it requires connection and billing transfer. */ - public final static String EXTRA_AG_FEATURE_MERGE_AND_DETACH = + public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH = "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; /* Action result codes */ - public final static int ACTION_RESULT_OK = 0; - public final static int ACTION_RESULT_ERROR = 1; - public final static int ACTION_RESULT_ERROR_NO_CARRIER = 2; - public final static int ACTION_RESULT_ERROR_BUSY = 3; - public final static int ACTION_RESULT_ERROR_NO_ANSWER = 4; - public final static int ACTION_RESULT_ERROR_DELAYED = 5; - public final static int ACTION_RESULT_ERROR_BLACKLISTED = 6; - public final static int ACTION_RESULT_ERROR_CME = 7; + public static final int ACTION_RESULT_OK = 0; + public static final int ACTION_RESULT_ERROR = 1; + public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2; + public static final int ACTION_RESULT_ERROR_BUSY = 3; + public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4; + public static final int ACTION_RESULT_ERROR_DELAYED = 5; + public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6; + public static final int ACTION_RESULT_ERROR_CME = 7; /* Detailed CME error codes */ - public final static int CME_PHONE_FAILURE = 0; - public final static int CME_NO_CONNECTION_TO_PHONE = 1; - public final static int CME_OPERATION_NOT_ALLOWED = 3; - public final static int CME_OPERATION_NOT_SUPPORTED = 4; - public final static int CME_PHSIM_PIN_REQUIRED = 5; - public final static int CME_PHFSIM_PIN_REQUIRED = 6; - public final static int CME_PHFSIM_PUK_REQUIRED = 7; - public final static int CME_SIM_NOT_INSERTED = 10; - public final static int CME_SIM_PIN_REQUIRED = 11; - public final static int CME_SIM_PUK_REQUIRED = 12; - public final static int CME_SIM_FAILURE = 13; - public final static int CME_SIM_BUSY = 14; - public final static int CME_SIM_WRONG = 15; - public final static int CME_INCORRECT_PASSWORD = 16; - public final static int CME_SIM_PIN2_REQUIRED = 17; - public final static int CME_SIM_PUK2_REQUIRED = 18; - public final static int CME_MEMORY_FULL = 20; - public final static int CME_INVALID_INDEX = 21; - public final static int CME_NOT_FOUND = 22; - public final static int CME_MEMORY_FAILURE = 23; - public final static int CME_TEXT_STRING_TOO_LONG = 24; - public final static int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; - public final static int CME_DIAL_STRING_TOO_LONG = 26; - public final static int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; - public final static int CME_NO_NETWORK_SERVICE = 30; - public final static int CME_NETWORK_TIMEOUT = 31; - public final static int CME_EMERGENCY_SERVICE_ONLY = 32; - public final static int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; - public final static int CME_NOT_SUPPORTED_FOR_VOIP = 34; - public final static int CME_SIP_RESPONSE_CODE = 35; - public final static int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; - public final static int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; - public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; - public final static int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; - public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; - public final static int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; - public final static int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; - public final static int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; - public final static int CME_HIDDEN_KEY_REQUIRED = 48; - public final static int CME_EAP_NOT_SUPPORTED = 49; - public final static int CME_INCORRECT_PARAMETERS = 50; + public static final int CME_PHONE_FAILURE = 0; + public static final int CME_NO_CONNECTION_TO_PHONE = 1; + public static final int CME_OPERATION_NOT_ALLOWED = 3; + public static final int CME_OPERATION_NOT_SUPPORTED = 4; + public static final int CME_PHSIM_PIN_REQUIRED = 5; + public static final int CME_PHFSIM_PIN_REQUIRED = 6; + public static final int CME_PHFSIM_PUK_REQUIRED = 7; + public static final int CME_SIM_NOT_INSERTED = 10; + public static final int CME_SIM_PIN_REQUIRED = 11; + public static final int CME_SIM_PUK_REQUIRED = 12; + public static final int CME_SIM_FAILURE = 13; + public static final int CME_SIM_BUSY = 14; + public static final int CME_SIM_WRONG = 15; + public static final int CME_INCORRECT_PASSWORD = 16; + public static final int CME_SIM_PIN2_REQUIRED = 17; + public static final int CME_SIM_PUK2_REQUIRED = 18; + public static final int CME_MEMORY_FULL = 20; + public static final int CME_INVALID_INDEX = 21; + public static final int CME_NOT_FOUND = 22; + public static final int CME_MEMORY_FAILURE = 23; + public static final int CME_TEXT_STRING_TOO_LONG = 24; + public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; + public static final int CME_DIAL_STRING_TOO_LONG = 26; + public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; + public static final int CME_NO_NETWORK_SERVICE = 30; + public static final int CME_NETWORK_TIMEOUT = 31; + public static final int CME_EMERGENCY_SERVICE_ONLY = 32; + public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; + public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34; + public static final int CME_SIP_RESPONSE_CODE = 35; + public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; + public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; + public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; + public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; + public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; + public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; + public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; + public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; + public static final int CME_HIDDEN_KEY_REQUIRED = 48; + public static final int CME_EAP_NOT_SUPPORTED = 49; + public static final int CME_INCORRECT_PARAMETERS = 50; /* Action policy for other calls when accepting call */ public static final int CALL_ACCEPT_NONE = 0; @@ -370,7 +370,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { private IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @Override public void onBluetoothStateChange(boolean up) { @@ -478,8 +478,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.connect(device); } catch (RemoteException e) { @@ -500,8 +499,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -564,8 +562,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -584,10 +581,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("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 { @@ -606,8 +602,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { @@ -632,8 +627,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.startVoiceRecognition(device); } catch (RemoteException e) { @@ -657,8 +651,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.stopVoiceRecognition(device); } catch (RemoteException e) { @@ -677,8 +670,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getCurrentCalls(device); } catch (RemoteException e) { @@ -697,8 +689,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getCurrentAgEvents(device); } catch (RemoteException e) { @@ -720,8 +711,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.acceptCall(device, flag); } catch (RemoteException e) { @@ -741,8 +731,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.holdCall(device); } catch (RemoteException e) { @@ -766,8 +755,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.rejectCall(device); } catch (RemoteException e) { @@ -784,8 +772,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Works only when Extended Call Control is supported by Audio Gateway. * * @param device remote device - * @param call Handle of call obtained in {@link dial()} or obtained via {@link - * ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active + * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via + * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active * calls. * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. @@ -796,8 +784,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.terminateCall(device, call); } catch (RemoteException e) { @@ -824,8 +811,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.enterPrivateMode(device, index); } catch (RemoteException e) { @@ -851,8 +837,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.explicitCallTransfer(device); } catch (RemoteException e) { @@ -874,8 +859,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.dial(device, number); } catch (RemoteException e) { @@ -898,8 +882,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.sendDTMF(device, code); } catch (RemoteException e) { @@ -924,8 +907,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getLastVoiceTagNumber(device); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 949cda0250f..dc00d63043f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -200,10 +200,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mOutgoing; } + @Override public String toString() { return toString(false); } + /** + * Generate a log string for this call + * @param loggable whether device address should be logged + * @return log string + */ public String toString(boolean loggable) { StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); builder.append(loggable ? mDevice : mDevice.hashCode()); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index fa759066ea1..dc5f38135aa 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -97,7 +97,7 @@ public final class BluetoothHealth implements BluetoothProfile { /** @hide */ public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -228,8 +228,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { return mService.connectChannelToSource(device, config); } catch (RemoteException e) { @@ -257,8 +256,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { return mService.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { @@ -286,8 +284,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { return mService.disconnectChannel(device, config, channelId); } catch (RemoteException e) { @@ -315,8 +312,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { return mService.getMainChannelFd(device, config); } catch (RemoteException e) { @@ -553,10 +549,10 @@ public final class BluetoothHealth implements BluetoothProfile { private boolean checkAppParam(String name, int role, int channelType, BluetoothHealthCallback callback) { - if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) || - (channelType != CHANNEL_TYPE_RELIABLE && - channelType != CHANNEL_TYPE_STREAMING && - channelType != CHANNEL_TYPE_ANY) || callback == null) { + if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) + || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING + && channelType != CHANNEL_TYPE_ANY) + || callback == null) { return false; } if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false; diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index d406ac2b1ec..7c9db6f7213 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -69,10 +69,8 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { if (mName == null) return false; - return mName.equals(config.getName()) && - mDataType == config.getDataType() && - mRole == config.getRole() && - mChannelType == config.getChannelType(); + return mName.equals(config.getName()) && mDataType == config.getDataType() + && mRole == config.getRole() && mChannelType == config.getChannelType(); } return false; } @@ -89,11 +87,11 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { @Override public String toString() { - return "BluetoothHealthAppConfiguration [mName = " + mName + - ",mDataType = " + mDataType + ", mRole = " + mRole + ",mChannelType = " + - mChannelType + "]"; + return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType + + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]"; } + @Override public int describeContents() { return 0; } @@ -154,6 +152,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index 198d06a8caa..40234856b8a 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -63,8 +63,8 @@ public abstract class BluetoothHealthCallback { public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, int channelId) { - Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + - "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + - "ChannelId:" + channelId); + Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + + "ChannelId:" + channelId); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index d8880a231fa..1f80ed78d75 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -22,18 +22,18 @@ import android.os.Parcelable; /** @hide */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { - final public int serviceType; - final public int tokenRate; - final public int tokenBucketSize; - final public int peakBandwidth; - final public int latency; - final public int delayVariation; + public final int serviceType; + public final int tokenRate; + public final int tokenBucketSize; + public final int peakBandwidth; + public final int latency; + public final int delayVariation; - final static public int SERVICE_NO_TRAFFIC = 0x00; - final static public int SERVICE_BEST_EFFORT = 0x01; - final static public int SERVICE_GUARANTEED = 0x02; + public static final int SERVICE_NO_TRAFFIC = 0x00; + public static final int SERVICE_BEST_EFFORT = 0x01; + public static final int SERVICE_GUARANTEED = 0x02; - final static public int MAX = (int) 0xffffffff; + public static final int MAX = (int) 0xffffffff; public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, int peakBandwidth, @@ -88,6 +88,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { out.writeInt(delayVariation); } + /** @return an int array representation of this instance */ public int[] toArray() { return new int[]{ serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index e17e785718d..d21d50611ab 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -22,11 +22,11 @@ import android.os.Parcelable; /** @hide */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { - final public String name; - final public String description; - final public String provider; - final public byte subclass; - final public byte[] descriptors; + public final String name; + public final String description; + public final String provider; + public final byte subclass; + public final byte[] descriptors; public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, byte subclass, byte[] descriptors) { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index d50505c092c..3d407a6ca73 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -35,11 +35,10 @@ public abstract class BluetoothHidDeviceCallback { * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has * Virtual Cable established with device. Only valid when application is registered, can be - * null . + * null. * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token * required to unregister application using - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} - * . + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}. * @param registered true if application is registered, false * otherwise. */ diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index a9b2e56bf3e..a9a9010c7ed 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -224,7 +224,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothInputDevice mService; - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); @@ -385,6 +385,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { @@ -402,6 +403,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { @@ -419,6 +421,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { @@ -451,8 +454,8 @@ public final class BluetoothInputDevice implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { return false; } try { diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 0cdcd577671..15303dc7944 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -163,7 +163,7 @@ public final class BluetoothInputHost implements BluetoothProfile { } } - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -287,6 +287,7 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); @@ -306,6 +307,7 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); @@ -325,6 +327,7 @@ public final class BluetoothInputHost implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index 062e4de0a78..8eb79b248d6 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -55,7 +55,7 @@ import java.io.InputStream; * @since Android 1.5 */ public int read() throws IOException { - byte b[] = new byte[1]; + byte[] b = new byte[1]; int ret = mSocket.read(b, 0, 1); if (ret == 1) { return (int) b[0] & 0xff; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 30c0d3c6d04..26a9106f49f 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -56,7 +56,7 @@ public final class BluetoothMap 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); @@ -232,8 +232,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -311,8 +310,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -337,10 +335,9 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("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 { @@ -366,8 +363,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 09dd5ad62b4..3e0c36548c4 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) { diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java index 8447282d3e7..7a31328eaf0 100644 --- a/framework/java/android/bluetooth/BluetoothMasInstance.java +++ b/framework/java/android/bluetooth/BluetoothMasInstance.java @@ -48,10 +48,11 @@ public final class BluetoothMasInstance implements Parcelable { @Override public String toString() { - return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" + - Integer.toHexString(mMsgTypes); + return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" + + Integer.toHexString(mMsgTypes); } + @Override public int describeContents() { return 0; } @@ -68,6 +69,7 @@ public final class BluetoothMasInstance implements Parcelable { } }; + @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); out.writeString(mName); diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index cecd3dbfff6..dfec4e102fd 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -49,7 +49,7 @@ import java.io.OutputStream; * @since Android 1.0 */ public void write(int oneByte) throws IOException { - byte b[] = new byte[1]; + byte[] b = new byte[1]; b[0] = (byte) oneByte; mSocket.write(b, 0, 1); } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4c00649c947..63e83d22178 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -183,7 +183,7 @@ public final class BluetoothPan implements BluetoothProfile { close(); } - final private IBluetoothStateChangeCallback mStateChangeCallback = + private final IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @Override @@ -238,8 +238,7 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mPanService != null && isEnabled() && - isValidDevice(device)) { + if (mPanService != null && isEnabled() && isValidDevice(device)) { try { return mPanService.connect(device); } catch (RemoteException e) { @@ -278,8 +277,7 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mPanService != null && isEnabled() && - isValidDevice(device)) { + if (mPanService != null && isEnabled() && isValidDevice(device)) { try { return mPanService.disconnect(device); } catch (RemoteException e) { @@ -294,6 +292,7 @@ public final class BluetoothPan implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); if (mPanService != null && isEnabled()) { @@ -311,6 +310,7 @@ public final class BluetoothPan implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); if (mPanService != null && isEnabled()) { @@ -328,6 +328,7 @@ public final class BluetoothPan implements BluetoothProfile { /** * {@inheritDoc} */ + @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (mPanService != null && isEnabled() diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index fe7ce1d62cb..78b7c7b7a70 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -110,7 +110,7 @@ public class BluetoothPbap { public void onServiceDisconnected(); } - final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 3d6d002e9fb..b9b372c8484 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -55,7 +55,7 @@ public final class BluetoothPbapClient 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) { @@ -346,10 +346,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("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 { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 9192ec44ee3..bc8fa846087 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -96,12 +96,12 @@ public interface BluetoothProfile { /** * GATT */ - static public final int GATT = 7; + public static final int GATT = 7; /** * GATT_SERVER */ - static public final int GATT_SERVER = 8; + public static final int GATT_SERVER = 8; /** * MAP Profile @@ -156,7 +156,7 @@ public interface BluetoothProfile { * * @hide */ - static public final int INPUT_HOST = 19; + public static final int INPUT_HOST = 19; /** * Max profile ID. This value should be updated whenever a new profile is added to match diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index ea635b239ca..bcdf4938fe2 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -94,7 +94,7 @@ public final class BluetoothSap implements BluetoothProfile { */ 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); @@ -279,8 +279,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.disconnect(device); } catch (RemoteException e) { @@ -340,8 +339,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); } catch (RemoteException e) { @@ -365,10 +363,9 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("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 { @@ -391,8 +388,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 7b438bdb4e3..58d090dc287 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -183,8 +183,8 @@ public final class BluetoothServerSocket implements Closeable { mMessage = message; } - /*package*/ void setServiceName(String ServiceName) { - mSocket.setServiceName(ServiceName); + /*package*/ void setServiceName(String serviceName) { + mSocket.setServiceName(serviceName); } /** diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index a90dd82b963..4035ee1beb8 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -126,9 +126,9 @@ public final class BluetoothSocket implements Closeable { private int mPort; /* RFCOMM channel or L2CAP psm */ private int mFd; private String mServiceName; - private static int PROXY_CONNECTION_TIMEOUT = 5000; + private static final int PROXY_CONNECTION_TIMEOUT = 5000; - private static int SOCK_SIGNAL_SIZE = 20; + private static final int SOCK_SIGNAL_SIZE = 20; private ByteBuffer mL2capBuffer = null; private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer. @@ -235,7 +235,7 @@ public final class BluetoothSocket implements Closeable { mMin16DigitPin = s.mMin16DigitPin; } - private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { + private BluetoothSocket acceptSocket(String remoteAddr) throws IOException { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); @@ -250,8 +250,8 @@ public final class BluetoothSocket implements Closeable { as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); - as.mAddress = RemoteAddr; - as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); + as.mAddress = remoteAddr; + as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr); return as; } @@ -428,8 +428,7 @@ public final class BluetoothSocket implements Closeable { try { synchronized (this) { if (DBG) { - Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + - mPfd); + Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); } if (mSocketState != SocketState.INIT) return EBADFD; if (mPfd == null) return -1; @@ -589,9 +588,9 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - Log.d(TAG, "close() this: " + this + ", channel: " + mPort + - ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + - "mSocket: " + mSocket + ", mSocketState: " + mSocketState); + Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket + ", mSocketState: " + + mSocketState); if (mSocketState == SocketState.CLOSED) { return; } else { @@ -658,12 +657,12 @@ public final class BluetoothSocket implements Closeable { * Change if a SDP entry should be automatically created. * Must be called before calling .bind, for the call to have any effect. * - * @param mExcludeSdp

      • TRUE - do not auto generate SDP record.
      • FALSE - default - auto + * @param excludeSdp
      • TRUE - do not auto generate SDP record.
      • FALSE - default - auto * generate SPP SDP record. * @hide */ public void setExcludeSdp(boolean excludeSdp) { - this.mExcludeSdp = excludeSdp; + mExcludeSdp = excludeSdp; } private String convertAddr(final byte[] addr) { @@ -675,8 +674,7 @@ public final class BluetoothSocket implements Closeable { byte[] sig = new byte[SOCK_SIGNAL_SIZE]; int ret = readAll(is, sig); if (VDBG) { - Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + - " bytes signal ret: " + ret); + Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret); } ByteBuffer bb = ByteBuffer.wrap(sig); /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ @@ -711,8 +709,7 @@ public final class BluetoothSocket implements Closeable { if (VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request if (VDBG) { - Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + - mL2capBuffer.remaining()); + Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + mL2capBuffer.remaining()); } } } @@ -727,8 +724,8 @@ public final class BluetoothSocket implements Closeable { } left -= ret; if (left != 0) { - Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + - ", expect size: " + b.length); + Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + + ", expect size: " + b.length); } } return b.length; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 3b49abdee09..5bfc54d267c 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -185,11 +185,11 @@ public final class BluetoothUuid { if (uuidA == null && uuidB == null) return true; if (uuidA == null) { - return uuidB.length == 0 ? true : false; + return uuidB.length == 0; } if (uuidB == null) { - return uuidA.length == 0 ? true : false; + return uuidA.length == 0; } HashSet uuidSet = new HashSet(Arrays.asList(uuidA)); @@ -210,7 +210,7 @@ public final class BluetoothUuid { if (uuidA == null && uuidB == null) return true; if (uuidA == null) { - return uuidB.length == 0 ? true : false; + return uuidB.length == 0; } if (uuidB == null) return true; @@ -250,8 +250,8 @@ public final class BluetoothUuid { throw new IllegalArgumentException("uuidBytes cannot be null"); } int length = uuidBytes.length; - if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT && - length != UUID_BYTES_128_BIT) { + if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT + && length != UUID_BYTES_128_BIT) { throw new IllegalArgumentException("uuidBytes length invalid - " + length); } diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 505cc5667de..d6325728697 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -28,13 +28,13 @@ import android.os.Parcelable; * @hide */ public class OobData implements Parcelable { - private byte[] leBluetoothDeviceAddress; - private byte[] securityManagerTk; - private byte[] leSecureConnectionsConfirmation; - private byte[] leSecureConnectionsRandom; + private byte[] mLeBluetoothDeviceAddress; + private byte[] mSecurityManagerTk; + private byte[] mLeSecureConnectionsConfirmation; + private byte[] mLeSecureConnectionsRandom; public byte[] getLeBluetoothDeviceAddress() { - return leBluetoothDeviceAddress; + return mLeBluetoothDeviceAddress; } /** @@ -43,11 +43,11 @@ public class OobData implements Parcelable { * a detailed description. */ public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { - this.leBluetoothDeviceAddress = leBluetoothDeviceAddress; + mLeBluetoothDeviceAddress = leBluetoothDeviceAddress; } public byte[] getSecurityManagerTk() { - return securityManagerTk; + return mSecurityManagerTk; } /** @@ -56,49 +56,50 @@ public class OobData implements Parcelable { * Part A 1.8 for a detailed description. */ public void setSecurityManagerTk(byte[] securityManagerTk) { - this.securityManagerTk = securityManagerTk; + mSecurityManagerTk = securityManagerTk; } public byte[] getLeSecureConnectionsConfirmation() { - return leSecureConnectionsConfirmation; + return mLeSecureConnectionsConfirmation; } public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) { - this.leSecureConnectionsConfirmation = leSecureConnectionsConfirmation; + mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation; } public byte[] getLeSecureConnectionsRandom() { - return leSecureConnectionsRandom; + return mLeSecureConnectionsRandom; } public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) { - this.leSecureConnectionsRandom = leSecureConnectionsRandom; + mLeSecureConnectionsRandom = leSecureConnectionsRandom; } public OobData() { } private OobData(Parcel in) { - leBluetoothDeviceAddress = in.createByteArray(); - securityManagerTk = in.createByteArray(); - leSecureConnectionsConfirmation = in.createByteArray(); - leSecureConnectionsRandom = in.createByteArray(); + mLeBluetoothDeviceAddress = in.createByteArray(); + mSecurityManagerTk = in.createByteArray(); + mLeSecureConnectionsConfirmation = in.createByteArray(); + mLeSecureConnectionsRandom = in.createByteArray(); } + @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { - out.writeByteArray(leBluetoothDeviceAddress); - out.writeByteArray(securityManagerTk); - out.writeByteArray(leSecureConnectionsConfirmation); - out.writeByteArray(leSecureConnectionsRandom); + out.writeByteArray(mLeBluetoothDeviceAddress); + out.writeByteArray(mSecurityManagerTk); + out.writeByteArray(mLeSecureConnectionsConfirmation); + out.writeByteArray(mLeSecureConnectionsRandom); } - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { public OobData createFromParcel(Parcel in) { return new OobData(in); } @@ -107,4 +108,4 @@ public class OobData implements Parcelable { return new OobData[size]; } }; -} \ No newline at end of file +} diff --git a/framework/java/android/bluetooth/SdpMasRecord.java b/framework/java/android/bluetooth/SdpMasRecord.java index b202e536ba0..72d49380b71 100644 --- a/framework/java/android/bluetooth/SdpMasRecord.java +++ b/framework/java/android/bluetooth/SdpMasRecord.java @@ -27,6 +27,7 @@ public class SdpMasRecord implements Parcelable { private final int mSupportedMessageTypes; private final String mServiceName; + /** Message type */ public static final class MessageType { public static final int EMAIL = 0x01; public static final int SMS_GSM = 0x02; @@ -34,30 +35,30 @@ public class SdpMasRecord implements Parcelable { public static final int MMS = 0x08; } - public SdpMasRecord(int mas_instance_id, - int l2cap_psm, - int rfcomm_channel_number, - int profile_version, - int supported_features, - int supported_message_types, - String service_name) { - this.mMasInstanceId = mas_instance_id; - this.mL2capPsm = l2cap_psm; - this.mRfcommChannelNumber = rfcomm_channel_number; - this.mProfileVersion = profile_version; - this.mSupportedFeatures = supported_features; - this.mSupportedMessageTypes = supported_message_types; - this.mServiceName = service_name; + public SdpMasRecord(int masInstanceId, + int l2capPsm, + int rfcommChannelNumber, + int profileVersion, + int supportedFeatures, + int supportedMessageTypes, + String serviceName) { + mMasInstanceId = masInstanceId; + mL2capPsm = l2capPsm; + mRfcommChannelNumber = rfcommChannelNumber; + mProfileVersion = profileVersion; + mSupportedFeatures = supportedFeatures; + mSupportedMessageTypes = supportedMessageTypes; + mServiceName = serviceName; } public SdpMasRecord(Parcel in) { - this.mMasInstanceId = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mRfcommChannelNumber = in.readInt(); - this.mProfileVersion = in.readInt(); - this.mSupportedFeatures = in.readInt(); - this.mSupportedMessageTypes = in.readInt(); - this.mServiceName = in.readString(); + mMasInstanceId = in.readInt(); + mL2capPsm = in.readInt(); + mRfcommChannelNumber = in.readInt(); + mProfileVersion = in.readInt(); + mSupportedFeatures = in.readInt(); + mSupportedMessageTypes = in.readInt(); + mServiceName = in.readString(); } @Override @@ -100,15 +101,13 @@ public class SdpMasRecord implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - - dest.writeInt(this.mMasInstanceId); - dest.writeInt(this.mL2capPsm); - dest.writeInt(this.mRfcommChannelNumber); - dest.writeInt(this.mProfileVersion); - dest.writeInt(this.mSupportedFeatures); - dest.writeInt(this.mSupportedMessageTypes); - dest.writeString(this.mServiceName); - + dest.writeInt(mMasInstanceId); + dest.writeInt(mL2capPsm); + dest.writeInt(mRfcommChannelNumber); + dest.writeInt(mProfileVersion); + dest.writeInt(mSupportedFeatures); + dest.writeInt(mSupportedMessageTypes); + dest.writeString(mServiceName); } @Override diff --git a/framework/java/android/bluetooth/SdpMnsRecord.java b/framework/java/android/bluetooth/SdpMnsRecord.java index 94ae6354425..a781d5df7dd 100644 --- a/framework/java/android/bluetooth/SdpMnsRecord.java +++ b/framework/java/android/bluetooth/SdpMnsRecord.java @@ -25,24 +25,24 @@ public class SdpMnsRecord implements Parcelable { private final int mProfileVersion; private final String mServiceName; - public SdpMnsRecord(int l2cap_psm, - int rfcomm_channel_number, - int profile_version, - int supported_features, - String service_name) { - this.mL2capPsm = l2cap_psm; - this.mRfcommChannelNumber = rfcomm_channel_number; - this.mSupportedFeatures = supported_features; - this.mServiceName = service_name; - this.mProfileVersion = profile_version; + public SdpMnsRecord(int l2capPsm, + int rfcommChannelNumber, + int profileVersion, + int supportedFeatures, + String serviceName) { + mL2capPsm = l2capPsm; + mRfcommChannelNumber = rfcommChannelNumber; + mSupportedFeatures = supportedFeatures; + mServiceName = serviceName; + mProfileVersion = profileVersion; } public SdpMnsRecord(Parcel in) { - this.mRfcommChannelNumber = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mServiceName = in.readString(); - this.mSupportedFeatures = in.readInt(); - this.mProfileVersion = in.readInt(); + mRfcommChannelNumber = in.readInt(); + mL2capPsm = in.readInt(); + mServiceName = in.readString(); + mSupportedFeatures = in.readInt(); + mProfileVersion = in.readInt(); } @Override diff --git a/framework/java/android/bluetooth/SdpOppOpsRecord.java b/framework/java/android/bluetooth/SdpOppOpsRecord.java index 23e301cc06f..e30745b8982 100644 --- a/framework/java/android/bluetooth/SdpOppOpsRecord.java +++ b/framework/java/android/bluetooth/SdpOppOpsRecord.java @@ -35,11 +35,11 @@ public class SdpOppOpsRecord implements Parcelable { public SdpOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList) { super(); - this.mServiceName = serviceName; - this.mRfcommChannel = rfcommChannel; - this.mL2capPsm = l2capPsm; - this.mProfileVersion = version; - this.mFormatsList = formatsList; + mServiceName = serviceName; + mRfcommChannel = rfcommChannel; + mL2capPsm = l2capPsm; + mProfileVersion = version; + mFormatsList = formatsList; } public String getServiceName() { @@ -69,17 +69,17 @@ public class SdpOppOpsRecord implements Parcelable { } public SdpOppOpsRecord(Parcel in) { - this.mRfcommChannel = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mProfileVersion = in.readInt(); - this.mServiceName = in.readString(); + mRfcommChannel = in.readInt(); + mL2capPsm = in.readInt(); + mProfileVersion = in.readInt(); + mServiceName = in.readString(); int arrayLength = in.readInt(); if (arrayLength > 0) { byte[] bytes = new byte[arrayLength]; in.readByteArray(bytes); - this.mFormatsList = bytes; + mFormatsList = bytes; } else { - this.mFormatsList = null; + mFormatsList = null; } } @@ -97,6 +97,7 @@ public class SdpOppOpsRecord implements Parcelable { } } + @Override public String toString() { StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n"); sb.append(" RFCOMM Chan Number: ").append(mRfcommChannel); diff --git a/framework/java/android/bluetooth/SdpPseRecord.java b/framework/java/android/bluetooth/SdpPseRecord.java index 5885f174121..72249d0585c 100644 --- a/framework/java/android/bluetooth/SdpPseRecord.java +++ b/framework/java/android/bluetooth/SdpPseRecord.java @@ -27,27 +27,27 @@ public class SdpPseRecord implements Parcelable { private final int mSupportedRepositories; private final String mServiceName; - public SdpPseRecord(int l2cap_psm, - int rfcomm_channel_number, - int profile_version, - int supported_features, - int supported_repositories, - String service_name) { - this.mL2capPsm = l2cap_psm; - this.mRfcommChannelNumber = rfcomm_channel_number; - this.mProfileVersion = profile_version; - this.mSupportedFeatures = supported_features; - this.mSupportedRepositories = supported_repositories; - this.mServiceName = service_name; + public SdpPseRecord(int l2capPsm, + int rfcommChannelNumber, + int profileVersion, + int supportedFeatures, + int supportedRepositories, + String serviceName) { + mL2capPsm = l2capPsm; + mRfcommChannelNumber = rfcommChannelNumber; + mProfileVersion = profileVersion; + mSupportedFeatures = supportedFeatures; + mSupportedRepositories = supportedRepositories; + mServiceName = serviceName; } public SdpPseRecord(Parcel in) { - this.mRfcommChannelNumber = in.readInt(); - this.mL2capPsm = in.readInt(); - this.mProfileVersion = in.readInt(); - this.mSupportedFeatures = in.readInt(); - this.mSupportedRepositories = in.readInt(); - this.mServiceName = in.readString(); + mRfcommChannelNumber = in.readInt(); + mL2capPsm = in.readInt(); + mProfileVersion = in.readInt(); + mSupportedFeatures = in.readInt(); + mSupportedRepositories = in.readInt(); + mServiceName = in.readString(); } @Override @@ -91,6 +91,7 @@ public class SdpPseRecord implements Parcelable { } + @Override public String toString() { String ret = "Bluetooth MNS SDP Record:\n"; diff --git a/framework/java/android/bluetooth/SdpRecord.java b/framework/java/android/bluetooth/SdpRecord.java index 714ecd8ac09..730862ec6f9 100644 --- a/framework/java/android/bluetooth/SdpRecord.java +++ b/framework/java/android/bluetooth/SdpRecord.java @@ -32,15 +32,15 @@ public class SdpRecord implements Parcelable { + ", rawSize=" + mRawSize + "]"; } - public SdpRecord(int size_record, byte[] record) { - this.mRawData = record; - this.mRawSize = size_record; + public SdpRecord(int sizeRecord, byte[] record) { + mRawData = record; + mRawSize = sizeRecord; } public SdpRecord(Parcel in) { - this.mRawSize = in.readInt(); - this.mRawData = new byte[mRawSize]; - in.readByteArray(this.mRawData); + mRawSize = in.readInt(); + mRawData = new byte[mRawSize]; + in.readByteArray(mRawData); } @@ -51,8 +51,8 @@ public class SdpRecord implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(this.mRawSize); - dest.writeByteArray(this.mRawData); + dest.writeInt(mRawSize); + dest.writeByteArray(mRawData); } diff --git a/framework/java/android/bluetooth/SdpSapsRecord.java b/framework/java/android/bluetooth/SdpSapsRecord.java index 1904118e592..a1e2f7b51f3 100644 --- a/framework/java/android/bluetooth/SdpSapsRecord.java +++ b/framework/java/android/bluetooth/SdpSapsRecord.java @@ -25,18 +25,16 @@ public class SdpSapsRecord implements Parcelable { private final int mProfileVersion; private final String mServiceName; - public SdpSapsRecord(int rfcomm_channel_number, - int profile_version, - String service_name) { - this.mRfcommChannelNumber = rfcomm_channel_number; - this.mProfileVersion = profile_version; - this.mServiceName = service_name; + public SdpSapsRecord(int rfcommChannelNumber, int profileVersion, String serviceName) { + mRfcommChannelNumber = rfcommChannelNumber; + mProfileVersion = profileVersion; + mServiceName = serviceName; } public SdpSapsRecord(Parcel in) { - this.mRfcommChannelNumber = in.readInt(); - this.mProfileVersion = in.readInt(); - this.mServiceName = in.readString(); + mRfcommChannelNumber = in.readInt(); + mProfileVersion = in.readInt(); + mServiceName = in.readString(); } @Override @@ -58,9 +56,9 @@ public class SdpSapsRecord implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(this.mRfcommChannelNumber); - dest.writeInt(this.mProfileVersion); - dest.writeString(this.mServiceName); + dest.writeInt(mRfcommChannelNumber); + dest.writeInt(mProfileVersion); + dest.writeString(mServiceName); } diff --git a/framework/java/android/bluetooth/UidTraffic.java b/framework/java/android/bluetooth/UidTraffic.java index f74ac75863f..cef362b3b57 100644 --- a/framework/java/android/bluetooth/UidTraffic.java +++ b/framework/java/android/bluetooth/UidTraffic.java @@ -91,11 +91,8 @@ public class UidTraffic implements Cloneable, Parcelable { @Override public String toString() { - return "UidTraffic{" + - "mAppUid=" + mAppUid + - ", mRxBytes=" + mRxBytes + - ", mTxBytes=" + mTxBytes + - '}'; + return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes=" + + mTxBytes + '}'; } public static final Creator CREATOR = new Creator() { diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 7c506dbba51..b65c31d1dcf 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -118,12 +118,12 @@ public final class AdvertiseData implements Parcelable { return false; } AdvertiseData other = (AdvertiseData) obj; - return Objects.equals(mServiceUuids, other.mServiceUuids) && - BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) - && - BluetoothLeUtils.equals(mServiceData, other.mServiceData) && - mIncludeDeviceName == other.mIncludeDeviceName && - mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; + return Objects.equals(mServiceUuids, other.mServiceUuids) + && BluetoothLeUtils.equals(mManufacturerSpecificData, + other.mManufacturerSpecificData) + && BluetoothLeUtils.equals(mServiceData, other.mServiceData) + && mIncludeDeviceName == other.mIncludeDeviceName + && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; } @Override diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 430f9bd97e5..35e232c7500 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -86,7 +86,7 @@ public final class AdvertiseSettings implements Parcelable { private AdvertiseSettings(Parcel in) { mAdvertiseMode = in.readInt(); mAdvertiseTxPowerLevel = in.readInt(); - mAdvertiseConnectable = in.readInt() != 0 ? true : false; + mAdvertiseConnectable = in.readInt() != 0; mAdvertiseTimeoutMillis = in.readInt(); } diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index b1c122c9e04..1df35e1e382 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -36,15 +36,15 @@ import android.util.Log; public final class AdvertisingSet { private static final String TAG = "AdvertisingSet"; - private final IBluetoothGatt gatt; - private int advertiserId; + private final IBluetoothGatt mGatt; + private int mAdvertiserId; /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager) { - this.advertiserId = advertiserId; + mAdvertiserId = advertiserId; try { - this.gatt = bluetoothManager.getBluetoothGatt(); + mGatt = bluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { Log.e(TAG, "Failed to get Bluetooth gatt - ", e); throw new IllegalStateException("Failed to get Bluetooth"); @@ -52,7 +52,7 @@ public final class AdvertisingSet { } /* package */ void setAdvertiserId(int advertiserId) { - this.advertiserId = advertiserId; + mAdvertiserId = advertiserId; } /** @@ -71,7 +71,7 @@ public final class AdvertisingSet { public void enableAdvertising(boolean enable, int duration, int maxExtendedAdvertisingEvents) { try { - gatt.enableAdvertisingSet(this.advertiserId, enable, duration, + mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration, maxExtendedAdvertisingEvents); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); @@ -92,7 +92,7 @@ public final class AdvertisingSet { */ public void setAdvertisingData(AdvertiseData advertiseData) { try { - gatt.setAdvertisingData(this.advertiserId, advertiseData); + mGatt.setAdvertisingData(mAdvertiserId, advertiseData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -109,7 +109,7 @@ public final class AdvertisingSet { */ public void setScanResponseData(AdvertiseData scanResponse) { try { - gatt.setScanResponseData(this.advertiserId, scanResponse); + mGatt.setScanResponseData(mAdvertiserId, scanResponse); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -124,7 +124,7 @@ public final class AdvertisingSet { */ public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { - gatt.setAdvertisingParameters(this.advertiserId, parameters); + mGatt.setAdvertisingParameters(mAdvertiserId, parameters); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -137,7 +137,7 @@ public final class AdvertisingSet { */ public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { try { - gatt.setPeriodicAdvertisingParameters(this.advertiserId, parameters); + mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -155,7 +155,7 @@ public final class AdvertisingSet { */ public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { - gatt.setPeriodicAdvertisingData(this.advertiserId, periodicData); + mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -170,7 +170,7 @@ public final class AdvertisingSet { */ public void setPeriodicAdvertisingEnabled(boolean enable) { try { - gatt.setPeriodicAdvertisingEnable(this.advertiserId, enable); + mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -187,7 +187,7 @@ public final class AdvertisingSet { */ public void getOwnAddress() { try { - gatt.getOwnAddress(this.advertiserId); + mGatt.getOwnAddress(mAdvertiserId); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -199,6 +199,6 @@ public final class AdvertisingSet { * @hide */ public int getAdvertiserId() { - return advertiserId; + return mAdvertiserId; } -} \ No newline at end of file +} diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index fa502d37ff3..58a3696fc09 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -125,9 +125,7 @@ public abstract class AdvertisingSetCallback { * @param advertisingSet The advertising set. * @param status Status of the operation. */ - public void - onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int status) { + public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, int status) { } /** @@ -163,4 +161,4 @@ public abstract class AdvertisingSetCallback { */ public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) { } -} \ No newline at end of file +} diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index f795861d19a..0c0291eb0ed 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -97,116 +97,116 @@ public final class AdvertisingSetParameters implements Parcelable { */ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; - private final boolean isLegacy; - private final boolean isAnonymous; - private final boolean includeTxPower; - private final int primaryPhy; - private final int secondaryPhy; - private final boolean connectable; - private final boolean scannable; - private final int interval; - private final int txPowerLevel; + private final boolean mIsLegacy; + private final boolean mIsAnonymous; + private final boolean mIncludeTxPower; + private final int mPrimaryPhy; + private final int mSecondaryPhy; + private final boolean mConnectable; + private final boolean mScannable; + private final int mInterval; + private final int mTxPowerLevel; private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, int interval, int txPowerLevel) { - this.connectable = connectable; - this.scannable = scannable; - this.isLegacy = isLegacy; - this.isAnonymous = isAnonymous; - this.includeTxPower = includeTxPower; - this.primaryPhy = primaryPhy; - this.secondaryPhy = secondaryPhy; - this.interval = interval; - this.txPowerLevel = txPowerLevel; + mConnectable = connectable; + mScannable = scannable; + mIsLegacy = isLegacy; + mIsAnonymous = isAnonymous; + mIncludeTxPower = includeTxPower; + mPrimaryPhy = primaryPhy; + mSecondaryPhy = secondaryPhy; + mInterval = interval; + mTxPowerLevel = txPowerLevel; } private AdvertisingSetParameters(Parcel in) { - connectable = in.readInt() != 0 ? true : false; - scannable = in.readInt() != 0 ? true : false; - isLegacy = in.readInt() != 0 ? true : false; - isAnonymous = in.readInt() != 0 ? true : false; - includeTxPower = in.readInt() != 0 ? true : false; - primaryPhy = in.readInt(); - secondaryPhy = in.readInt(); - interval = in.readInt(); - txPowerLevel = in.readInt(); + mConnectable = in.readInt() != 0; + mScannable = in.readInt() != 0; + mIsLegacy = in.readInt() != 0; + mIsAnonymous = in.readInt() != 0; + mIncludeTxPower = in.readInt() != 0; + mPrimaryPhy = in.readInt(); + mSecondaryPhy = in.readInt(); + mInterval = in.readInt(); + mTxPowerLevel = in.readInt(); } /** * Returns whether the advertisement will be connectable. */ public boolean isConnectable() { - return connectable; + return mConnectable; } /** * Returns whether the advertisement will be scannable. */ public boolean isScannable() { - return scannable; + return mScannable; } /** * Returns whether the legacy advertisement will be used. */ public boolean isLegacy() { - return isLegacy; + return mIsLegacy; } /** * Returns whether the advertisement will be anonymous. */ public boolean isAnonymous() { - return isAnonymous; + return mIsAnonymous; } /** * Returns whether the TX Power will be included. */ public boolean includeTxPower() { - return includeTxPower; + return mIncludeTxPower; } /** * Returns the primary advertising phy. */ public int getPrimaryPhy() { - return primaryPhy; + return mPrimaryPhy; } /** * Returns the secondary advertising phy. */ public int getSecondaryPhy() { - return secondaryPhy; + return mSecondaryPhy; } /** * Returns the advertising interval. */ public int getInterval() { - return interval; + return mInterval; } /** * Returns the TX power level for advertising. */ public int getTxPowerLevel() { - return txPowerLevel; + return mTxPowerLevel; } @Override public String toString() { - return "AdvertisingSetParameters [connectable=" + connectable - + ", isLegacy=" + isLegacy - + ", isAnonymous=" + isAnonymous - + ", includeTxPower=" + includeTxPower - + ", primaryPhy=" + primaryPhy - + ", secondaryPhy=" + secondaryPhy - + ", interval=" + interval - + ", txPowerLevel=" + txPowerLevel + "]"; + return "AdvertisingSetParameters [connectable=" + mConnectable + + ", isLegacy=" + mIsLegacy + + ", isAnonymous=" + mIsAnonymous + + ", includeTxPower=" + mIncludeTxPower + + ", primaryPhy=" + mPrimaryPhy + + ", secondaryPhy=" + mSecondaryPhy + + ", interval=" + mInterval + + ", txPowerLevel=" + mTxPowerLevel + "]"; } @Override @@ -216,15 +216,15 @@ public final class AdvertisingSetParameters implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(connectable ? 1 : 0); - dest.writeInt(scannable ? 1 : 0); - dest.writeInt(isLegacy ? 1 : 0); - dest.writeInt(isAnonymous ? 1 : 0); - dest.writeInt(includeTxPower ? 1 : 0); - dest.writeInt(primaryPhy); - dest.writeInt(secondaryPhy); - dest.writeInt(interval); - dest.writeInt(txPowerLevel); + dest.writeInt(mConnectable ? 1 : 0); + dest.writeInt(mScannable ? 1 : 0); + dest.writeInt(mIsLegacy ? 1 : 0); + dest.writeInt(mIsAnonymous ? 1 : 0); + dest.writeInt(mIncludeTxPower ? 1 : 0); + dest.writeInt(mPrimaryPhy); + dest.writeInt(mSecondaryPhy); + dest.writeInt(mInterval); + dest.writeInt(mTxPowerLevel); } public static final Parcelable.Creator CREATOR = @@ -244,16 +244,15 @@ public final class AdvertisingSetParameters implements Parcelable { * Builder class for {@link AdvertisingSetParameters}. */ public static final class Builder { - - private boolean connectable = false; - private boolean scannable = false; - private boolean isLegacy = false; - private boolean isAnonymous = false; - private boolean includeTxPower = false; - private int primaryPhy = BluetoothDevice.PHY_LE_1M; - private int secondaryPhy = BluetoothDevice.PHY_LE_1M; - private int interval = INTERVAL_LOW; - private int txPowerLevel = TX_POWER_MEDIUM; + private boolean mConnectable = false; + private boolean mScannable = false; + private boolean mIsLegacy = false; + private boolean mIsAnonymous = false; + private boolean mIncludeTxPower = false; + private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M; + private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M; + private int mInterval = INTERVAL_LOW; + private int mTxPowerLevel = TX_POWER_MEDIUM; /** * Set whether the advertisement type should be connectable or @@ -265,7 +264,7 @@ public final class AdvertisingSetParameters implements Parcelable { * non-connectable (false). */ public Builder setConnectable(boolean connectable) { - this.connectable = connectable; + mConnectable = connectable; return this; } @@ -278,7 +277,7 @@ public final class AdvertisingSetParameters implements Parcelable { * non-scannable (false). */ public Builder setScannable(boolean scannable) { - this.scannable = scannable; + mScannable = scannable; return this; } @@ -289,7 +288,7 @@ public final class AdvertisingSetParameters implements Parcelable { * @param isLegacy whether legacy advertising mode should be used. */ public Builder setLegacyMode(boolean isLegacy) { - this.isLegacy = isLegacy; + mIsLegacy = isLegacy; return this; } @@ -302,7 +301,7 @@ public final class AdvertisingSetParameters implements Parcelable { * @param isAnonymous whether anonymous advertising should be used. */ public Builder setAnonymous(boolean isAnonymous) { - this.isAnonymous = isAnonymous; + mIsAnonymous = isAnonymous; return this; } @@ -314,7 +313,7 @@ public final class AdvertisingSetParameters implements Parcelable { * @param includeTxPower whether TX power should be included in extended header */ public Builder setIncludeTxPower(boolean includeTxPower) { - this.includeTxPower = includeTxPower; + mIncludeTxPower = includeTxPower; return this; } @@ -331,11 +330,11 @@ public final class AdvertisingSetParameters implements Parcelable { * @throws IllegalArgumentException If the primaryPhy is invalid. */ public Builder setPrimaryPhy(int primaryPhy) { - if (primaryPhy != BluetoothDevice.PHY_LE_1M && - primaryPhy != BluetoothDevice.PHY_LE_CODED) { + if (primaryPhy != BluetoothDevice.PHY_LE_1M + && primaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); } - this.primaryPhy = primaryPhy; + mPrimaryPhy = primaryPhy; return this; } @@ -354,12 +353,12 @@ public final class AdvertisingSetParameters implements Parcelable { * @throws IllegalArgumentException If the secondaryPhy is invalid. */ public Builder setSecondaryPhy(int secondaryPhy) { - if (secondaryPhy != BluetoothDevice.PHY_LE_1M && - secondaryPhy != BluetoothDevice.PHY_LE_2M && - secondaryPhy != BluetoothDevice.PHY_LE_CODED) { + if (secondaryPhy != BluetoothDevice.PHY_LE_1M + && secondaryPhy != BluetoothDevice.PHY_LE_2M + && secondaryPhy != BluetoothDevice.PHY_LE_CODED) { throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); } - this.secondaryPhy = secondaryPhy; + mSecondaryPhy = secondaryPhy; return this; } @@ -376,7 +375,7 @@ public final class AdvertisingSetParameters implements Parcelable { if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { throw new IllegalArgumentException("unknown interval " + interval); } - this.interval = interval; + mInterval = interval; return this; } @@ -393,10 +392,9 @@ public final class AdvertisingSetParameters implements Parcelable { */ public Builder setTxPowerLevel(int txPowerLevel) { if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { - throw new IllegalArgumentException("unknown txPowerLevel " + - txPowerLevel); + throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel); } - this.txPowerLevel = txPowerLevel; + mTxPowerLevel = txPowerLevel; return this; } @@ -406,35 +404,34 @@ public final class AdvertisingSetParameters implements Parcelable { * @throws IllegalStateException if invalid combination of parameters is used. */ public AdvertisingSetParameters build() { - if (isLegacy) { - if (isAnonymous) { + if (mIsLegacy) { + if (mIsAnonymous) { throw new IllegalArgumentException("Legacy advertising can't be anonymous"); } - if (connectable == true && scannable == false) { + if (mConnectable && !mScannable) { throw new IllegalStateException( "Legacy advertisement can't be connectable and non-scannable"); } - if (includeTxPower) { + if (mIncludeTxPower) { throw new IllegalStateException( "Legacy advertising can't include TX power level in header"); } } else { - if (connectable && scannable) { + if (mConnectable && mScannable) { throw new IllegalStateException( "Advertising can't be both connectable and scannable"); } - if (isAnonymous && connectable) { + if (mIsAnonymous && mConnectable) { throw new IllegalStateException( "Advertising can't be both connectable and anonymous"); } } - return new AdvertisingSetParameters(connectable, scannable, isLegacy, isAnonymous, - includeTxPower, primaryPhy, - secondaryPhy, interval, txPowerLevel); + return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous, + mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel); } } -} \ No newline at end of file +} diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 08128deda5c..0fb4ba1a876 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -115,8 +115,8 @@ public final class BluetoothLeAdvertiser { throw new IllegalArgumentException("callback cannot be null"); } boolean isConnectable = settings.isConnectable(); - if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES || - totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { + if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES + || totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); return; } @@ -177,9 +177,9 @@ public final class BluetoothLeAdvertiser { @Override public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, int status) { - if (enabled == true) { - Log.e(TAG, "Legacy advertiser should be only disabled on timeout," + - " but was enabled!"); + if (enabled) { + Log.e(TAG, "Legacy advertiser should be only disabled on timeout," + + " but was enabled!"); return; } @@ -396,11 +396,11 @@ public final class BluetoothLeAdvertiser { "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); } - if (maxExtendedAdvertisingEvents != 0 && - !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { + if (maxExtendedAdvertisingEvents != 0 + && !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { throw new IllegalArgumentException( - "Can't use maxExtendedAdvertisingEvents with controller that don't support " + - "LE Extended Advertising"); + "Can't use maxExtendedAdvertisingEvents with controller that don't support " + + "LE Extended Advertising"); } if (duration < 0 || duration > 65535) { @@ -488,18 +488,16 @@ public final class BluetoothLeAdvertiser { } // 16 bit service uuids are grouped into one field when doing advertising. if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; } // 32 bit service uuids are grouped into one field when doing advertising. if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; } // 128 bit service uuids are grouped into one field when doing advertising. if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + - num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } for (ParcelUuid uuid : data.getServiceData().keySet()) { @@ -508,8 +506,8 @@ public final class BluetoothLeAdvertiser { + byteLength(data.getServiceData().get(uuid)); } for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { - size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + - byteLength(data.getManufacturerSpecificData().valueAt(i)); + size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH + + byteLength(data.getManufacturerSpecificData().valueAt(i)); } if (data.getIncludeTxPowerLevel()) { size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 41c4f5ad5df..19fab37a109 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -62,8 +62,8 @@ public final class BluetoothLeScanner { * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this * extra will not be available. */ - public static final String EXTRA_LIST_SCAN_RESULT - = "android.bluetooth.le.extra.LIST_SCAN_RESULT"; + public static final String EXTRA_LIST_SCAN_RESULT = + "android.bluetooth.le.extra.LIST_SCAN_RESULT"; /** * Optional extra indicating the error code, if any. The error code will be one of the @@ -419,8 +419,8 @@ public final class BluetoothLeScanner { */ @Override public void onScannerRegistered(int status, int scannerId) { - Log.d(TAG, "onScannerRegistered() - status=" + status + - " scannerId=" + scannerId + " mScannerId=" + mScannerId); + Log.d(TAG, "onScannerRegistered() - status=" + status + + " scannerId=" + scannerId + " mScannerId=" + mScannerId); synchronized (this) { if (status == BluetoothGatt.GATT_SUCCESS) { try { @@ -481,8 +481,7 @@ public final class BluetoothLeScanner { @Override public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { if (VDBG) { - Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + - " " + scanResult.toString()); + Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); } // Check null in case the scan has been stopped @@ -574,8 +573,8 @@ public final class BluetoothLeScanner { if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { // For onlost/onfound, we required hw support be available - return (mBluetoothAdapter.isOffloadedFilteringSupported() && - mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); + return (mBluetoothAdapter.isOffloadedFilteringSupported() + && mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); } return true; } diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java index afec72bc36e..6381f557c1b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -92,8 +92,8 @@ public class BluetoothLeUtils { // Keys are guaranteed in ascending order when indices are in ascending order. for (int i = 0; i < array.size(); ++i) { - if (array.keyAt(i) != otherArray.keyAt(i) || - !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { + if (array.keyAt(i) != otherArray.keyAt(i) + || !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { return false; } } @@ -132,8 +132,7 @@ public class BluetoothLeUtils { * BluetoothAdapter#STATE_ON}. */ static void checkAdapterStateOn(BluetoothAdapter adapter) { - if (adapter == null - || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) { + if (adapter == null || !adapter.isLeEnabled()) { throw new IllegalStateException("BT Adapter is not turned ON"); } } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 5e7f4c08bdf..0f1a8e913ba 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -57,7 +57,7 @@ public final class PeriodicAdvertisingManager { /* maps callback, to callback wrapper and sync handle */ Map callbackWrappers; + IPeriodicAdvertisingCallback /* callbackWrapper */> mCallbackWrappers; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. @@ -68,7 +68,7 @@ public final class PeriodicAdvertisingManager { public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - callbackWrappers = new IdentityHashMap<>(); + mCallbackWrappers = new IdentityHashMap<>(); } /** @@ -153,7 +153,7 @@ public final class PeriodicAdvertisingManager { } IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); - callbackWrappers.put(callback, wrapped); + mCallbackWrappers.put(callback, wrapped); try { gatt.registerSync(scanResult, skip, timeout, wrapped); @@ -183,7 +183,7 @@ public final class PeriodicAdvertisingManager { return; } - IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback); + IPeriodicAdvertisingCallback wrapper = mCallbackWrappers.remove(callback); if (wrapper == null) { throw new IllegalArgumentException("callback was not properly registered"); } @@ -213,7 +213,7 @@ public final class PeriodicAdvertisingManager { // App can still unregister the sync until notified it failed. Remove // callback // after app was notifed. - callbackWrappers.remove(callback); + mCallbackWrappers.remove(callback); } } }); @@ -233,9 +233,9 @@ public final class PeriodicAdvertisingManager { @Override public void run() { callback.onSyncLost(syncHandle); - // App can still unregister the sync until notified it's lost. Remove callback after - // app was notifed. - callbackWrappers.remove(callback); + // App can still unregister the sync until notified it's lost. + // Remove callback after app was notifed. + mCallbackWrappers.remove(callback); } }); } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index 5bd47756c23..e3a130c4b43 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -29,24 +29,24 @@ public final class PeriodicAdvertisingParameters implements Parcelable { private static final int INTERVAL_MIN = 80; private static final int INTERVAL_MAX = 65519; - private final boolean includeTxPower; - private final int interval; + private final boolean mIncludeTxPower; + private final int mInterval; private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) { - this.includeTxPower = includeTxPower; - this.interval = interval; + mIncludeTxPower = includeTxPower; + mInterval = interval; } private PeriodicAdvertisingParameters(Parcel in) { - includeTxPower = in.readInt() != 0 ? true : false; - interval = in.readInt(); + mIncludeTxPower = in.readInt() != 0; + mInterval = in.readInt(); } /** * Returns whether the TX Power will be included. */ public boolean getIncludeTxPower() { - return includeTxPower; + return mIncludeTxPower; } /** @@ -54,7 +54,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { * Valid values are from 80 (100ms) to 65519 (81.89875s). */ public int getInterval() { - return interval; + return mInterval; } @Override @@ -64,8 +64,8 @@ public final class PeriodicAdvertisingParameters implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(includeTxPower ? 1 : 0); - dest.writeInt(interval); + dest.writeInt(mIncludeTxPower ? 1 : 0); + dest.writeInt(mInterval); } public static final Parcelable @@ -83,15 +83,15 @@ public final class PeriodicAdvertisingParameters implements Parcelable { }; public static final class Builder { - private boolean includeTxPower = false; - private int interval = INTERVAL_MAX; + private boolean mIncludeTxPower = false; + private int mInterval = INTERVAL_MAX; /** * Whether the transmission power level should be included in the periodic * packet. */ public Builder setIncludeTxPower(boolean includeTxPower) { - this.includeTxPower = includeTxPower; + mIncludeTxPower = includeTxPower; return this; } @@ -104,10 +104,10 @@ public final class PeriodicAdvertisingParameters implements Parcelable { */ public Builder setInterval(int interval) { if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { - throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN + - "-" + INTERVAL_MAX + ")"); + throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN + + "-" + INTERVAL_MAX + ")"); } - this.interval = interval; + mInterval = interval; return this; } @@ -115,7 +115,7 @@ public final class PeriodicAdvertisingParameters implements Parcelable { * Build the {@link AdvertisingSetParameters} object. */ public PeriodicAdvertisingParameters build() { - return new PeriodicAdvertisingParameters(includeTxPower, interval); + return new PeriodicAdvertisingParameters(mIncludeTxPower, mInterval); } } } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 9f1099b125d..55c3a730a83 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -40,28 +40,28 @@ public final class PeriodicAdvertisingReport implements Parcelable { */ public static final int DATA_INCOMPLETE_TRUNCATED = 2; - private int syncHandle; - private int txPower; - private int rssi; - private int dataStatus; + private int mSyncHandle; + private int mTxPower; + private int mRssi; + private int mDataStatus; // periodic advertising data. @Nullable - private ScanRecord data; + private ScanRecord mData; // Device timestamp when the result was last seen. - private long timestampNanos; + private long mTimestampNanos; /** * Constructor of periodic advertising result. */ public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi, int dataStatus, ScanRecord data) { - this.syncHandle = syncHandle; - this.txPower = txPower; - this.rssi = rssi; - this.dataStatus = dataStatus; - this.data = data; + mSyncHandle = syncHandle; + mTxPower = txPower; + mRssi = rssi; + mDataStatus = dataStatus; + mData = data; } private PeriodicAdvertisingReport(Parcel in) { @@ -70,25 +70,25 @@ public final class PeriodicAdvertisingReport implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(syncHandle); - dest.writeLong(txPower); - dest.writeInt(rssi); - dest.writeInt(dataStatus); - if (data != null) { + dest.writeInt(mSyncHandle); + dest.writeLong(mTxPower); + dest.writeInt(mRssi); + dest.writeInt(mDataStatus); + if (mData != null) { dest.writeInt(1); - dest.writeByteArray(data.getBytes()); + dest.writeByteArray(mData.getBytes()); } else { dest.writeInt(0); } } private void readFromParcel(Parcel in) { - syncHandle = in.readInt(); - txPower = in.readInt(); - rssi = in.readInt(); - dataStatus = in.readInt(); + mSyncHandle = in.readInt(); + mTxPower = in.readInt(); + mRssi = in.readInt(); + mDataStatus = in.readInt(); if (in.readInt() == 1) { - data = ScanRecord.parseFromBytes(in.createByteArray()); + mData = ScanRecord.parseFromBytes(in.createByteArray()); } } @@ -101,7 +101,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { * Returns the synchronization handle. */ public int getSyncHandle() { - return syncHandle; + return mSyncHandle; } /** @@ -109,14 +109,14 @@ public final class PeriodicAdvertisingReport implements Parcelable { * of 127 means information was not available. */ public int getTxPower() { - return txPower; + return mTxPower; } /** * Returns the received signal strength in dBm. The valid range is [-127, 20]. */ public int getRssi() { - return rssi; + return mRssi; } /** @@ -124,7 +124,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}. */ public int getDataStatus() { - return dataStatus; + return mDataStatus; } /** @@ -132,19 +132,19 @@ public final class PeriodicAdvertisingReport implements Parcelable { */ @Nullable public ScanRecord getData() { - return data; + return mData; } /** * Returns timestamp since boot when the scan record was observed. */ public long getTimestampNanos() { - return timestampNanos; + return mTimestampNanos; } @Override public int hashCode() { - return Objects.hash(syncHandle, txPower, rssi, dataStatus, data, timestampNanos); + return Objects.hash(mSyncHandle, mTxPower, mRssi, mDataStatus, mData, mTimestampNanos); } @Override @@ -156,19 +156,19 @@ public final class PeriodicAdvertisingReport implements Parcelable { return false; } PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj; - return (syncHandle == other.syncHandle) && - (txPower == other.txPower) && - (rssi == other.rssi) && - (dataStatus == other.dataStatus) && - Objects.equals(data, other.data) && - (timestampNanos == other.timestampNanos); + return (mSyncHandle == other.mSyncHandle) + && (mTxPower == other.mTxPower) + && (mRssi == other.mRssi) + && (mDataStatus == other.mDataStatus) + && Objects.equals(mData, other.mData) + && (mTimestampNanos == other.mTimestampNanos); } @Override public String toString() { - return "PeriodicAdvertisingReport{syncHandle=" + syncHandle + - ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus + - ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}'; + return "PeriodicAdvertisingReport{syncHandle=" + mSyncHandle + + ", txPower=" + mTxPower + ", rssi=" + mRssi + ", dataStatus=" + mDataStatus + + ", data=" + Objects.toString(mData) + ", timestampNanos=" + mTimestampNanos + '}'; } public static final Parcelable.Creator CREATOR = diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java index 75139b48ce2..63bdf69e727 100644 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -78,8 +78,8 @@ public final class ResultStorageDescriptor implements Parcelable { mLength = in.readInt(); } - public static final Parcelable.Creator - CREATOR = new Creator() { + public static final Parcelable.Creator CREATOR = + new Creator() { @Override public ResultStorageDescriptor createFromParcel(Parcel source) { return new ResultStorageDescriptor(source); diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index f91cb7559d3..c3fae7d470a 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -145,8 +145,8 @@ public final class ScanFilter implements Parcelable { /** * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. */ - public static final Creator - CREATOR = new Creator() { + public static final Creator CREATOR = + new Creator() { @Override public ScanFilter[] newArray(int size) { @@ -403,16 +403,16 @@ public final class ScanFilter implements Parcelable { return false; } ScanFilter other = (ScanFilter) obj; - return Objects.equals(mDeviceName, other.mDeviceName) && - Objects.equals(mDeviceAddress, other.mDeviceAddress) && - mManufacturerId == other.mManufacturerId && - Objects.deepEquals(mManufacturerData, other.mManufacturerData) && - Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && - Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && - Objects.deepEquals(mServiceData, other.mServiceData) && - Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && - Objects.equals(mServiceUuid, other.mServiceUuid) && - Objects.equals(mServiceUuidMask, other.mServiceUuidMask); + return Objects.equals(mDeviceName, other.mDeviceName) + && Objects.equals(mDeviceAddress, other.mDeviceAddress) + && mManufacturerId == other.mManufacturerId + && Objects.deepEquals(mManufacturerData, other.mManufacturerData) + && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) + && Objects.equals(mServiceDataUuid, other.mServiceDataUuid) + && Objects.deepEquals(mServiceData, other.mServiceData) + && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) + && Objects.equals(mServiceUuid, other.mServiceUuid) + && Objects.equals(mServiceUuidMask, other.mServiceUuidMask); } /** diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 9e5a29aa344..f8aaba910b7 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -247,8 +247,8 @@ public final class ScanRecord { case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // The first two bytes of the manufacturer specific data are // manufacturer ids in little endian. - int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + - (scanRecord[currentPos] & 0xFF); + int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) + + (scanRecord[currentPos] & 0xFF); byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, dataLength - 2); manufacturerData.put(manufacturerId, manufacturerDataBytes); diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 774382243b8..f87a47fb936 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -101,6 +101,7 @@ public final class ScanResult implements Parcelable { * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, * ScanRecord, long)} */ + @Deprecated public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, long timestampNanos) { mDevice = device; @@ -316,25 +317,25 @@ public final class ScanResult implements Parcelable { return false; } ScanResult other = (ScanResult) obj; - return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) && - Objects.equals(mScanRecord, other.mScanRecord) && - (mTimestampNanos == other.mTimestampNanos) && - mEventType == other.mEventType && - mPrimaryPhy == other.mPrimaryPhy && - mSecondaryPhy == other.mSecondaryPhy && - mAdvertisingSid == other.mAdvertisingSid && - mTxPower == other.mTxPower && - mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; + return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) + && Objects.equals(mScanRecord, other.mScanRecord) + && (mTimestampNanos == other.mTimestampNanos) + && mEventType == other.mEventType + && mPrimaryPhy == other.mPrimaryPhy + && mSecondaryPhy == other.mSecondaryPhy + && mAdvertisingSid == other.mAdvertisingSid + && mTxPower == other.mTxPower + && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; } @Override public String toString() { - return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + - Objects.toString(mScanRecord) + ", rssi=" + mRssi + - ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + - ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + - ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + - ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; + return "ScanResult{" + "device=" + mDevice + ", scanRecord=" + + Objects.toString(mScanRecord) + ", rssi=" + mRssi + + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType + + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower + + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; } public static final Parcelable.Creator CREATOR = new Creator() { diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index d2792e0aa45..35ed424aa2d 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -221,7 +221,7 @@ public final class ScanSettings implements Parcelable { mReportDelayMillis = in.readLong(); mMatchMode = in.readInt(); mNumOfMatchesPerFilter = in.readInt(); - mLegacy = in.readInt() != 0 ? true : false; + mLegacy = in.readInt() != 0; mPhy = in.readInt(); } @@ -242,8 +242,8 @@ public final class ScanSettings implements Parcelable { return 0; } - public static final Parcelable.Creator - CREATOR = new Creator() { + public static final Parcelable.Creator CREATOR = + new Creator() { @Override public ScanSettings[] newArray(int size) { return new ScanSettings[size]; @@ -300,9 +300,9 @@ public final class ScanSettings implements Parcelable { // Returns true if the callbackType is valid. private boolean isValidCallbackType(int callbackType) { - if (callbackType == CALLBACK_TYPE_ALL_MATCHES || - callbackType == CALLBACK_TYPE_FIRST_MATCH || - callbackType == CALLBACK_TYPE_MATCH_LOST) { + if (callbackType == CALLBACK_TYPE_ALL_MATCHES + || callbackType == CALLBACK_TYPE_FIRST_MATCH + || callbackType == CALLBACK_TYPE_MATCH_LOST) { return true; } return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST); -- GitLab From 01ea3736138c612f04ccee60adf68a5b0e2d5b7d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 28 Aug 2017 04:12:49 -0700 Subject: [PATCH 0826/1408] Fix GATT client leakage when scan is throttled (1/2) Currently, scan throttling happens after client is registered, but before the scan is started. This might lead to scan client being leaked. This patch fixed that by moving check before client registration. Bug: 64887233 Test: manual Change-Id: I22ae624a0c51110cb69679f796926e3b2b36d0ac --- .../java/android/bluetooth/le/BluetoothLeScanner.java | 10 +++++++++- framework/java/android/bluetooth/le/ScanCallback.java | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 19fab37a109..ad9e20b9292 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -344,6 +344,7 @@ public final class BluetoothLeScanner { private List> mResultStorages; // mLeHandle 0: not registered + // -2: registration failed because app is scanning to frequently // -1: scan stopped or registration failed // > 0: registered and scan started private int mScannerId; @@ -364,7 +365,7 @@ public final class BluetoothLeScanner { public void startRegistration() { synchronized (this) { // Scan stopped. - if (mScannerId == -1) return; + if (mScannerId == -1 || mScannerId == -2) return; try { mBluetoothGatt.registerScanner(this, mWorkSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); @@ -378,6 +379,10 @@ public final class BluetoothLeScanner { // Registration timed out or got exception, reset scannerId to -1 so no // subsequent operations can proceed. if (mScannerId == 0) mScannerId = -1; + + // If scanning too frequently, don't report anything to the app. + if (mScannerId == -2) return; + postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); } @@ -437,6 +442,9 @@ public final class BluetoothLeScanner { Log.e(TAG, "fail to start le scan: " + e); mScannerId = -1; } + } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) { + // applicaiton was scanning too frequently + mScannerId = -2; } else { // registration failed mScannerId = -1; diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index fcbc2c74f0d..53d9310a123 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -51,6 +51,12 @@ public abstract class ScanCallback { */ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; + /** + * Fails to start scan as application tries to scan too frequently. + * @hide + */ + public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6; + static final int NO_ERROR = 0; /** -- GitLab From 7ec636e0b36715c0c8007c969811b20743e8d49c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 28 Aug 2017 04:12:49 -0700 Subject: [PATCH 0827/1408] Fix GATT client leakage when scan is throttled (1/2) Currently, scan throttling happens after client is registered, but before the scan is started. This might lead to scan client being leaked. This patch fixed that by moving check before client registration. Bug: 64887233 Test: manual Change-Id: I22ae624a0c51110cb69679f796926e3b2b36d0ac Merged-In: I22ae624a0c51110cb69679f796926e3b2b36d0ac (cherry picked from commit 01ea3736138c612f04ccee60adf68a5b0e2d5b7d) --- .../java/android/bluetooth/le/BluetoothLeScanner.java | 10 +++++++++- framework/java/android/bluetooth/le/ScanCallback.java | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index e3bc78e5a2b..9e9c8fe71be 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -344,6 +344,7 @@ public final class BluetoothLeScanner { private List> mResultStorages; // mLeHandle 0: not registered + // -2: registration failed because app is scanning to frequently // -1: scan stopped or registration failed // > 0: registered and scan started private int mScannerId; @@ -364,7 +365,7 @@ public final class BluetoothLeScanner { public void startRegistration() { synchronized (this) { // Scan stopped. - if (mScannerId == -1) return; + if (mScannerId == -1 || mScannerId == -2) return; try { mBluetoothGatt.registerScanner(this, mWorkSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); @@ -378,6 +379,10 @@ public final class BluetoothLeScanner { // Registration timed out or got exception, reset scannerId to -1 so no // subsequent operations can proceed. if (mScannerId == 0) mScannerId = -1; + + // If scanning too frequently, don't report anything to the app. + if (mScannerId == -2) return; + postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); } @@ -437,6 +442,9 @@ public final class BluetoothLeScanner { Log.e(TAG, "fail to start le scan: " + e); mScannerId = -1; } + } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) { + // applicaiton was scanning too frequently + mScannerId = -2; } else { // registration failed mScannerId = -1; diff --git a/framework/java/android/bluetooth/le/ScanCallback.java b/framework/java/android/bluetooth/le/ScanCallback.java index aff2e909502..57c9b4033ed 100644 --- a/framework/java/android/bluetooth/le/ScanCallback.java +++ b/framework/java/android/bluetooth/le/ScanCallback.java @@ -50,6 +50,12 @@ public abstract class ScanCallback { */ public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; + /** + * Fails to start scan as application tries to scan too frequently. + * @hide + */ + public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6; + static final int NO_ERROR = 0; /** -- GitLab From 97de572b1b76ba72740bea672a89a8f52a80d73c Mon Sep 17 00:00:00 2001 From: Vinay Kalia Date: Fri, 1 Sep 2017 12:18:10 -0700 Subject: [PATCH 0828/1408] Update documentation for startScan With change c4a1e1, unfiltered BLE scans are stopped on screen off and resumed when screen is turned back on. This is done to save power. This change updates the documentation accordingly. BUG: 62264269 Test: Documentation update. Change-Id: I2d8e9f9f122f978c4d4f59d4139cb51cd4e4a123 --- .../java/android/bluetooth/le/BluetoothLeScanner.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index ad9e20b9292..dd1a61c3164 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -99,7 +99,9 @@ public final class BluetoothLeScanner { /** * Start Bluetooth LE scan with default parameters and no filters. The scan results will be - * delivered through {@code callback}. + * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen + * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use + * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}. *

        * An app must hold * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or @@ -116,6 +118,9 @@ public final class BluetoothLeScanner { /** * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. + * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is + * resumed when screen is turned on again. To avoid this, do filetered scanning by + * using proper {@link ScanFilter}. *

        * An app must hold * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or -- GitLab From cc1d291b44eb0575180dfad2e0d1bc924b5d9849 Mon Sep 17 00:00:00 2001 From: Vinay Kalia Date: Tue, 5 Sep 2017 13:51:58 -0700 Subject: [PATCH 0829/1408] Update ScanSettings documentation for scan modes SCAN_MODE_LOW_POWER is enforced for applications running in background. Test: BLE scanning applications. BUG: 38198694 BUG: 62491228 Change-Id: Ib2b6c297298e05f1b088411e94cfe4789dde0821 --- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 35ed424aa2d..8fdcba85d33 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -35,7 +35,7 @@ public final class ScanSettings implements Parcelable { /** * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the - * least power. + * least power. This mode is enforced if the scanning application is not in foreground. */ public static final int SCAN_MODE_LOW_POWER = 0; -- GitLab From 1f686f645294d624f8b61e4761fd14787a496c75 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 17 Aug 2017 12:11:18 -0700 Subject: [PATCH 0830/1408] 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 --- .../android/bluetooth/BluetoothA2dpSink.java | 81 ++++---- .../bluetooth/BluetoothAvrcpController.java | 54 ++--- .../android/bluetooth/BluetoothDevice.java | 143 +++++++------ .../android/bluetooth/BluetoothHeadset.java | 181 +++++++++-------- .../bluetooth/BluetoothHeadsetClient.java | 188 ++++++++++-------- .../android/bluetooth/BluetoothHealth.java | 58 +++--- .../bluetooth/BluetoothInputDevice.java | 123 ++++++------ .../android/bluetooth/BluetoothInputHost.java | 65 +++--- .../java/android/bluetooth/BluetoothMap.java | 68 ++++--- .../android/bluetooth/BluetoothMapClient.java | 72 ++++--- .../java/android/bluetooth/BluetoothPan.java | 61 +++--- .../java/android/bluetooth/BluetoothPbap.java | 22 +- .../bluetooth/BluetoothPbapClient.java | 63 +++--- .../java/android/bluetooth/BluetoothSap.java | 72 +++---- 14 files changed, 680 insertions(+), 571 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 611531c4f7c..faab000a899 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -125,7 +125,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothA2dpSink mService; + private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -240,15 +240,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -279,15 +280,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -297,15 +299,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -315,15 +318,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -333,16 +337,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -359,16 +363,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getAudioConfig(device); + return service.getAudioConfig(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } @@ -389,20 +393,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink 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, "Stack:" + 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; } @@ -421,16 +425,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getPriority(device); + return service.getPriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.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 BluetoothProfile.PRIORITY_OFF; } @@ -442,16 +446,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -485,7 +489,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, BluetoothA2dpSink.this); @@ -502,15 +505,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 7528aa97210..5f0e5d97447 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -81,7 +81,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothAvrcpController mService; + private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -179,15 +179,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -197,15 +198,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -215,16 +217,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -236,9 +238,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - settings = mService.getPlayerSettings(device); + settings = service.getPlayerSettings(device); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getMetadata() " + e); return null; @@ -253,15 +256,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.setPlayerApplicationSetting(plAppSetting); + return service.setPlayerApplicationSetting(plAppSetting); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); 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; } @@ -272,23 +276,23 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - mService.sendGroupNavigationCmd(device, keyCode, keyState); + service.sendGroupNavigationCmd(device, keyCode, keyState); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); return; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, BluetoothAvrcpController.this); @@ -305,15 +309,11 @@ public final class BluetoothAvrcpController implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3ab2c4a8f44..d982bb7ffb4 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -712,7 +712,7 @@ public final class BluetoothDevice implements Parcelable { * getService() called. * TODO: Unify implementation of sService amongst BluetoothFoo API's */ - private static IBluetooth sService; + private static volatile IBluetooth sService; private final String mAddress; @@ -839,12 +839,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public String getName() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); return null; } try { - return sService.getRemoteName(this); + return service.getRemoteName(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -859,12 +860,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getType() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); return DEVICE_TYPE_UNKNOWN; } try { - return sService.getRemoteType(this); + return service.getRemoteType(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -879,12 +881,13 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public String getAlias() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias"); return null; } try { - return sService.getRemoteAlias(this); + return service.getRemoteAlias(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -902,12 +905,13 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setAlias(String alias) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); return false; } try { - return sService.setRemoteAlias(this, alias); + return service.setRemoteAlias(this, alias); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -942,12 +946,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBatteryLevel() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); return BATTERY_LEVEL_UNKNOWN; } try { - return sService.getBatteryLevel(this); + return service.getBatteryLevel(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -966,7 +971,8 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } @@ -974,7 +980,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "createBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return sService.createBond(this, TRANSPORT_AUTO); + return service.createBond(this, TRANSPORT_AUTO); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -998,7 +1004,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBond(int transport) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } @@ -1009,7 +1016,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "createBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return sService.createBond(this, transport); + return service.createBond(this, transport); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1035,8 +1042,13 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBondOutOfBand(int transport, OobData oobData) { + final IBluetooth service = sService; + if (service == null) { + Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); + return false; + } try { - return sService.createBondOutOfBand(this, transport, oobData); + return service.createBondOutOfBand(this, transport, oobData); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1045,8 +1057,13 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean isBondingInitiatedLocally() { + final IBluetooth service = sService; + if (service == null) { + Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed"); + return false; + } try { - return sService.isBondingInitiatedLocally(this); + return service.isBondingInitiatedLocally(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1082,7 +1099,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean cancelBondProcess() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond"); return false; } @@ -1090,7 +1108,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "cancelBondProcess() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return sService.cancelBondProcess(this); + return service.cancelBondProcess(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1108,7 +1126,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean removeBond() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond"); return false; } @@ -1116,7 +1135,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "removeBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return sService.removeBond(this); + return service.removeBond(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1134,19 +1153,15 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBondState() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get bond state"); return BOND_NONE; } try { - return sService.getBondState(this); + return service.getBondState(this); } catch (RemoteException e) { Log.e(TAG, "", e); - } catch (NullPointerException npe) { - // Handle case where bluetooth service proxy - // is already null. - Log.e(TAG, "NullPointerException for getBondState() of device (" - + getAddress() + ")", npe); } return BOND_NONE; } @@ -1160,12 +1175,13 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi public boolean isConnected() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { // BT is not enabled, we cannot be connected. return false; } try { - return sService.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; + return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1182,12 +1198,13 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi public boolean isEncrypted() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { // BT is not enabled, we cannot be connected. return false; } try { - return sService.getConnectionState(this) > CONNECTION_STATE_CONNECTED; + return service.getConnectionState(this) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1201,12 +1218,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothClass getBluetoothClass() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); return null; } try { - int classInt = sService.getRemoteClass(this); + int classInt = service.getRemoteClass(this); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) { @@ -1227,12 +1245,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public ParcelUuid[] getUuids() { - if (sService == null || !isBluetoothEnabled()) { + final IBluetooth service = sService; + if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); return null; } try { - return sService.getRemoteUuids(this); + return service.getRemoteUuids(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1254,7 +1273,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { - IBluetooth service = sService; + final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; @@ -1289,12 +1308,13 @@ public final class BluetoothDevice implements Parcelable { */ /** @hide */ public boolean sdpSearch(ParcelUuid uuid) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); return false; } try { - return sService.sdpSearch(this, uuid); + return service.sdpSearch(this, uuid); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1308,12 +1328,13 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error */ public boolean setPin(byte[] pin) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); return false; } try { - return sService.setPin(this, true, pin.length, pin); + return service.setPin(this, true, pin.length, pin); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1337,12 +1358,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); return false; } try { - return sService.setPairingConfirmation(this, confirm); + return service.setPairingConfirmation(this, confirm); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1361,12 +1383,13 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean cancelPairingUserInput() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create pairing user input"); return false; } try { - return sService.cancelBondProcess(this); + return service.cancelBondProcess(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1400,11 +1423,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getPhonebookAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getPhonebookAccessPermission(this); + return service.getPhonebookAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1421,11 +1445,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setPhonebookAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setPhonebookAccessPermission(this, value); + return service.setPhonebookAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1440,11 +1465,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getMessageAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getMessageAccessPermission(this); + return service.getMessageAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1461,11 +1487,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setMessageAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setMessageAccessPermission(this, value); + return service.setMessageAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1480,11 +1507,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getSimAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getSimAccessPermission(this); + return service.getSimAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1501,11 +1529,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setSimAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setSimAccessPermission(this, value); + return service.setSimAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index be1ce63cadc..85550c7720a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -306,7 +306,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHeadset mService; + private volatile IBluetoothHeadset mService; private BluetoothAdapter mAdapter; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -418,15 +418,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } 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; } @@ -457,15 +458,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset 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())); 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; } @@ -475,15 +477,16 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset 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(); } @@ -493,15 +496,16 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset 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(); } @@ -511,15 +515,16 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset 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; } @@ -540,19 +545,20 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset 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; } @@ -571,15 +577,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset 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; } @@ -605,14 +612,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startVoiceRecognition(device); + return service.startVoiceRecognition(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; } @@ -627,14 +635,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopVoiceRecognition(device); + return service.stopVoiceRecognition(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; } @@ -648,14 +657,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.isAudioConnected(device); + return service.isAudioConnected(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; } @@ -674,14 +684,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getBatteryUsageHint(BluetoothDevice device) { if (VDBG) log("getBatteryUsageHint()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getBatteryUsageHint(device); + return service.getBatteryUsageHint(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 -1; } @@ -704,9 +715,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean acceptIncomingConnect(BluetoothDevice device) { if (DBG) log("acceptIncomingConnect"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.acceptIncomingConnect(device); + return service.acceptIncomingConnect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -724,9 +736,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean rejectIncomingConnect(BluetoothDevice device) { if (DBG) log("rejectIncomingConnect"); - if (mService != null) { + final IBluetoothHeadset service = mService; + if (service != null) { try { - return mService.rejectIncomingConnect(device); + return service.rejectIncomingConnect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -745,9 +758,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - if (mService != null && !isDisabled()) { + final IBluetoothHeadset service = mService; + if (service != null && !isDisabled()) { try { - return mService.getAudioState(device); + return service.getAudioState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -770,9 +784,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void setAudioRouteAllowed(boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.setAudioRouteAllowed(allowed); + service.setAudioRouteAllowed(allowed); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -790,9 +805,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean getAudioRouteAllowed() { if (VDBG) log("getAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(); + return service.getAudioRouteAllowed(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -812,9 +828,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void setForceScoAudio(boolean forced) { if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.setForceScoAudio(forced); + service.setForceScoAudio(forced); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -834,14 +851,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean isAudioOn() { if (VDBG) log("isAudioOn()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.isAudioOn(); + return service.isAudioOn(); } 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; } @@ -855,9 +873,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean connectAudio() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.connectAudio(); + return service.connectAudio(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -877,9 +896,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean disconnectAudio() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.disconnectAudio(); + return service.disconnectAudio(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -903,9 +923,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { if (DBG) log("startScoUsingVirtualVoiceCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startScoUsingVirtualVoiceCall(device); + return service.startScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -926,9 +947,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { if (DBG) log("stopScoUsingVirtualVoiceCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopScoUsingVirtualVoiceCall(device); + return service.stopScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -949,9 +971,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type) { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.phoneStateChanged(numActive, numHeld, callState, number, type); + service.phoneStateChanged(numActive, numHeld, callState, number, type); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -968,9 +991,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type) { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.clccResponse(index, direction, status, mode, mpty, number, type); + service.clccResponse(index, direction, status, mode, mpty, number, type); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1006,14 +1030,15 @@ public final class BluetoothHeadset implements BluetoothProfile { if (command == null) { throw new IllegalArgumentException("command is null"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendVendorSpecificResultCode(device, command, arg); + return service.sendVendorSpecificResultCode(device, command, arg); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -1027,9 +1052,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean enableWBS() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.enableWBS(); + return service.enableWBS(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1048,9 +1074,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean disableWBS() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.disableWBS(); + return service.disableWBS(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1083,9 +1110,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public void bindResponse(int indId, boolean indStatus) { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.bindResponse(indId, indStatus); + service.bindResponse(indId, indStatus); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1115,20 +1143,15 @@ public final class BluetoothHeadset implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } private boolean isDisabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_OFF; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 7ed2d2e98f7..031287f5ee1 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -76,8 +76,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Intent sent whenever audio state changes. * *

        It includes two mandatory extras: - * {@link BluetoothProfile.EXTRA_STATE}, - * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE}, + * {@link BluetoothProfile#EXTRA_STATE}, + * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}, * with possible values: * {@link #STATE_AUDIO_CONNECTING}, * {@link #STATE_AUDIO_CONNECTED}, @@ -367,7 +367,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHeadsetClient mService; + private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -478,15 +478,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } 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; } @@ -499,15 +500,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient 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())); 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; } @@ -519,15 +521,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient 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(); } @@ -541,15 +544,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient 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(); } @@ -562,15 +566,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient 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; } @@ -581,19 +586,20 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient 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; } @@ -602,15 +608,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient 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; } @@ -627,14 +634,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startVoiceRecognition(device); + return service.startVoiceRecognition(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; } @@ -651,14 +659,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopVoiceRecognition(device); + return service.stopVoiceRecognition(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; } @@ -670,14 +679,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getCurrentCalls(device); + return service.getCurrentCalls(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 null; } @@ -689,14 +699,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getCurrentAgEvents(device); + return service.getCurrentAgEvents(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 null; } @@ -711,14 +722,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.acceptCall(device, flag); + return service.acceptCall(device, flag); } 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; } @@ -731,14 +743,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.holdCall(device); + return service.holdCall(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; } @@ -755,14 +768,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.rejectCall(device); + return service.rejectCall(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; } @@ -784,14 +798,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.terminateCall(device, call); + return service.terminateCall(device, call); } 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; } @@ -811,14 +826,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.enterPrivateMode(device, index); + return service.enterPrivateMode(device, index); } 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; } @@ -837,14 +853,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.explicitCallTransfer(device); + return service.explicitCallTransfer(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; } @@ -859,14 +876,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.dial(device, number); + return service.dial(device, number); } 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 null; } @@ -882,14 +900,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendDTMF(device, code); + return service.sendDTMF(device, code); } 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; } @@ -907,14 +926,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getLastVoiceTagNumber(device); + return service.getLastVoiceTagNumber(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; } @@ -925,9 +945,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioState(device); + return service.getAudioState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -947,9 +968,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - mService.setAudioRouteAllowed(device, allowed); + service.setAudioRouteAllowed(device, allowed); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -968,9 +990,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(device); + return service.getAudioRouteAllowed(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -991,9 +1014,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.connectAudio(device); + return service.connectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1014,9 +1038,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.disconnectAudio(device); + return service.disconnectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1034,9 +1059,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getCurrentAgFeatures(device); + return service.getCurrentAgFeatures(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1048,7 +1074,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); @@ -1071,15 +1097,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index dc5f38135aa..57a019755f8 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -176,9 +176,10 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config = new BluetoothHealthAppConfiguration(name, dataType, role, channelType); - if (mService != null) { + final IBluetoothHealth service = mService; + if (service != null) { try { - result = mService.registerAppConfiguration(config, wrapper); + result = service.registerAppConfiguration(config, wrapper); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -200,9 +201,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; - if (mService != null && isEnabled() && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && config != null) { try { - result = mService.unregisterAppConfiguration(config); + result = service.unregisterAppConfiguration(config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -228,9 +230,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.connectChannelToSource(device, config); + return service.connectChannelToSource(device, config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -256,9 +259,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { - if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.connectChannelToSink(device, config, channelType); + return service.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -284,9 +288,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.disconnectChannel(device, config, channelId); + return service.disconnectChannel(device, config, channelId); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -312,9 +317,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.getMainChannelFd(device, config); + return service.getMainChannelFd(device, config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -341,9 +347,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public int getConnectionState(BluetoothDevice device) { - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getHealthDeviceConnectionState(device); + return service.getHealthDeviceConnectionState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -370,15 +377,16 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getConnectedDevices() { - if (mService != null && isEnabled()) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedHealthDevices(); + return service.getConnectedHealthDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -401,15 +409,16 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - if (mService != null && isEnabled()) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled()) { try { - return mService.getHealthDevicesMatchingConnectionStates(states); + return service.getHealthDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -455,7 +464,7 @@ public final class BluetoothHealth implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHealth mService; + private volatile IBluetoothHealth mService; BluetoothAdapter mAdapter; /** @@ -540,11 +549,8 @@ public final class BluetoothHealth 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()); } private boolean checkAppParam(String name, int role, int channelType, diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index a9a9010c7ed..32615761cf8 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -222,7 +222,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetoothInputDevice mService; + private volatile IBluetoothInputDevice mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -331,15 +331,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -370,15 +371,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -388,15 +390,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -406,15 +409,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -424,15 +428,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -453,19 +458,20 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice 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, "Stack:" + 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; } @@ -484,15 +490,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getPriority(device); + return service.getPriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.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 BluetoothProfile.PRIORITY_OFF; } @@ -517,18 +524,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } - /** * Initiate virtual unplug for a HID input device. * @@ -540,16 +542,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.virtualUnplug(device); + return service.virtualUnplug(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -565,15 +568,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getProtocolMode(device); + return service.getProtocolMode(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -588,15 +592,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setProtocolMode(device, protocolMode); + return service.setProtocolMode(device, protocolMode); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -615,19 +620,19 @@ public final class BluetoothInputDevice implements BluetoothProfile { public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { if (VDBG) { - log( - "getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId - + "bufferSize=" + bufferSize); + log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + + "bufferSize=" + bufferSize); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getReport(device, reportType, reportId, bufferSize); + return service.getReport(device, reportType, reportId, bufferSize); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -644,15 +649,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setReport(device, reportType, report); + return service.setReport(device, reportType, report); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -668,15 +674,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendData(device, report); + return service.sendData(device, report); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -691,15 +698,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getIdleTime(device); + return service.getIdleTime(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -715,15 +723,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setIdleTime(device, idleTime); + return service.setIdleTime(device, idleTime); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 15303dc7944..37f04278d46 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile { private ServiceListener mServiceListener; - private IBluetoothInputHost mService; + private volatile IBluetoothInputHost mService; private BluetoothAdapter mAdapter; @@ -202,24 +202,18 @@ public final class BluetoothInputHost implements BluetoothProfile { } }; - private ServiceConnection mConnection = new ServiceConnection() { - + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothInputHost.Stub.asInterface(service); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, BluetoothInputHost.this); } } - public void onServiceDisconnected(ComponentName className) { Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); } @@ -291,9 +285,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -311,9 +306,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -331,9 +327,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -370,13 +367,14 @@ public final class BluetoothInputHost implements BluetoothProfile { return false; } - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { BluetoothHidDeviceAppConfiguration config = new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = new BluetoothHidDeviceCallbackWrapper(callback); - result = mService.registerApp(config, sdp, inQos, outQos, cbw); + result = service.registerApp(config, sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -403,9 +401,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.unregisterApp(config); + result = service.unregisterApp(config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -427,9 +426,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.sendReport(device, id, data); + result = service.sendReport(device, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -454,9 +454,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.replyReport(device, type, id, data); + result = service.replyReport(device, type, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -479,9 +480,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.reportError(device, error); + result = service.reportError(device, error); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -502,9 +504,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.unplug(device); + result = service.unplug(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -526,9 +529,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.connect(device); + result = service.connect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -549,9 +553,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.disconnect(device); + result = service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 26a9106f49f..5b55b23680c 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -43,7 +43,7 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothMap mService; + private volatile IBluetoothMap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -161,9 +161,10 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.getState(); + return service.getState(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -182,9 +183,10 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.getClient(); + return service.getClient(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -202,9 +204,10 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.isConnected(device); + return service.isConnected(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -232,15 +235,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMap 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())); 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; } @@ -272,15 +276,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothMap 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(); } @@ -291,15 +296,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothMap 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(); } @@ -310,15 +316,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMap 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; } @@ -335,19 +342,20 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMap 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; } @@ -363,15 +371,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothMap 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; } @@ -403,13 +412,8 @@ public final class BluetoothMap implements BluetoothProfile { log("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; + private static boolean isValidDevice(BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 3e0c36548c4..af3b662d6a4 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()); } - } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 63e83d22178..866b0630835 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -123,7 +123,7 @@ public final class BluetoothPan implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetoothPan mPanService; + private volatile IBluetoothPan mPanService; /** * Create a BluetoothPan proxy object for interacting with the local @@ -238,15 +238,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mPanService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -277,15 +278,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mPanService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -295,15 +297,16 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -313,15 +316,16 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -331,25 +335,25 @@ public final class BluetoothPan implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mPanService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - mPanService.setBluetoothTethering(value); + service.setBluetoothTethering(value); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -358,10 +362,10 @@ public final class BluetoothPan implements BluetoothProfile { public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.isTetheringOn(); + return service.isTetheringOn(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -373,7 +377,6 @@ public final class BluetoothPan implements BluetoothProfile { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PAN, BluetoothPan.this); @@ -390,15 +393,11 @@ public final class BluetoothPan implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 78b7c7b7a70..19f5198ca71 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -68,7 +68,7 @@ public class BluetoothPbap { public static final String PBAP_STATE_CHANGED_ACTION = "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; - private IBluetoothPbap mService; + private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -214,9 +214,10 @@ public class BluetoothPbap { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.getState(); + return service.getState(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -235,9 +236,10 @@ public class BluetoothPbap { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.getClient(); + return service.getClient(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -255,9 +257,10 @@ public class BluetoothPbap { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.isConnected(device); + return service.isConnected(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -275,9 +278,10 @@ public class BluetoothPbap { */ public boolean disconnect() { if (DBG) log("disconnect()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - mService.disconnect(); + service.disconnect(); return true; } catch (RemoteException e) { Log.e(TAG, e.toString()); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index b9b372c8484..00a15f3f708 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -42,7 +42,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothPbapClient mService; + private volatile IBluetoothPbapClient mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -173,15 +173,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -197,16 +198,17 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception()); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - mService.disconnect(device); + service.disconnect(device); return true; } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -223,15 +225,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - if (mService != null && isEnabled()) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return new ArrayList(); @@ -247,15 +250,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - if (mService != null && isEnabled()) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return new ArrayList(); @@ -271,15 +275,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return BluetoothProfile.STATE_DISCONNECTED; @@ -321,14 +326,8 @@ public final class BluetoothPbapClient 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()); } /** @@ -339,26 +338,27 @@ public final class BluetoothPbapClient implements BluetoothProfile { * {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device - * @param priority + * @param priority Priority of this profile * @return true if priority is set, false on error */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -378,15 +378,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return PRIORITY_OFF; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index bcdf4938fe2..48481620c97 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -68,7 +68,7 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothSap mService; + private volatile IBluetoothSap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -202,9 +202,10 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.getState(); + return service.getState(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -224,9 +225,10 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.getClient(); + return service.getClient(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -246,9 +248,10 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.isConnected(device); + return service.isConnected(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -279,15 +282,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothSap 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())); 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; } @@ -299,15 +303,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothSap 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(); } @@ -319,15 +324,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothSap 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(); } @@ -339,15 +345,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothSap 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; } @@ -363,19 +370,20 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothSap 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; } @@ -388,19 +396,20 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothSap 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; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); @@ -432,15 +441,8 @@ public final class BluetoothSap 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()); } } -- GitLab From 440e81f7aa7c077400a5e8a42567ada24b20b2ac Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 17 Aug 2017 12:11:18 -0700 Subject: [PATCH 0831/1408] 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 (cherry picked from commit 1f686f645294d624f8b61e4761fd14787a496c75) --- .../android/bluetooth/BluetoothA2dpSink.java | 89 +++-- .../bluetooth/BluetoothAvrcpController.java | 59 ++-- .../android/bluetooth/BluetoothDevice.java | 334 +++++++++++------- .../android/bluetooth/BluetoothHeadset.java | 227 ++++++------ .../bluetooth/BluetoothHeadsetClient.java | 227 ++++++------ .../android/bluetooth/BluetoothHealth.java | 62 ++-- .../bluetooth/BluetoothInputDevice.java | 134 +++---- .../android/bluetooth/BluetoothInputHost.java | 71 ++-- .../java/android/bluetooth/BluetoothMap.java | 98 ++--- .../android/bluetooth/BluetoothMapClient.java | 80 +++-- .../java/android/bluetooth/BluetoothPan.java | 63 ++-- .../java/android/bluetooth/BluetoothPbap.java | 36 +- .../bluetooth/BluetoothPbapClient.java | 77 ++-- .../java/android/bluetooth/BluetoothSap.java | 101 +++--- 14 files changed, 897 insertions(+), 761 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 9dfc4b442fa..4ebef4f2182 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -125,7 +125,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothA2dpSink mService; + private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -239,16 +239,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -280,16 +280,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -298,15 +298,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -315,15 +316,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -332,16 +334,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -358,16 +360,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getAudioConfig(device); + return service.getAudioConfig(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } @@ -388,21 +390,21 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON){ + final IBluetoothA2dpSink 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, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** @@ -420,16 +422,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getPriority(device); + return service.getPriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.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 BluetoothProfile.PRIORITY_OFF; } @@ -441,16 +443,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothA2dpSink service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -483,7 +485,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, BluetoothA2dpSink.this); @@ -499,15 +500,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 0261b1b35e2..a04f110d6f4 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -20,8 +20,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.media.MediaMetadata; -import android.media.session.PlaybackState; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -83,7 +81,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothAvrcpController mService; + private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -180,15 +178,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -197,15 +196,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -214,16 +214,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -235,9 +235,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - settings = mService.getPlayerSettings(device); + settings = service.getPlayerSettings(device); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getMetadata() " + e); return null; @@ -252,15 +253,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - if (mService != null && isEnabled()) { + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - return mService.setPlayerApplicationSetting(plAppSetting); + return service.setPlayerApplicationSetting(plAppSetting); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); 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; } @@ -269,24 +271,25 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * possible keycode values: next_grp, previous_grp defined above */ public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { - Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - if (mService != null && isEnabled()) { + Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + + keyState); + final IBluetoothAvrcpController service = mService; + if (service != null && isEnabled()) { try { - mService.sendGroupNavigationCmd(device, keyCode, keyState); + service.sendGroupNavigationCmd(device, keyCode, keyState); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); return; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, BluetoothAvrcpController.this); @@ -302,15 +305,11 @@ public final class BluetoothAvrcpController implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a206b53b536..98cd319a399 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -23,10 +23,9 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.Context; import android.os.Handler; -import android.os.Looper; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -683,7 +682,7 @@ public final class BluetoothDevice implements Parcelable { * getService() called. * TODO: Unify implementation of sService amongst BluetoothFoo API's */ - private static IBluetooth sService; + private static volatile IBluetooth sService; private final String mAddress; @@ -803,13 +802,16 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public String getName() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); return null; } try { - return sService.getRemoteName(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.getRemoteName(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -822,13 +824,16 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getType() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); return DEVICE_TYPE_UNKNOWN; } try { - return sService.getRemoteType(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.getRemoteType(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return DEVICE_TYPE_UNKNOWN; } @@ -840,13 +845,16 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public String getAlias() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias"); return null; } try { - return sService.getRemoteAlias(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.getRemoteAlias(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } @@ -861,13 +869,16 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setAlias(String alias) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); return false; } try { - return sService.setRemoteAlias(this, alias); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.setRemoteAlias(this, alias); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -899,13 +910,16 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBatteryLevel() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); return BATTERY_LEVEL_UNKNOWN; } try { - return sService.getBatteryLevel(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.getBatteryLevel(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return BATTERY_LEVEL_UNKNOWN; } @@ -921,16 +935,19 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } try { - Log.i(TAG, "createBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); - return sService.createBond(this, TRANSPORT_AUTO); - } catch (RemoteException e) {Log.e(TAG, "", e);} + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); + return service.createBond(this, TRANSPORT_AUTO); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -951,7 +968,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBond(int transport) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); return false; } @@ -960,11 +978,13 @@ public final class BluetoothDevice implements Parcelable { throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); } try { - Log.i(TAG, "createBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); - return sService.createBond(this, transport); - } catch (RemoteException e) {Log.e(TAG, "", e);} + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); + return service.createBond(this, transport); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -988,17 +1008,31 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean createBondOutOfBand(int transport, OobData oobData) { + final IBluetooth service = sService; + if (service == null) { + Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); + return false; + } try { - return sService.createBondOutOfBand(this, transport, oobData); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.createBondOutOfBand(this, transport, oobData); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } /** @hide */ public boolean isBondingInitiatedLocally() { + final IBluetooth service = sService; + if (service == null) { + Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed"); + return false; + } try { - return sService.isBondingInitiatedLocally(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.isBondingInitiatedLocally(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1032,16 +1066,19 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean cancelBondProcess() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond"); return false; } try { - Log.i(TAG, "cancelBondProcess() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); - return sService.cancelBondProcess(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + Log.i(TAG, "cancelBondProcess() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); + return service.cancelBondProcess(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1056,16 +1093,19 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean removeBond() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond"); return false; } try { - Log.i(TAG, "removeBond() for device " + getAddress() + - " called by pid: " + Process.myPid() + - " tid: " + Process.myTid()); - return sService.removeBond(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + Log.i(TAG, "removeBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); + return service.removeBond(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1080,18 +1120,15 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBondState() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get bond state"); return BOND_NONE; } try { - return sService.getBondState(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} - catch (NullPointerException npe) { - // Handle case where bluetooth service proxy - // is already null. - Log.e(TAG, "NullPointerException for getBondState() of device ("+ - getAddress()+")", npe); + return service.getBondState(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); } return BOND_NONE; } @@ -1105,12 +1142,13 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi public boolean isConnected() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { // BT is not enabled, we cannot be connected. return false; } try { - return sService.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; + return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1127,12 +1165,13 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi public boolean isEncrypted() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { // BT is not enabled, we cannot be connected. return false; } try { - return sService.getConnectionState(this) > CONNECTION_STATE_CONNECTED; + return service.getConnectionState(this) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1146,12 +1185,13 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothClass getBluetoothClass() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); return null; } try { - int classInt = sService.getRemoteClass(this); + int classInt = service.getRemoteClass(this); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -1170,75 +1210,82 @@ public final class BluetoothDevice implements Parcelable { * or null on error */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public ParcelUuid[] getUuids() { - if (sService == null || isBluetoothEnabled() == false) { + public ParcelUuid[] getUuids() { + final IBluetooth service = sService; + if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); return null; } try { - return sService.getRemoteUuids(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.getRemoteUuids(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return null; } - /** - * Perform a service discovery on the remote device to get the UUIDs supported. - * - *

        This API is asynchronous and {@link #ACTION_UUID} intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently - * present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. - * - * @return False if the sanity check fails, True if the process - * of initiating an ACL connection to the remote device - * was started. - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public boolean fetchUuidsWithSdp() { - IBluetooth service = sService; - if (service == null || isBluetoothEnabled() == false) { + /** + * Perform a service discovery on the remote device to get the UUIDs supported. + * + *

        This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently + * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * if service discovery is not to be performed. + * + * @return False if the sanity check fails, True if the process of initiating an ACL connection + * to the remote device was started. + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean fetchUuidsWithSdp() { + final IBluetooth service = sService; + if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } try { return service.fetchRemoteUuids(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; } - /** - * Perform a service discovery on the remote device to get the SDP records associated - * with the specified UUID. - * - *

        This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, - * with the SDP records found on the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_SDP_RECORD} intent is sent with an status value in - * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. - * Detailed status error codes can be found by members of the Bluetooth package in - * the AbstractionLayer class. - *

        Requires {@link android.Manifest.permission#BLUETOOTH}. - * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. - * The object type will match one of the SdpXxxRecord types, depending on the UUID searched - * for. - * - * @return False if the sanity check fails, True if the process - * of initiating an ACL connection to the remote device - * was started. - */ - /** @hide */ - public boolean sdpSearch(ParcelUuid uuid) { - if (sService == null) { - Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); - return false; - } - try { - return sService.sdpSearch(this,uuid); - } catch (RemoteException e) {Log.e(TAG, "", e);} - return false; - } + /** + * Perform a service discovery on the remote device to get the SDP records associated + * with the specified UUID. + * + *

        This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, + * with the SDP records found on the remote end. If there is an error + * in getting the SDP records or if the process takes a long time, + * {@link #ACTION_SDP_RECORD} intent is sent with an status value in + * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. + * Detailed status error codes can be found by members of the Bluetooth package in + * the AbstractionLayer class. + *

        Requires {@link android.Manifest.permission#BLUETOOTH}. + * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. + * The object type will match one of the SdpXxxRecord types, depending on the UUID searched + * for. + * + * @return False if the sanity check fails, True if the process + * of initiating an ACL connection to the remote device + * was started. + */ + /** @hide */ + public boolean sdpSearch(ParcelUuid uuid) { + final IBluetooth service = sService; + if (service == null) { + Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); + return false; + } + try { + return service.sdpSearch(this, uuid); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} @@ -1248,13 +1295,16 @@ public final class BluetoothDevice implements Parcelable { * false for error */ public boolean setPin(byte[] pin) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); return false; } try { - return sService.setPin(this, true, pin.length, pin); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.setPin(this, true, pin.length, pin); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1276,13 +1326,16 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); return false; } try { - return sService.setPairingConfirmation(this, confirm); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.setPairingConfirmation(this, confirm); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1298,13 +1351,16 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ public boolean cancelPairingUserInput() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { Log.e(TAG, "BT not enabled. Cannot create pairing user input"); return false; } try { - return sService.cancelBondProcess(this); - } catch (RemoteException e) {Log.e(TAG, "", e);} + return service.cancelBondProcess(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } return false; } @@ -1334,11 +1390,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getPhonebookAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getPhonebookAccessPermission(this); + return service.getPhonebookAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1354,11 +1411,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setPhonebookAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setPhonebookAccessPermission(this, value); + return service.setPhonebookAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1372,11 +1430,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getMessageAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getMessageAccessPermission(this); + return service.getMessageAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1392,11 +1451,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setMessageAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setMessageAccessPermission(this, value); + return service.setMessageAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1410,11 +1470,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public int getSimAccessPermission() { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return ACCESS_UNKNOWN; } try { - return sService.getSimAccessPermission(this); + return service.getSimAccessPermission(this); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1430,11 +1491,12 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public boolean setSimAccessPermission(int value) { - if (sService == null) { + final IBluetooth service = sService; + if (service == null) { return false; } try { - return sService.setSimAccessPermission(this, value); + return service.setSimAccessPermission(this, value); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c84643fc46b..500cba30f9d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -298,7 +298,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHeadset mService; + private volatile IBluetoothHeadset mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -411,16 +411,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } 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; } @@ -452,16 +452,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset 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())); 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; } @@ -470,15 +470,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset 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(); } @@ -487,15 +488,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset 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(); } @@ -504,16 +506,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset 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; } @@ -534,20 +536,20 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothHeadset 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; } @@ -566,16 +568,16 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset 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; } @@ -602,15 +604,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startVoiceRecognition(device); + return service.startVoiceRecognition(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; } @@ -626,15 +628,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopVoiceRecognition(device); + return service.stopVoiceRecognition(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; } @@ -649,15 +651,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.isAudioConnected(device); + return service.isAudioConnected(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; } @@ -677,15 +679,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getBatteryUsageHint(BluetoothDevice device) { if (VDBG) log("getBatteryUsageHint()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getBatteryUsageHint(device); + return service.getBatteryUsageHint(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 -1; } @@ -708,10 +710,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean acceptIncomingConnect(BluetoothDevice device) { if (DBG) log("acceptIncomingConnect"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.acceptIncomingConnect(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.acceptIncomingConnect(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())); @@ -725,10 +730,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean rejectIncomingConnect(BluetoothDevice device) { if (DBG) log("rejectIncomingConnect"); - if (mService != null) { + final IBluetoothHeadset service = mService; + if (service != null) { try { - return mService.rejectIncomingConnect(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.rejectIncomingConnect(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())); @@ -744,10 +752,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - if (mService != null && !isDisabled()) { + final IBluetoothHeadset service = mService; + if (service != null && !isDisabled()) { try { - return mService.getAudioState(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getAudioState(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())); @@ -768,10 +779,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void setAudioRouteAllowed(boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.setAudioRouteAllowed(allowed); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + service.setAudioRouteAllowed(allowed); + } 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())); @@ -786,10 +800,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean getAudioRouteAllowed() { if (VDBG) log("getAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getAudioRouteAllowed(); + } 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())); @@ -807,9 +824,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public void setForceScoAudio(boolean forced) { if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.setForceScoAudio(forced); + service.setForceScoAudio(forced); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -830,14 +848,15 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean isAudioOn() { if (VDBG) log("isAudioOn()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.isAudioOn(); + return service.isAudioOn(); } 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; } @@ -852,9 +871,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean connectAudio() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.connectAudio(); + return service.connectAudio(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -875,9 +895,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean disconnectAudio() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.disconnectAudio(); + return service.disconnectAudio(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -901,9 +922,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { if (DBG) log("startScoUsingVirtualVoiceCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startScoUsingVirtualVoiceCall(device); + return service.startScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -924,9 +946,10 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { if (DBG) log("stopScoUsingVirtualVoiceCall()"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopScoUsingVirtualVoiceCall(device); + return service.stopScoUsingVirtualVoiceCall(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -946,10 +969,11 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public void phoneStateChanged(int numActive, int numHeld, int callState, String number, - int type) { - if (mService != null && isEnabled()) { + int type) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.phoneStateChanged(numActive, numHeld, callState, number, type); + service.phoneStateChanged(numActive, numHeld, callState, number, type); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -965,10 +989,11 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type) { - if (mService != null && isEnabled()) { + String number, int type) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.clccResponse(index, direction, status, mode, mpty, number, type); + service.clccResponse(index, direction, status, mode, mpty, number, type); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1004,15 +1029,15 @@ public final class BluetoothHeadset implements BluetoothProfile { if (command == null) { throw new IllegalArgumentException("command is null"); } - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendVendorSpecificResultCode(device, command, arg); + return service.sendVendorSpecificResultCode(device, command, arg); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -1027,9 +1052,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean enableWBS() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.enableWBS(); + return service.enableWBS(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1049,9 +1075,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean disableWBS() { - if (mService != null && isEnabled()) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - return mService.disableWBS(); + return service.disableWBS(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1078,16 +1105,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * Send Headset the BIND response from AG to report change in the status of the * HF indicators to the headset * - * @param ind_id Assigned Number of the indicator (defined by SIG) - * @param ind_status + * @param indId Assigned Number of the indicator (defined by SIG) + * @param indStatus * possible values- false-Indicator is disabled, no value changes shall be sent for this indicator * true-Indicator is enabled, value changes may be sent for this indicator * @hide */ - public void bindResponse(int ind_id, boolean ind_status) { - if (mService != null && isEnabled()) { + public void bindResponse(int indId, boolean indStatus) { + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { try { - mService.bindResponse(ind_id, ind_status); + service.bindResponse(indId, indStatus); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1116,20 +1144,15 @@ public final class BluetoothHeadset implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } private boolean isDisabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_OFF; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 544b3b95db0..73a16ded815 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -28,7 +28,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; -import java.util.UUID; /** * Public API to control Hands Free Profile (HFP role only). @@ -77,8 +76,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Intent sent whenever audio state changes. * *

        It includes two mandatory extras: - * {@link BluetoothProfile.EXTRA_STATE}, - * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE}, + * {@link BluetoothProfile#EXTRA_STATE}, + * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}, * with possible values: * {@link #STATE_AUDIO_CONNECTING}, * {@link #STATE_AUDIO_CONNECTED}, @@ -368,7 +367,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHeadsetClient mService; + private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -480,16 +479,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } 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; } @@ -504,16 +503,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient 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())); 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; } @@ -525,15 +524,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient 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(); } @@ -548,15 +548,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient 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(); } @@ -569,16 +570,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient 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; } @@ -589,20 +590,20 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothHeadsetClient 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; } @@ -611,16 +612,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient 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; } @@ -639,15 +640,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.startVoiceRecognition(device); + return service.startVoiceRecognition(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; } @@ -666,15 +667,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.stopVoiceRecognition(device); + return service.stopVoiceRecognition(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; } @@ -686,15 +687,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getCurrentCalls(device); + return service.getCurrentCalls(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 null; } @@ -707,15 +708,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getCurrentAgEvents(device); + return service.getCurrentAgEvents(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 null; } @@ -733,15 +734,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.acceptCall(device, flag); + return service.acceptCall(device, flag); } 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; } @@ -756,15 +757,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.holdCall(device); + return service.holdCall(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; } @@ -783,15 +784,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.rejectCall(device); + return service.rejectCall(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; } @@ -802,7 +803,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @param device remote device * @param call Handle of call obtained in {@link dial()} or obtained via - * {@link ACTION_CALL_CHANGED}. {@code call} may be null in which + * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which * case we will hangup all active calls. * @return true if command has been issued successfully; * false otherwise; @@ -815,15 +816,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.terminateCall(device, call); + return service.terminateCall(device, call); } 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; } @@ -845,15 +846,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.enterPrivateMode(device, index); + return service.enterPrivateMode(device, index); } 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; } @@ -874,15 +875,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.explicitCallTransfer(device); + return service.explicitCallTransfer(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; } @@ -900,15 +901,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.dial(device, number); + return service.dial(device, number); } 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 null; } @@ -925,15 +926,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendDTMF(device, code); + return service.sendDTMF(device, code); } 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; } @@ -952,15 +953,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getLastVoiceTagNumber(device); + return service.getLastVoiceTagNumber(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; } @@ -971,10 +972,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioState(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getAudioState(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())); @@ -991,10 +995,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - mService.setAudioRouteAllowed(device, allowed); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + service.setAudioRouteAllowed(device, allowed); + } 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())); @@ -1009,10 +1016,13 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getAudioRouteAllowed(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getAudioRouteAllowed(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())); @@ -1032,9 +1042,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * intent; */ public boolean connectAudio(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.connectAudio(device); + return service.connectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1057,9 +1068,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * intent; */ public boolean disconnectAudio(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.disconnectAudio(device); + return service.disconnectAudio(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1078,9 +1090,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - if (mService != null && isEnabled()) { + final IBluetoothHeadsetClient service = mService; + if (service != null && isEnabled()) { try { - return mService.getCurrentAgFeatures(device); + return service.getCurrentAgFeatures(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1092,7 +1105,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); @@ -1114,15 +1127,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 8d77888193b..07be63f01ef 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -178,9 +178,10 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthAppConfiguration config = new BluetoothHealthAppConfiguration(name, dataType, role, channelType); - if (mService != null) { + final IBluetoothHealth service = mService; + if (service != null) { try { - result = mService.registerAppConfiguration(config, wrapper); + result = service.registerAppConfiguration(config, wrapper); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -202,9 +203,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; - if (mService != null && isEnabled() && config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && config != null) { try { - result = mService.unregisterAppConfiguration(config); + result = service.unregisterAppConfiguration(config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -230,10 +232,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.connectChannelToSource(device, config); + return service.connectChannelToSource(device, config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -259,10 +261,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.connectChannelToSink(device, config, channelType); + return service.connectChannelToSink(device, config, channelType); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -288,10 +290,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.disconnectChannel(device, config, channelId); + return service.disconnectChannel(device, config, channelId); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -317,10 +319,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - if (mService != null && isEnabled() && isValidDevice(device) && - config != null) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.getMainChannelFd(device, config); + return service.getMainChannelFd(device, config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -348,9 +350,10 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public int getConnectionState(BluetoothDevice device) { - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getHealthDeviceConnectionState(device); + return service.getHealthDeviceConnectionState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -376,15 +379,16 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getConnectedDevices() { - if (mService != null && isEnabled()) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedHealthDevices(); + return service.getConnectedHealthDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -408,15 +412,16 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - if (mService != null && isEnabled()) { + final IBluetoothHealth service = mService; + if (service != null && isEnabled()) { try { - return mService.getHealthDevicesMatchingConnectionStates(states); + return service.getHealthDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -462,7 +467,7 @@ public final class BluetoothHealth implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; - private IBluetoothHealth mService; + private volatile IBluetoothHealth mService; BluetoothAdapter mAdapter; /** @@ -546,11 +551,8 @@ public final class BluetoothHealth 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()); } private boolean checkAppParam(String name, int role, int channelType, diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothInputDevice.java index a5a02435e37..07966ed1d72 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothInputDevice.java @@ -214,7 +214,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetoothInputDevice mService; + private volatile IBluetoothInputDevice mService; final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -325,15 +325,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -365,15 +366,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -382,15 +384,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled()) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -399,15 +402,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled()) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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(); } @@ -416,15 +420,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -445,19 +450,20 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothInputDevice 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, "Stack:" + 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; } @@ -476,15 +482,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getPriority(device); + return service.getPriority(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.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 BluetoothProfile.PRIORITY_OFF; } @@ -507,18 +514,13 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } - /** * Initiate virtual unplug for a HID input device. * @@ -531,16 +533,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.virtualUnplug(device); + return service.virtualUnplug(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -557,16 +560,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getProtocolMode(device); + return service.getProtocolMode(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** @@ -581,15 +585,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setProtocolMode(device, protocolMode); + return service.setProtocolMode(device, protocolMode); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -606,17 +611,22 @@ public final class BluetoothInputDevice implements BluetoothProfile { * true otherwise * @hide */ - public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { - if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); - if (mService != null && isEnabled() && isValidDevice(device)) { + public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, + int bufferSize) { + if (VDBG) { + log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + + "bufferSize=" + bufferSize); + } + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getReport(device, reportType, reportId, bufferSize); + return service.getReport(device, reportType, reportId, bufferSize); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -634,15 +644,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setReport(device, reportType, report); + return service.setReport(device, reportType, report); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -659,15 +670,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.sendData(device, report); + return service.sendData(device, report); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -683,15 +695,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.getIdleTime(device); + return service.getIdleTime(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } @@ -708,15 +721,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothInputDevice service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.setIdleTime(device, idleTime); + return service.setIdleTime(device, idleTime); } catch (RemoteException e) { Log.e(TAG, "Stack:" + 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; } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 68d105f1155..6a0506d5fb5 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -26,8 +26,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -114,7 +114,7 @@ public final class BluetoothInputHost implements BluetoothProfile { private ServiceListener mServiceListener; - private IBluetoothInputHost mService; + private volatile IBluetoothInputHost mService; private BluetoothAdapter mAdapter; @@ -195,24 +195,18 @@ public final class BluetoothInputHost implements BluetoothProfile { } }; - private ServiceConnection mConnection = new ServiceConnection() { - + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothInputHost.Stub.asInterface(service); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, BluetoothInputHost.this); } } - public void onServiceDisconnected(ComponentName className) { Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); } @@ -283,9 +277,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -302,9 +297,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -321,9 +317,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - return mService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -363,13 +360,14 @@ public final class BluetoothInputHost implements BluetoothProfile { return false; } - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { BluetoothHidDeviceAppConfiguration config = - new BluetoothHidDeviceAppConfiguration(); + new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = - new BluetoothHidDeviceCallbackWrapper(callback); - result = mService.registerApp(config, sdp, inQos, outQos, cbw); + new BluetoothHidDeviceCallbackWrapper(callback); + result = service.registerApp(config, sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -397,9 +395,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.unregisterApp(config); + result = service.unregisterApp(config); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -421,9 +420,10 @@ public final class BluetoothInputHost implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.sendReport(device, id, data); + result = service.sendReport(device, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -448,9 +448,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.replyReport(device, type, id, data); + result = service.replyReport(device, type, id, data); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -473,9 +474,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.reportError(device, error); + result = service.reportError(device, error); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -496,9 +498,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.unplug(device); + result = service.unplug(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -520,9 +523,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.connect(device); + result = service.connect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -543,9 +547,10 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - if (mService != null) { + final IBluetoothInputHost service = mService; + if (service != null) { try { - result = mService.disconnect(device); + result = service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 2e73051ee61..0f801fd067c 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,15 +16,18 @@ package android.bluetooth; -import java.util.List; -import java.util.ArrayList; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.*; +import android.os.Binder; +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 * Profile. @@ -39,7 +42,7 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothMap mService; + private volatile IBluetoothMap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -156,10 +159,13 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getState(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -175,10 +181,13 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getClient(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -193,10 +202,13 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothMap service = mService; + if (service != null) { try { - return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.isConnected(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -222,16 +234,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothMap 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())); 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; } @@ -262,15 +274,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothMap 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(); } @@ -281,15 +294,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothMap 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(); } @@ -300,16 +314,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothMap 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; } @@ -326,20 +340,20 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothMap 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; } @@ -355,16 +369,16 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothMap 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; } @@ -395,12 +409,8 @@ public final class BluetoothMap implements BluetoothProfile { log("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; + private static boolean isValidDevice(BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ccab3cdf0b6..b8fadf414e8 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,15 +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; } @@ -236,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<>(); } @@ -256,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<>(); } @@ -276,16 +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; } @@ -300,20 +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)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { + 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; } @@ -329,16 +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; } @@ -357,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; @@ -376,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; @@ -413,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()); } - } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 2a026a91e8e..123d10b5946 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -121,7 +121,7 @@ public final class BluetoothPan implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private IBluetoothPan mPanService; + private volatile IBluetoothPan mPanService; /** * Create a BluetoothPan proxy object for interacting with the local @@ -235,16 +235,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - if (mPanService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -276,16 +276,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mPanService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.disconnect(device); + return service.disconnect(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } @@ -294,15 +294,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.getConnectedDevices(); + return service.getConnectedDevices(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -311,15 +312,16 @@ public final class BluetoothPan implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.getDevicesMatchingConnectionStates(states); + return service.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); } @@ -328,25 +330,25 @@ public final class BluetoothPan implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - if (mPanService != null && isEnabled() - && isValidDevice(device)) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mPanService.getConnectionState(device); + return service.getConnectionState(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; } } - if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - mPanService.setBluetoothTethering(value); + service.setBluetoothTethering(value); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -355,10 +357,10 @@ public final class BluetoothPan implements BluetoothProfile { public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - - if (mPanService != null && isEnabled()) { + final IBluetoothPan service = mPanService; + if (service != null && isEnabled()) { try { - return mPanService.isTetheringOn(); + return service.isTetheringOn(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -370,7 +372,6 @@ public final class BluetoothPan implements BluetoothProfile { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothProfile.PAN, BluetoothPan.this); @@ -386,15 +387,11 @@ public final class BluetoothPan implements BluetoothProfile { }; private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; + return mAdapter.getState() == BluetoothAdapter.STATE_ON; } - 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()); } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index dc01fc7a949..f16160e4752 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -20,8 +20,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; /** @@ -67,7 +67,7 @@ public class BluetoothPbap { public static final String PBAP_STATE_CHANGED_ACTION = "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; - private IBluetoothPbap mService; + private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -212,10 +212,13 @@ public class BluetoothPbap { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getState(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -231,10 +234,13 @@ public class BluetoothPbap { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getClient(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -249,10 +255,13 @@ public class BluetoothPbap { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.isConnected(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -267,9 +276,10 @@ public class BluetoothPbap { */ public boolean disconnect() { if (DBG) log("disconnect()"); - if (mService != null) { + final IBluetoothPbap service = mService; + if (service != null) { try { - mService.disconnect(); + service.disconnect(); return true; } catch (RemoteException e) {Log.e(TAG, e.toString());} } else { diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 9f00e1aaa3a..28b551e5208 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,17 +16,18 @@ package android.bluetooth; -import java.util.List; -import java.util.ArrayList; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.Binder; 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 PBAP Client Profile. *@hide @@ -40,7 +41,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothPbapClient mService; + private volatile IBluetoothPbapClient mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -171,15 +172,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - return mService.connect(device); + return service.connect(device); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -196,16 +198,17 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception() ); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { try { - mService.disconnect(device); + service.disconnect(device); return true; } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; } } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -222,15 +225,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - if (mService != null && isEnabled()) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return new ArrayList(); @@ -246,15 +250,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - if (mService != null && isEnabled()) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return new ArrayList(); @@ -270,15 +275,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return BluetoothProfile.STATE_DISCONNECTED; @@ -318,14 +324,8 @@ public final class BluetoothPbapClient 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()); } /** @@ -336,27 +336,27 @@ public final class BluetoothPbapClient implements BluetoothProfile { * {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device - * @param priority + * @param priority Priority of this profile * @return true if priority is set, false on error */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return false; @@ -376,15 +376,16 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - if (mService != null && isEnabled() && isValidDevice(device)) { + final IBluetoothPbapClient 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) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return PRIORITY_OFF; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 89c1bf8f9aa..f9ddb2e7a3a 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -16,19 +16,18 @@ package android.bluetooth; -import java.util.ArrayList; -import java.util.List; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.RemoteException; import android.os.Binder; import android.os.IBinder; -import android.os.ServiceManager; +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 SIM * Access Profile (SAP). @@ -67,7 +66,7 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private IBluetoothSap mService; + private volatile IBluetoothSap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -196,10 +195,13 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.getState(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getState(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -216,10 +218,13 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.getClient(); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.getClient(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -235,10 +240,13 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - if (mService != null) { + final IBluetoothSap service = mService; + if (service != null) { try { - return mService.isConnected(device); - } catch (RemoteException e) {Log.e(TAG, e.toString());} + return service.isConnected(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } } else { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); @@ -266,16 +274,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothSap 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())); 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; } @@ -287,15 +295,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - if (mService != null && isEnabled()) { + final IBluetoothSap 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(); } @@ -307,15 +316,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - if (mService != null && isEnabled()) { + final IBluetoothSap 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(); } @@ -327,16 +337,16 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothSap 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; } @@ -352,20 +362,20 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF && - priority != BluetoothProfile.PRIORITY_ON) { - return false; + final IBluetoothSap 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; } @@ -378,20 +388,20 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - if (mService != null && isEnabled() && - isValidDevice(device)) { + final IBluetoothSap 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; } - private ServiceConnection mConnection = new ServiceConnection() { + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) log("Proxy object connected"); mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); @@ -421,13 +431,8 @@ public final class BluetoothSap 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()); } } -- GitLab From 4b7557625fe8ce11ac36dd954377361ea2b0ae2d Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Tue, 5 Sep 2017 13:41:19 -0700 Subject: [PATCH 0832/1408] Javadoc update for permission requirements Bluetooth scanning requires holding these permissions for results to be delivered. Bug: 65013767 Test: N/A Change-Id: I0b5fa9efa7fc8d5cff25319fbd7719cedee6a4aa --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 9e9c8fe71be..7106a84efda 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -137,6 +137,11 @@ public final class BluetoothLeScanner { * the PendingIntent. Use this method of scanning if your process is not always running and it * should be started when scan results are available. *

        + * An app must hold + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission + * in order to get results. + *

        * When the PendingIntent is delivered, the Intent passed to the receiver or activity * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of -- GitLab From a3787b1660861f355ab1dae3913a9e8411ad2883 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 12 Sep 2017 14:48:30 -0700 Subject: [PATCH 0833/1408] Bluetooth: fix GATT race conditions when using Handler Bug: 65596701 Test: manual Change-Id: Id703cea0543626bdd5a583da95615b650bbcc331 --- framework/java/android/bluetooth/BluetoothGatt.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 759d772920b..3ded05d1a7f 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -371,12 +371,11 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - if (status == 0) characteristic.setValue(value); - runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { + if (status == 0) characteristic.setValue(value); mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } @@ -454,12 +453,11 @@ public final class BluetoothGatt implements BluetoothProfile { handle); if (characteristic == null) return; - characteristic.setValue(value); - runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { + characteristic.setValue(value); mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } @@ -489,7 +487,6 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; - if (status == 0) descriptor.setValue(value); if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) @@ -511,6 +508,7 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public void run() { if (mCallback != null) { + if (status == 0) descriptor.setValue(value); mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } -- GitLab From 3213042e913ad2d89694442de07c71579fcfd36e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 13 Sep 2017 09:33:34 -0700 Subject: [PATCH 0834/1408] Bluetooth: thread-safe callback invocation Bug: 65596701 Test: manual Change-Id: I92a436328a3070ea842e8e652891e485406c2ed7 --- .../java/android/bluetooth/BluetoothGatt.java | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 3ded05d1a7f..83a82f7f835 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean VDBG = false; private IBluetoothGatt mService; - private BluetoothGattCallback mCallback; + private volatile BluetoothGattCallback mCallback; private Handler mHandler; private int mClientIf; private BluetoothDevice mDevice; @@ -164,8 +164,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, BluetoothProfile.STATE_DISCONNECTED); } @@ -203,8 +204,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); } } }); @@ -227,8 +229,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); } } }); @@ -254,8 +257,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionStateChange(BluetoothGatt.this, status, profileState); } } @@ -320,8 +324,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onServicesDiscovered(BluetoothGatt.this, status); } } }); @@ -374,9 +379,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { if (status == 0) characteristic.setValue(value); - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, + callback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } } @@ -428,8 +434,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); } } @@ -456,9 +463,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { characteristic.setValue(value); - mCallback.onCharacteristicChanged(BluetoothGatt.this, + callback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } } @@ -507,9 +515,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { if (status == 0) descriptor.setValue(value); - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } }); @@ -557,8 +566,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); } } }); @@ -585,8 +595,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onReliableWriteCompleted(BluetoothGatt.this, status); } } }); @@ -608,8 +619,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); } } }); @@ -632,8 +644,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onMtuChanged(BluetoothGatt.this, mtu, status); } } }); @@ -658,8 +671,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionUpdated(BluetoothGatt.this, interval, latency, timeout, status); } } -- GitLab From b94b075ef22c2f560be7afccbd433747851264b6 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 12 Sep 2017 14:48:30 -0700 Subject: [PATCH 0835/1408] Bluetooth: fix GATT race conditions when using Handler Bug: 65596701 Test: manual Change-Id: Id703cea0543626bdd5a583da95615b650bbcc331 Merged-In: Id703cea0543626bdd5a583da95615b650bbcc331 --- framework/java/android/bluetooth/BluetoothGatt.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 678159b7129..2c3aa32799b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -350,12 +350,11 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - if (status == 0) characteristic.setValue(value); - runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { + if (status == 0) characteristic.setValue(value); mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } @@ -428,12 +427,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle); if (characteristic == null) return; - characteristic.setValue(value); - runOrQueueCallback(new Runnable() { @Override public void run() { if (mCallback != null) { + characteristic.setValue(value); mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } } @@ -459,7 +457,6 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; - if (status == 0) descriptor.setValue(value); if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) @@ -481,6 +478,7 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public void run() { if (mCallback != null) { + if (status == 0) descriptor.setValue(value); mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } -- GitLab From 1e0f5c9b229e20351588fccb0a0d774e4c12dc0c Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 13 Sep 2017 09:33:34 -0700 Subject: [PATCH 0836/1408] Bluetooth: thread-safe callback invocation Bug: 65596701 Test: manual Change-Id: I92a436328a3070ea842e8e652891e485406c2ed7 Merged-In: I92a436328a3070ea842e8e652891e485406c2ed7 (cherry picked from commit 3213042e913ad2d89694442de07c71579fcfd36e) --- .../java/android/bluetooth/BluetoothGatt.java | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 2c3aa32799b..b596dd6b2c9 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean VDBG = false; private IBluetoothGatt mService; - private BluetoothGattCallback mCallback; + private volatile BluetoothGattCallback mCallback; private Handler mHandler; private int mClientIf; private BluetoothDevice mDevice; @@ -159,8 +159,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, BluetoothProfile.STATE_DISCONNECTED); } } @@ -194,8 +195,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); } } }); @@ -216,8 +218,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); } } }); @@ -241,8 +244,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionStateChange(BluetoothGatt.this, status, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionStateChange(BluetoothGatt.this, status, profileState); } } @@ -303,8 +307,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onServicesDiscovered(BluetoothGatt.this, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onServicesDiscovered(BluetoothGatt.this, status); } } }); @@ -353,9 +358,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { if (status == 0) characteristic.setValue(value); - mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, + callback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } } @@ -403,8 +409,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); } } @@ -430,9 +437,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { characteristic.setValue(value); - mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); + callback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } } }); @@ -477,9 +485,10 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { if (status == 0) descriptor.setValue(value); - mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); + callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } }); @@ -524,8 +533,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); } } }); @@ -550,8 +560,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onReliableWriteCompleted(BluetoothGatt.this, status); } } }); @@ -571,8 +582,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); } } }); @@ -593,8 +605,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onMtuChanged(BluetoothGatt.this, mtu, status); + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onMtuChanged(BluetoothGatt.this, mtu, status); } } }); @@ -617,8 +630,9 @@ public final class BluetoothGatt implements BluetoothProfile { runOrQueueCallback(new Runnable() { @Override public void run() { - if (mCallback != null) { - mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency, + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onConnectionUpdated(BluetoothGatt.this, interval, latency, timeout, status); } } -- GitLab From 21c5f72fde1b184fc9a38c949d20acbb2f746b55 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 14 Sep 2017 08:24:15 -0700 Subject: [PATCH 0837/1408] Bluetooth: fix parameter name in onConnectionUpdated Test: compilation Change-Id: I625e4ca63477b795310ebb42ca5e9a0549523cc5 --- .../java/android/bluetooth/BluetoothGattServerCallback.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 22eba351b36..2c8114be3fe 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -184,7 +184,7 @@ public abstract class BluetoothGattServerCallback { /** * Callback indicating the connection parameters were updated. * - * @param gatt The remote device involved + * @param device The remote device involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). * @param latency Slave latency for the connection in number of connection events. Valid range @@ -195,7 +195,7 @@ public abstract class BluetoothGattServerCallback { * successfully * @hide */ - public void onConnectionUpdated(BluetoothDevice gatt, int interval, int latency, int timeout, + public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, int status) { } -- GitLab From 6f08232e3f42f6444528666937876b9a2523fa36 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 14 Sep 2017 08:51:44 -0700 Subject: [PATCH 0838/1408] Bluetooth: fix typo in comment Test: none Change-Id: I48c674049df88285268145d4140870eb4ab79a62 --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 83a82f7f835..75f3f62e937 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -913,7 +913,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, whether the PHY change will happen depends on other applications peferences, + * recommendation, whether the PHY change will happen depends on other applications preferences, * local and remote controller capabilities. Controller can override these settings. *

        * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even -- GitLab From 6209c2052f3b879b118f55864660b87a36bb1f5d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 14 Sep 2017 11:54:59 -0700 Subject: [PATCH 0839/1408] Fix included service parsing (3/3) Bug: 65637368 Test: sl4a GattIncludedServiceTest Change-Id: Idb967df9d5064b0532db7f5c250f677d1dbbc54c --- framework/java/android/bluetooth/BluetoothGatt.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 75f3f62e937..a2af3422eae 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -311,8 +311,7 @@ public final class BluetoothGatt implements BluetoothProfile { for (BluetoothGattService brokenRef : includedServices) { BluetoothGattService includedService = getService(mDevice, - brokenRef.getUuid(), brokenRef.getInstanceId(), - brokenRef.getType()); + brokenRef.getUuid(), brokenRef.getInstanceId()); if (includedService != null) { fixedService.addIncludedService(includedService); } else { @@ -714,10 +713,9 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, - int instanceId, int type) { + int instanceId) { for (BluetoothGattService svc : mServices) { if (svc.getDevice().equals(device) - && svc.getType() == type && svc.getInstanceId() == instanceId && svc.getUuid().equals(uuid)) { return svc; -- GitLab From 3bff59e26dd2e7c8dfc4c4bbf83c4a623b3b72d6 Mon Sep 17 00:00:00 2001 From: Ruina Liu Date: Sat, 22 Jul 2017 18:41:48 +0800 Subject: [PATCH 0840/1408] Fix extracting 32-bit uuid error via calling method uuidToBytes A new defined method of uuidToBytes is used to convert Bluetooth uuid to bytes in the case of BLE advertising. But the most significant 16 bits of a 32-bit uuid will be cleared after the and operations with 0X0000FFFF00000000L in the function of getServiceIdentifierFromParcelUuid. 0XFFFFFFFF00000000L should be used as bit mask. Change-Id: I83e22ffbecd718540e644289fee12bf9c3b66305 Test: Advertise with payload contains 32-bit uuid --- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 5bfc54d267c..76cb3f5b548 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -232,7 +232,7 @@ public final class BluetoothUuid { */ public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); - long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; + long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32; return (int) value; } -- GitLab From 4729ea80cf268eb82091fe8f6c4a2071ca33606e Mon Sep 17 00:00:00 2001 From: Pulkit Bhuwalka Date: Wed, 16 Aug 2017 21:52:04 -0700 Subject: [PATCH 0841/1408] Modify Bluetooth Class of Device from Android stack Bug: 36015415 Test: Modified Class of Device using sample app and verified device icon change when discovering from a remote device. Change-Id: Ie25f10be5560f9c090ebe489d5f3bb00cbca81ef --- .../android/bluetooth/BluetoothAdapter.java | 23 ++++++++++ .../android/bluetooth/BluetoothClass.java | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 70591d4d058..84765f6d728 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1133,6 +1133,29 @@ public final class BluetoothAdapter { return false; } + /** + * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of + * the local Bluetooth adapter. + * + * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to. + * @return true if successful, false if unsuccessful. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setBluetoothClass(BluetoothClass bluetoothClass) { + if (getState() != STATE_ON) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.setBluetoothClass(bluetoothClass); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + /** * Get the current Bluetooth scan mode of the local Bluetooth adapter. *

        The Bluetooth scan mode determines if the local adapter is diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 57e4abb1298..f22ea6e88e0 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -19,6 +19,10 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + /** * Represents a Bluetooth class, which describes general characteristics * and capabilities of a device. For example, a Bluetooth class will @@ -275,6 +279,48 @@ public final class BluetoothClass implements Parcelable { return (mClass & Device.BITMASK); } + /** + * Return the Bluetooth Class of Device (CoD) value including the + * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and + * minor device fields. + * + *

        This value is an integer representation of Bluetooth CoD as in + * Bluetooth specification. + * + * @see https://www.bluetooth.com/specifications/assigned-numbers/baseband + * + * @hide + */ + public int getClassOfDevice() { + return mClass; + } + + /** + * Return the Bluetooth Class of Device (CoD) value including the + * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and + * minor device fields. + * + *

        This value is a byte array representation of Bluetooth CoD as in + * Bluetooth specification. + * + *

        Bluetooth COD information is 3 bytes, but stored as an int. Hence the + * MSB is useless and needs to be thrown away. The lower 3 bytes are + * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN. + * + * @see https://www.bluetooth.com/specifications/assigned-numbers/baseband + * + * @hide + */ + public byte[] getClassOfDeviceBytes() { + byte[] bytes = ByteBuffer.allocate(4) + .order(ByteOrder.BIG_ENDIAN) + .putInt(mClass) + .array(); + + // Discard the top byte + return Arrays.copyOfRange(bytes, 1, bytes.length); + } + /** @hide */ public static final int PROFILE_HEADSET = 0; /** @hide */ -- GitLab From 2dad8c7ed21d67c537f02daca83355a41bc37f52 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 29 Sep 2017 09:26:09 -0700 Subject: [PATCH 0842/1408] Fix a typo "DIGITIZER_TABLED" should be "DIGITIZER_TABLET" Test: Not needed Change-Id: I8d701655e3a32db8564aa21381febdd9f93f1bdb --- framework/java/android/bluetooth/BluetoothInputHost.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 37f04278d46..e18d9d1be51 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -74,7 +74,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** -- GitLab From 690d1ba8df712428301e0885515740987ed62dc6 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 27 Sep 2017 16:59:01 -0700 Subject: [PATCH 0843/1408] Bluetooth HID Device: Fix a typo Fixed a typo in BluetoothInputHost. "DIGITIZER_TABLED" should be "DIGITIZER_TABLET". Test: Not needed. Change-Id: I06ffc536f5912d53319b4d325f77991d65ab04f2 --- framework/java/android/bluetooth/BluetoothInputHost.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index 37f04278d46..e18d9d1be51 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -74,7 +74,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** -- GitLab From b41359755f7cff8c07356723da09b4a31aa7aa4c Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 2 Oct 2017 22:19:38 +0000 Subject: [PATCH 0844/1408] DO NOT MERGE ANYWHERE: Revert "Merge "Bluetooth HID Device: Fix a typo" am: 9e156db9e0 am: deb4ec7686 am: 45f17f4533" This reverts commit 2fb75eb22df84d14f1216178447fdaf1f15add31. Merged-In: I06ffc536f5912d53319b4d325f77991d65ab04f2 Change-Id: Ib83d5de0dbc0c98c6cf0b4c5f7381cb0f70a5da5 --- framework/java/android/bluetooth/BluetoothInputHost.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index e18d9d1be51..37f04278d46 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -74,7 +74,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** -- GitLab From 359cdcbd6bf61e799f22f310b82ee9597fde24cf Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 2 Oct 2017 22:20:13 +0000 Subject: [PATCH 0845/1408] DO NOT MERGE ANYWHERE: Revert "Merge "Bluetooth HID Device: Fix a typo" am: 9e156db9e0 am: deb4ec7686" This reverts commit 45f17f453342accf5478e0b3a2a80e9e8a4ce8e8. Merged-In: I06ffc536f5912d53319b4d325f77991d65ab04f2 Change-Id: I74271f26bbe28b02117d688a6aa0bf83f0be3780 --- framework/java/android/bluetooth/BluetoothInputHost.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothInputHost.java index e18d9d1be51..37f04278d46 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothInputHost.java @@ -74,7 +74,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** -- GitLab From 935eb4be48aa6f9aae680a86a3f4f4ef3e27b990 Mon Sep 17 00:00:00 2001 From: Jack He Date: Mon, 2 Oct 2017 19:08:30 -0700 Subject: [PATCH 0846/1408] PBAP: Use ACTION_CONNECTION_STATE_CHANGED intent * Use ACTION_CONNECTION_STATE_CHANGED to broadcast connection state change to comply with BluetoothProfile interface requirement * Use BluetoothProfile.STATE_* variables to represent connection states for PBAP profile Bug: 63873163 Test: Connect to car kits Change-Id: I7dfcfc1b3a3e4868ea5e313f62ad5e504d58b9c2 --- .../java/android/bluetooth/BluetoothPbap.java | 46 +++++++++---------- .../bluetooth/BluetoothPbapClient.java | 2 +- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 19f5198ca71..a1a9347df69 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.SdkConstant; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -53,35 +54,32 @@ public class BluetoothPbap { private static final boolean DBG = true; private static final boolean VDBG = false; - /** int extra for PBAP_STATE_CHANGED_ACTION */ - public static final String PBAP_STATE = - "android.bluetooth.pbap.intent.PBAP_STATE"; - /** int extra for PBAP_STATE_CHANGED_ACTION */ - public static final String PBAP_PREVIOUS_STATE = - "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; - /** - * Indicates the state of a pbap connection state has changed. - * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. + * Intent used to broadcast the change in connection state of the PBAP + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + *

        {@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE} + * can be any of {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING}. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ - public static final String PBAP_STATE_CHANGED_ACTION = - "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; private volatile IBluetoothPbap 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; - /** No client currently connected */ - public static final int STATE_DISCONNECTED = 0; - /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; - /** Client is currently connected */ - public static final int STATE_CONNECTED = 2; - public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; /** Connection canceled before completion. */ @@ -209,8 +207,8 @@ public class BluetoothPbap { /** * Get the current state of the BluetoothPbap service. * - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not - * connected to the Pbap service. + * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED} + * if this proxy object is currently not connected to the Pbap service. */ public int getState() { if (VDBG) log("getState()"); @@ -225,7 +223,7 @@ public class BluetoothPbap { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } - return BluetoothPbap.STATE_ERROR; + return BluetoothProfile.STATE_DISCONNECTED; } /** diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 00a15f3f708..01b3f6e0e84 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -40,7 +40,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { private static final boolean VDBG = false; public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; private volatile IBluetoothPbapClient mService; private final Context mContext; -- GitLab From d9bf8fb5edc93a9f8154943af8ac36e9447df9ca Mon Sep 17 00:00:00 2001 From: Selim Gurun Date: Tue, 17 Oct 2017 17:01:38 -0700 Subject: [PATCH 0847/1408] Add SystemApis annotations There are some number of places where bluetooth APIs are used via reflection from GMSCore. Add proper annotations. Bug: 67052734 Test: Manual - and using make update-api Change-Id: Ib6e3aa1ff5b6f9cdc78367f9be13ed00542d6f65 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 ++ framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++++++ framework/java/android/bluetooth/BluetoothHeadset.java | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 84765f6d728..3526e189662 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2277,6 +2277,8 @@ public final class BluetoothAdapter { * * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect() { if (isEnabled()) { if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d982bb7ffb4..ad7a93cd6bb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1098,6 +1098,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess() { final IBluetooth service = sService; if (service == null) { @@ -1125,6 +1127,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond() { final IBluetooth service = sService; if (service == null) { @@ -1174,6 +1178,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected() { final IBluetooth service = sService; if (service == null) { @@ -1197,6 +1202,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted() { final IBluetooth service = sService; if (service == null) { @@ -1444,6 +1450,8 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 85550c7720a..1241f2306c7 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -416,6 +418,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -456,6 +460,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -543,6 +549,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); final IBluetoothHeadset service = mService; -- GitLab From dba34c5da02e483e3e98c9a202497d060f463cf0 Mon Sep 17 00:00:00 2001 From: Pulkit Bhuwalka Date: Mon, 25 Sep 2017 17:55:12 -0700 Subject: [PATCH 0848/1408] Persist Bluetooth CoD config value across reboot Create new Settings key for storage and update the API doc. Bug: 36015415 Test: Verified by storing a value through a test app and restarting the machine to ensure it's picked up. Change-Id: I94aa054e525c4656bb3a824a29cae9c88f8904e0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 84765f6d728..5c26232726b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1134,8 +1134,10 @@ public final class BluetoothAdapter { } /** - * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of - * the local Bluetooth adapter. + * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth + * adapter. + * + *

        Note: This value persists across system reboot. * * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to. * @return true if successful, false if unsuccessful. -- GitLab From 9279eda246a7843cef6d4c0b49685bb781da4fd6 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 20 Oct 2017 15:55:59 -0700 Subject: [PATCH 0849/1408] Change Bluetooth HID Profile Name (1/11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the Bluetooth HID profile name consistent with the Bluetooth HID service name. BluetoothInputHost → BluetoothHidDevice BluetoothInputDevice → BluetoothHidHost IBluetoothInputHost → IBluetoothHidDevice IBluetoothInputDevice → IBluetoothHidHost BluetoothProfile.INPUT_HOST → BluetoothProfile.HID_DEVICE BluetoothProfile.INPUT_DEVICE → BluetoothProfile.HID_HOST Bug: 68055651 Test: make Change-Id: Iadb890a54dd3d6868b87514472bbac6bb0c6179f --- .../android/bluetooth/BluetoothAdapter.java | 18 ++--- ...InputHost.java => BluetoothHidDevice.java} | 44 ++++++------ ...InputDevice.java => BluetoothHidHost.java} | 70 +++++++++---------- .../android/bluetooth/BluetoothProfile.java | 8 +-- .../bluetooth/BluetoothStressTest.java | 6 +- .../android/bluetooth/BluetoothTestUtils.java | 34 ++++----- 6 files changed, 90 insertions(+), 90 deletions(-) rename framework/java/android/bluetooth/{BluetoothInputHost.java => BluetoothHidDevice.java} (94%) rename framework/java/android/bluetooth/{BluetoothInputDevice.java => BluetoothHidHost.java} (91%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3526e189662..c450f8aab06 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2104,8 +2104,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); + } else if (profile == BluetoothProfile.HID_HOST) { + BluetoothHidHost iDev = new BluetoothHidHost(context, listener); return true; } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); @@ -2128,8 +2128,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_HOST) { - BluetoothInputHost iHost = new BluetoothInputHost(context, listener); + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; } else { return false; @@ -2167,8 +2167,8 @@ public final class BluetoothAdapter { BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy; avrcp.close(); break; - case BluetoothProfile.INPUT_DEVICE: - BluetoothInputDevice iDev = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + BluetoothHidHost iDev = (BluetoothHidHost) proxy; iDev.close(); break; case BluetoothProfile.PAN: @@ -2207,9 +2207,9 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient) proxy; mapClient.close(); break; - case BluetoothProfile.INPUT_HOST: - BluetoothInputHost iHost = (BluetoothInputHost) proxy; - iHost.close(); + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; + hidDevice.close(); break; } } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothHidDevice.java similarity index 94% rename from framework/java/android/bluetooth/BluetoothInputHost.java rename to framework/java/android/bluetooth/BluetoothHidDevice.java index e18d9d1be51..179f36dab32 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -33,9 +33,9 @@ import java.util.List; /** * @hide */ -public final class BluetoothInputHost implements BluetoothProfile { +public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothInputHost.class.getSimpleName(); + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** * Intent used to broadcast the change in connection state of the Input @@ -57,7 +57,7 @@ public final class BluetoothInputHost implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; /** * Constants representing device subclass. @@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile { private ServiceListener mServiceListener; - private volatile IBluetoothInputHost mService; + private volatile IBluetoothHidDevice mService; private BluetoothAdapter mAdapter; @@ -205,23 +205,23 @@ public final class BluetoothInputHost implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothInputHost.Stub.asInterface(service); + mService = IBluetoothHidDevice.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, - BluetoothInputHost.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); } } public void onServiceDisconnected(ComponentName className) { Log.d(TAG, "onServiceDisconnected()"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); } } }; - BluetoothInputHost(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothInputHost"); + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); mContext = context; mServiceListener = listener; @@ -240,7 +240,7 @@ public final class BluetoothInputHost implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputHost.class.getName()); + Intent intent = new Intent(IBluetoothHidDevice.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -285,7 +285,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectedDevices(); @@ -306,7 +306,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -327,7 +327,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectionState(device); @@ -367,7 +367,7 @@ public final class BluetoothInputHost implements BluetoothProfile { return false; } - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { BluetoothHidDeviceAppConfiguration config = @@ -401,7 +401,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unregisterApp(config); @@ -426,7 +426,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.sendReport(device, id, data); @@ -454,7 +454,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -480,7 +480,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.reportError(device, error); @@ -504,7 +504,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unplug(device); @@ -529,7 +529,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.connect(device); @@ -553,7 +553,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.disconnect(device); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothHidHost.java similarity index 91% rename from framework/java/android/bluetooth/BluetoothInputDevice.java rename to framework/java/android/bluetooth/BluetoothHidHost.java index 32615761cf8..8ad0f9d064f 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -35,16 +35,16 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth Input * Device Profile. * - *

        BluetoothInputDevice is a proxy object for controlling the Bluetooth + *

        BluetoothHidHost is a proxy object for controlling the Bluetooth * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothInputDevice proxy object. + * the BluetoothHidHost proxy object. * *

        Each method is protected with its appropriate permission. * * @hide */ -public final class BluetoothInputDevice implements BluetoothProfile { - private static final String TAG = "BluetoothInputDevice"; +public final class BluetoothHidHost implements BluetoothProfile { + private static final String TAG = "BluetoothHidHost"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -177,52 +177,52 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public static final String EXTRA_PROTOCOL_MODE = - "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; + "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE"; /** * @hide */ public static final String EXTRA_REPORT_TYPE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE"; /** * @hide */ public static final String EXTRA_REPORT_ID = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_ID"; /** * @hide */ public static final String EXTRA_REPORT_BUFFER_SIZE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE"; /** * @hide */ - public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT"; + public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT"; /** * @hide */ - public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS"; + public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS"; /** * @hide */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS"; /** * @hide */ public static final String EXTRA_IDLE_TIME = - "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothInputDevice mService; + private volatile IBluetoothHidHost mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -254,10 +254,10 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; /** - * Create a BluetoothInputDevice proxy object for interacting with the local + * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothInputDevice(Context context, ServiceListener l) { + /*package*/ BluetoothHidHost(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -275,7 +275,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputDevice.class.getName()); + Intent intent = new Intent(IBluetoothHidHost.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -331,7 +331,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -371,7 +371,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -390,7 +390,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -409,7 +409,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -428,7 +428,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -458,7 +458,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -490,7 +490,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,11 +506,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); + mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, - BluetoothInputDevice.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, + BluetoothHidHost.this); } } @@ -518,7 +518,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); } } }; @@ -542,7 +542,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -568,7 +568,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -592,7 +592,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -623,7 +623,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -649,7 +649,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -674,7 +674,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -698,7 +698,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -723,7 +723,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index bc8fa846087..46a230b5060 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -73,11 +73,11 @@ public interface BluetoothProfile { public static final int HEALTH = 3; /** - * Input Device Profile + * HID Host * * @hide */ - public static final int INPUT_DEVICE = 4; + public static final int HID_HOST = 4; /** * PAN Profile @@ -152,11 +152,11 @@ public interface BluetoothProfile { public static final int MAP_CLIENT = 18; /** - * Input Host + * HID Device * * @hide */ - public static final int INPUT_HOST = 19; + public static final int HID_DEVICE = 19; /** * Max profile ID. This value should be updated whenever a new profile is added to match diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java index 31ce95eea1d..4b32ceae061 100644 --- a/framework/tests/src/android/bluetooth/BluetoothStressTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -256,13 +256,13 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.unpair(mAdapter, device); mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("connectInput(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("disconnectInput(device=%s)", device)); } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index ee159788ad2..ada03666b7b 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -227,8 +227,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; break; - case BluetoothProfile.INPUT_DEVICE: - mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED; + case BluetoothProfile.HID_HOST: + mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED; break; case BluetoothProfile.PAN: mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; @@ -322,8 +322,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = (BluetoothHeadset) proxy; break; - case BluetoothProfile.INPUT_DEVICE: - mInput = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + mInput = (BluetoothHidHost) proxy; break; case BluetoothProfile.PAN: mPan = (BluetoothPan) proxy; @@ -342,7 +342,7 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = null; break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: mInput = null; break; case BluetoothProfile.PAN: @@ -362,7 +362,7 @@ public class BluetoothTestUtils extends Assert { private Context mContext; private BluetoothA2dp mA2dp = null; private BluetoothHeadset mHeadset = null; - private BluetoothInputDevice mInput = null; + private BluetoothHidHost mInput = null; private BluetoothPan mPan = null; /** @@ -894,7 +894,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -935,8 +935,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).connect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).connect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).connect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).connect(device)); } break; default: @@ -975,7 +975,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -1010,8 +1010,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).disconnect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).disconnect(device)); } break; case BluetoothProfile.STATE_DISCONNECTED: @@ -1237,7 +1237,7 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { state = mPan.getConnectionState(device); - if (state == BluetoothInputDevice.STATE_DISCONNECTED + if (state == BluetoothHidHost.STATE_DISCONNECTED && (receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { @@ -1255,7 +1255,7 @@ public class BluetoothTestUtils extends Assert { int firedFlags = receiver.getFiredFlags(); removeReceiver(receiver); fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, state, BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask)); + methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask)); } /** @@ -1404,7 +1404,7 @@ public class BluetoothTestUtils extends Assert { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); @@ -1443,7 +1443,7 @@ public class BluetoothTestUtils extends Assert { return mHeadset; } break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: if (mInput != null) { return mInput; } @@ -1469,7 +1469,7 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mHeadset; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } -- GitLab From f0761580c81033f1004f31a7844ff86741134dea Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 20 Oct 2017 15:55:59 -0700 Subject: [PATCH 0850/1408] Change Bluetooth HID Profile Name (1/6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the Bluetooth HID profile name consistent with the Bluetooth HID service name. BluetoothInputHost → BluetoothHidDevice BluetoothInputDevice → BluetoothHidHost IBluetoothInputHost → IBluetoothHidDevice IBluetoothInputDevice → IBluetoothHidHost BluetoothProfile.INPUT_HOST → BluetoothProfile.HID_DEVICE BluetoothProfile.INPUT_DEVICE → BluetoothProfile.HID_HOST (Cherry-picked from commit c26c76c63d933f8057f795d05624f91b811c8c71) Merged-In: Iadb890a54dd3d6868b87514472bbac6bb0c6179f Bug: 68055651 Test: make Change-Id: Iadb890a54dd3d6868b87514472bbac6bb0c6179f --- .../android/bluetooth/BluetoothAdapter.java | 18 ++--- ...InputHost.java => BluetoothHidDevice.java} | 44 ++++++------ ...InputDevice.java => BluetoothHidHost.java} | 70 +++++++++---------- .../android/bluetooth/BluetoothProfile.java | 8 +-- .../bluetooth/BluetoothStressTest.java | 6 +- .../android/bluetooth/BluetoothTestUtils.java | 34 ++++----- 6 files changed, 90 insertions(+), 90 deletions(-) rename framework/java/android/bluetooth/{BluetoothInputHost.java => BluetoothHidDevice.java} (94%) rename framework/java/android/bluetooth/{BluetoothInputDevice.java => BluetoothHidHost.java} (91%) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 84765f6d728..17ae9078ef9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2104,8 +2104,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - BluetoothInputDevice iDev = new BluetoothInputDevice(context, listener); + } else if (profile == BluetoothProfile.HID_HOST) { + BluetoothHidHost iDev = new BluetoothHidHost(context, listener); return true; } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); @@ -2128,8 +2128,8 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_HOST) { - BluetoothInputHost iHost = new BluetoothInputHost(context, listener); + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; } else { return false; @@ -2167,8 +2167,8 @@ public final class BluetoothAdapter { BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy; avrcp.close(); break; - case BluetoothProfile.INPUT_DEVICE: - BluetoothInputDevice iDev = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + BluetoothHidHost iDev = (BluetoothHidHost) proxy; iDev.close(); break; case BluetoothProfile.PAN: @@ -2207,9 +2207,9 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient) proxy; mapClient.close(); break; - case BluetoothProfile.INPUT_HOST: - BluetoothInputHost iHost = (BluetoothInputHost) proxy; - iHost.close(); + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; + hidDevice.close(); break; } } diff --git a/framework/java/android/bluetooth/BluetoothInputHost.java b/framework/java/android/bluetooth/BluetoothHidDevice.java similarity index 94% rename from framework/java/android/bluetooth/BluetoothInputHost.java rename to framework/java/android/bluetooth/BluetoothHidDevice.java index e18d9d1be51..179f36dab32 100644 --- a/framework/java/android/bluetooth/BluetoothInputHost.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -33,9 +33,9 @@ import java.util.List; /** * @hide */ -public final class BluetoothInputHost implements BluetoothProfile { +public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothInputHost.class.getSimpleName(); + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** * Intent used to broadcast the change in connection state of the Input @@ -57,7 +57,7 @@ public final class BluetoothInputHost implements BluetoothProfile { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; /** * Constants representing device subclass. @@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile { private ServiceListener mServiceListener; - private volatile IBluetoothInputHost mService; + private volatile IBluetoothHidDevice mService; private BluetoothAdapter mAdapter; @@ -205,23 +205,23 @@ public final class BluetoothInputHost implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothInputHost.Stub.asInterface(service); + mService = IBluetoothHidDevice.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST, - BluetoothInputHost.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); } } public void onServiceDisconnected(ComponentName className) { Log.d(TAG, "onServiceDisconnected()"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); } } }; - BluetoothInputHost(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothInputHost"); + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); mContext = context; mServiceListener = listener; @@ -240,7 +240,7 @@ public final class BluetoothInputHost implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputHost.class.getName()); + Intent intent = new Intent(IBluetoothHidDevice.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -285,7 +285,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectedDevices(); @@ -306,7 +306,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -327,7 +327,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { return service.getConnectionState(device); @@ -367,7 +367,7 @@ public final class BluetoothInputHost implements BluetoothProfile { return false; } - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { BluetoothHidDeviceAppConfiguration config = @@ -401,7 +401,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unregisterApp(config); @@ -426,7 +426,7 @@ public final class BluetoothInputHost implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.sendReport(device, id, data); @@ -454,7 +454,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -480,7 +480,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.reportError(device, error); @@ -504,7 +504,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.unplug(device); @@ -529,7 +529,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.connect(device); @@ -553,7 +553,7 @@ public final class BluetoothInputHost implements BluetoothProfile { boolean result = false; - final IBluetoothInputHost service = mService; + final IBluetoothHidDevice service = mService; if (service != null) { try { result = service.disconnect(device); diff --git a/framework/java/android/bluetooth/BluetoothInputDevice.java b/framework/java/android/bluetooth/BluetoothHidHost.java similarity index 91% rename from framework/java/android/bluetooth/BluetoothInputDevice.java rename to framework/java/android/bluetooth/BluetoothHidHost.java index 32615761cf8..8ad0f9d064f 100644 --- a/framework/java/android/bluetooth/BluetoothInputDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -35,16 +35,16 @@ import java.util.List; * This class provides the public APIs to control the Bluetooth Input * Device Profile. * - *

        BluetoothInputDevice is a proxy object for controlling the Bluetooth + *

        BluetoothHidHost is a proxy object for controlling the Bluetooth * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothInputDevice proxy object. + * the BluetoothHidHost proxy object. * *

        Each method is protected with its appropriate permission. * * @hide */ -public final class BluetoothInputDevice implements BluetoothProfile { - private static final String TAG = "BluetoothInputDevice"; +public final class BluetoothHidHost implements BluetoothProfile { + private static final String TAG = "BluetoothHidHost"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -177,52 +177,52 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public static final String EXTRA_PROTOCOL_MODE = - "android.bluetooth.BluetoothInputDevice.extra.PROTOCOL_MODE"; + "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE"; /** * @hide */ public static final String EXTRA_REPORT_TYPE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_TYPE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE"; /** * @hide */ public static final String EXTRA_REPORT_ID = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_ID"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_ID"; /** * @hide */ public static final String EXTRA_REPORT_BUFFER_SIZE = - "android.bluetooth.BluetoothInputDevice.extra.REPORT_BUFFER_SIZE"; + "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE"; /** * @hide */ - public static final String EXTRA_REPORT = "android.bluetooth.BluetoothInputDevice.extra.REPORT"; + public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT"; /** * @hide */ - public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS"; + public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS"; /** * @hide */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS"; /** * @hide */ public static final String EXTRA_IDLE_TIME = - "android.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothInputDevice mService; + private volatile IBluetoothHidHost mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -254,10 +254,10 @@ public final class BluetoothInputDevice implements BluetoothProfile { }; /** - * Create a BluetoothInputDevice proxy object for interacting with the local + * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothInputDevice(Context context, ServiceListener l) { + /*package*/ BluetoothHidHost(Context context, ServiceListener l) { mContext = context; mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -275,7 +275,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } boolean doBind() { - Intent intent = new Intent(IBluetoothInputDevice.class.getName()); + Intent intent = new Intent(IBluetoothHidHost.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, @@ -331,7 +331,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -371,7 +371,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -390,7 +390,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -409,7 +409,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -428,7 +428,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -458,7 +458,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -490,7 +490,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,11 +506,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothInputDevice.Stub.asInterface(Binder.allowBlocking(service)); + mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, - BluetoothInputDevice.this); + mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, + BluetoothHidHost.this); } } @@ -518,7 +518,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE); + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); } } }; @@ -542,7 +542,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -568,7 +568,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -592,7 +592,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -623,7 +623,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -649,7 +649,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -674,7 +674,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -698,7 +698,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -723,7 +723,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothInputDevice service = mService; + final IBluetoothHidHost service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index bc8fa846087..46a230b5060 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -73,11 +73,11 @@ public interface BluetoothProfile { public static final int HEALTH = 3; /** - * Input Device Profile + * HID Host * * @hide */ - public static final int INPUT_DEVICE = 4; + public static final int HID_HOST = 4; /** * PAN Profile @@ -152,11 +152,11 @@ public interface BluetoothProfile { public static final int MAP_CLIENT = 18; /** - * Input Host + * HID Device * * @hide */ - public static final int INPUT_HOST = 19; + public static final int HID_DEVICE = 19; /** * Max profile ID. This value should be updated whenever a new profile is added to match diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java index 31ce95eea1d..4b32ceae061 100644 --- a/framework/tests/src/android/bluetooth/BluetoothStressTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -256,13 +256,13 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.unpair(mAdapter, device); mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, null); + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null); for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("connectInput(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.INPUT_DEVICE, + mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, String.format("disconnectInput(device=%s)", device)); } diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index ee159788ad2..ada03666b7b 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -227,8 +227,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; break; - case BluetoothProfile.INPUT_DEVICE: - mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED; + case BluetoothProfile.HID_HOST: + mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED; break; case BluetoothProfile.PAN: mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; @@ -322,8 +322,8 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = (BluetoothHeadset) proxy; break; - case BluetoothProfile.INPUT_DEVICE: - mInput = (BluetoothInputDevice) proxy; + case BluetoothProfile.HID_HOST: + mInput = (BluetoothHidHost) proxy; break; case BluetoothProfile.PAN: mPan = (BluetoothPan) proxy; @@ -342,7 +342,7 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.HEADSET: mHeadset = null; break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: mInput = null; break; case BluetoothProfile.PAN: @@ -362,7 +362,7 @@ public class BluetoothTestUtils extends Assert { private Context mContext; private BluetoothA2dp mA2dp = null; private BluetoothHeadset mHeadset = null; - private BluetoothInputDevice mInput = null; + private BluetoothHidHost mInput = null; private BluetoothPan mPan = null; /** @@ -894,7 +894,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -935,8 +935,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).connect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).connect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).connect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).connect(device)); } break; default: @@ -975,7 +975,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#INPUT_DEVICE}. + * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -1010,8 +1010,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothA2dp)proxy).disconnect(device)); } else if (profile == BluetoothProfile.HEADSET) { assertTrue(((BluetoothHeadset)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.INPUT_DEVICE) { - assertTrue(((BluetoothInputDevice)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.HID_HOST) { + assertTrue(((BluetoothHidHost)proxy).disconnect(device)); } break; case BluetoothProfile.STATE_DISCONNECTED: @@ -1237,7 +1237,7 @@ public class BluetoothTestUtils extends Assert { long s = System.currentTimeMillis(); while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { state = mPan.getConnectionState(device); - if (state == BluetoothInputDevice.STATE_DISCONNECTED + if (state == BluetoothHidHost.STATE_DISCONNECTED && (receiver.getFiredFlags() & mask) == mask) { long finish = receiver.getCompletedTime(); if (start != -1 && finish != -1) { @@ -1255,7 +1255,7 @@ public class BluetoothTestUtils extends Assert { int firedFlags = receiver.getFiredFlags(); removeReceiver(receiver); fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, state, BluetoothInputDevice.STATE_DISCONNECTED, firedFlags, mask)); + methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask)); } /** @@ -1404,7 +1404,7 @@ public class BluetoothTestUtils extends Assert { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); @@ -1443,7 +1443,7 @@ public class BluetoothTestUtils extends Assert { return mHeadset; } break; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: if (mInput != null) { return mInput; } @@ -1469,7 +1469,7 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mHeadset; - case BluetoothProfile.INPUT_DEVICE: + case BluetoothProfile.HID_HOST: while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { sleep(POLL_TIME); } -- GitLab From 02e360e17e9cf32afbbe7fe3257f45d186136808 Mon Sep 17 00:00:00 2001 From: Pulkit Bhuwalka Date: Wed, 20 Sep 2017 15:10:45 -0700 Subject: [PATCH 0851/1408] Get Bluetooth Class of Device Adds API to fetch Bluetooth CoD value of the stack. The API is hidden to only be used by System code. Bug: 36015415 Test: Verified fetching of COD from test app on flashed device, after modification and after after reboot. Change-Id: Ie35ecf141704c2aac46678da7cabdc7203a088f2 --- .../android/bluetooth/BluetoothAdapter.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 01af9fe31d2..3d9651db683 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1133,6 +1133,28 @@ public final class BluetoothAdapter { return false; } + /** + * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth + * adapter. + * + * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public BluetoothClass getBluetoothClass() { + if (getState() != STATE_ON) return null; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getBluetoothClass(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return null; + } + /** * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth * adapter. -- GitLab From 3bb4e188251756e19c986edccf9e6fbebbf220d7 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 27 Sep 2017 14:17:20 -0700 Subject: [PATCH 0852/1408] Enable HID Device Profile Service (2/2) Add Profile Proxy for HID Device Profile. * Add a helper method doUnbind() to deal with unbinding to Bluetooth HID Service. * Fix docstrings. Bug: 63384609 Test: SL4A Bluetooth HID test Change-Id: I168c21c3b010baac9889c556635b914c0ba3f267 --- .../android/bluetooth/BluetoothHidDevice.java | 71 ++++++++++--------- .../BluetoothHidDeviceAppConfiguration.java | 11 ++- .../BluetoothHidDeviceAppQosSettings.java | 24 +++++-- .../BluetoothHidDeviceAppSdpSettings.java | 21 +++++- .../bluetooth/BluetoothHidDeviceCallback.java | 18 +++-- 5 files changed, 97 insertions(+), 48 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 179f36dab32..e3d763ab9b1 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -31,7 +31,14 @@ import java.util.Arrays; import java.util.List; /** - * @hide + * Provides the public APIs to control the Bluetooth HID Device + * profile. + * + * BluetoothHidDevice is a proxy object for controlling the Bluetooth HID + * Device Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHidDevice proxy object. + * + * {@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { @@ -62,7 +69,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Constants representing device subclass. * - * @see #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback) + * @see #registerApp + * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; @@ -80,9 +89,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Constants representing report types. * - * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int) - * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) - * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[]) + * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int) + * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see BluetoothHidDeviceCallback#onIntrData(BluetoothDevice, byte, byte[]) */ public static final byte REPORT_TYPE_INPUT = (byte) 1; public static final byte REPORT_TYPE_OUTPUT = (byte) 2; @@ -91,7 +100,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Constants representing error response for Set Report. * - * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) */ public static final byte ERROR_RSP_SUCCESS = (byte) 0; public static final byte ERROR_RSP_NOT_READY = (byte) 1; @@ -104,7 +113,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Constants representing protocol mode used set by host. Default is always * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. * - * @see BluetoothHidDeviceCallback#onSetProtocol(byte) + * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte) */ public static final byte PROTOCOL_BOOT_MODE = (byte) 0; public static final byte PROTOCOL_REPORT_MODE = (byte) 1; @@ -169,18 +178,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { Log.d(TAG, "onBluetoothStateChange: up=" + up); synchronized (mConnection) { - if (!up) { - Log.d(TAG, "Unbinding service..."); - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "onBluetoothStateChange: could not unbind service:", - e); - } - } - } else { + if (up) { try { if (mService == null) { Log.d(TAG, "Binding HID Device service..."); @@ -189,14 +187,15 @@ public final class BluetoothHidDevice implements BluetoothProfile { } catch (IllegalStateException e) { Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", - e); + + "service: ", e); } catch (SecurityException e) { Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", - e); + + "service: ", e); } + } else { + Log.d(TAG, "Unbinding service..."); + doUnbind(); } } } @@ -252,6 +251,18 @@ public final class BluetoothHidDevice implements BluetoothProfile { return true; } + void doUnbind() { + Log.d(TAG, "Unbinding HidDevService"); + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Unable to unbind HidDevService", e); + } + } + } + void close() { Log.v(TAG, "close()"); @@ -265,16 +276,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } synchronized (mConnection) { - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "close: could not unbind HID Dev service: ", e); - } - } + doUnbind(); } - mServiceListener = null; } @@ -388,7 +391,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Unregisters application. Active connection will be disconnected and no * new connections will be allowed until registered again using - * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)} + * {@link #registerApp + * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} * * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java index 2731935a4ea..d1efa2d6fca 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java @@ -21,7 +21,16 @@ import android.os.Parcelable; import java.util.Random; -/** @hide */ +/** + * Represents the app configuration for a Bluetooth HID Device application. + * + * The app needs a BluetoothHidDeviceAppConfiguration token to unregister + * the Bluetooth HID Device service. + * + * {@see BluetoothHidDevice} + * + * {@hide} + */ public final class BluetoothHidDeviceAppConfiguration implements Parcelable { private final long mHash; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 1f80ed78d75..ccc3ef4008f 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -19,7 +19,17 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -/** @hide */ +/** + * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device + * application. + * + * The BluetoothHidDevice framework will update the L2CAP QoS settings for the + * app during registration. + * + * {@see BluetoothHidDevice} + * + * {@hide} + */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public final int serviceType; @@ -36,8 +46,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public static final int MAX = (int) 0xffffffff; public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, - int peakBandwidth, - int latency, int delayVariation) { + int peakBandwidth, int latency, int delayVariation) { this.serviceType = serviceType; this.tokenRate = tokenRate; this.tokenBucketSize = tokenBucketSize; @@ -66,10 +75,13 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @Override public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { - return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), + return new BluetoothHidDeviceAppQosSettings( + in.readInt(), + in.readInt(), + in.readInt(), in.readInt(), in.readInt(), - in.readInt(), in.readInt()); + in.readInt()); } @Override @@ -90,7 +102,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** @return an int array representation of this instance */ public int[] toArray() { - return new int[]{ + return new int[] { serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation }; } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index d21d50611ab..f01c4932896 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -19,7 +19,18 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -/** @hide */ +/** + * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth + * HID Device application. + * + * The BluetoothHidDevice framework adds the SDP record during app + * registration, so that the Android device can be discovered as a Bluetooth + * HID Device. + * + * {@see BluetoothHidDevice} + * + * {@hide} + */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { public final String name; @@ -57,8 +68,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @Override public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { - return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), - in.readString(), in.readByte(), in.createByteArray()); + return new BluetoothHidDeviceAppSdpSettings( + in.readString(), + in.readString(), + in.readString(), + in.readByte(), + in.createByteArray()); } @Override diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 3d407a6ca73..5ccda0dc7dd 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -18,16 +18,24 @@ package android.bluetooth; import android.util.Log; -/** @hide */ +/** + * The template class that applications use to call callback functions on + * events from the HID host. Callback functions are wrapped in this class and + * registered to the Android system during app registration. + * + * {@see BluetoothHidDevice} + * + * {@hide} + */ public abstract class BluetoothHidDeviceCallback { - private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName(); + private static final String TAG = "BluetoothHidDevCallback"; /** * Callback called when application registration state changes. Usually it's * called due to either - * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[], - * BluetoothHidDeviceCallback)} + * {@link BluetoothHidDevice#registerApp + * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} * or * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} * , but can be also unsolicited in case e.g. Bluetooth was turned off in @@ -79,7 +87,7 @@ public abstract class BluetoothHidDeviceCallback { /** * Callback called when SET_REPORT is received from remote host. In case * received data are invalid, application shall respond with - * {@link BluetoothHidDevice#reportError(BluetoothDevice)}. + * {@link BluetoothHidDevice#reportError(BluetoothDevice, byte)}. * * @param type Report Type. * @param id Report Id. -- GitLab From 17fde5cd88ca34d7e67e0646a242e3ba5fa01104 Mon Sep 17 00:00:00 2001 From: Andrew Solovay Date: Fri, 27 Oct 2017 15:11:48 -0700 Subject: [PATCH 0853/1408] docs: Typo fixes See first comment for doc stage location. Change-Id: I29661ffab0ff10021671df0b725c9f9b12430960 Bug: 67886596 Bug: 67932414 Test: make ds-docs --- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 1cc2270be56..6609907ec3c 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -119,7 +119,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public static final int WRITE_TYPE_DEFAULT = 0x02; /** - * Wrtite characteristic without requiring a response by the remote device + * Write characteristic without requiring a response by the remote device */ public static final int WRITE_TYPE_NO_RESPONSE = 0x01; -- GitLab From f831af96f0d4dfa8041e0d74a4b88911aac7eddf Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 6 Nov 2017 12:16:25 -0800 Subject: [PATCH 0854/1408] Move createSocketChannel into IBluetoothSocketManager (3/3) Bug: 68359837 Test: none Change-Id: I52b03ff3d637bf661c70279b7ca18b105157f7a2 --- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 4035ee1beb8..23956e14d9e 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -417,7 +417,7 @@ public final class BluetoothSocket implements Closeable { return -1; } try { - mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName, + mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName, mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); -- GitLab From eb4cb5c58285173f7b05081ee445c2d603da3511 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 6 Nov 2017 12:17:30 -0800 Subject: [PATCH 0855/1408] Move connectSocket into IBluetoothSocketManager (3/3) Bug: 68359837 Test: none Change-Id: I1161a5fe74b034fba0112fd3a78bdf1fbace6e12 --- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 23956e14d9e..0569913435a 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -375,7 +375,7 @@ public final class BluetoothSocket implements Closeable { IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); - mPfd = bluetoothProxy.connectSocket(mDevice, mType, + mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); synchronized (this) { if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); -- GitLab From 3f78e6ced9cb29f364b55870f8a31af8ae6d5c93 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 6 Nov 2017 12:17:30 -0800 Subject: [PATCH 0856/1408] Move connectSocket into IBluetoothSocketManager (3/3) Bug: 68359837 Test: none Merged-In: I1161a5fe74b034fba0112fd3a78bdf1fbace6e12 Change-Id: I1161a5fe74b034fba0112fd3a78bdf1fbace6e12 --- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 23956e14d9e..0569913435a 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -375,7 +375,7 @@ public final class BluetoothSocket implements Closeable { IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); - mPfd = bluetoothProxy.connectSocket(mDevice, mType, + mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); synchronized (this) { if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); -- GitLab From 8d93c73ed99a732c75caf65d5a44bb3090dbb027 Mon Sep 17 00:00:00 2001 From: Marie Janssen Date: Mon, 26 Jun 2017 07:21:03 -0700 Subject: [PATCH 0857/1408] Bluetooth: Use elapsed real time for calculating onDuration The system time can change after boot. Using elapsed real time avoids this problem. Test: start factory reset device, adb shell dumpsys bluetooth_manager Change-Id: Ieea2a2bc8b4bcb375897e609275207b6c3eccdce Fixes: 62985817 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 75206e48aa8..c34c30cf6e4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -195,6 +195,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private LinkedList mActiveLogs; private LinkedList mCrashTimestamps; private int mCrashes; + private long mLastEnabledTime; // configuration from external IBinder call which is used to // synchronize with broadcast receiver. @@ -2021,6 +2022,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); addActiveLog(packageName, true); + mLastEnabledTime = SystemClock.elapsedRealtime(); } private void addActiveLog(String packageName, boolean enable) { @@ -2142,7 +2144,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" address: " + mAddress); writer.println(" name: " + mName); if (mEnable) { - long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); + long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime; String onDurationString = String.format("%02d:%02d:%02d.%03d", (int)(onDuration / (1000 * 60 * 60)), (int)((onDuration / (1000 * 60)) % 60), -- GitLab From 57e570ee2ea0f9d8da921579ecfeb3a50133c5f8 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 8 Nov 2017 09:57:12 -0800 Subject: [PATCH 0858/1408] Bluetooth HID Device API docs and helper * Add a builder for BluetoothHidDeviceAppQosSettings * Add documentation for BluetoothHidDeviceAppSdpSettings and BluetoothHidDeviceAppQosSettings * Fix the behavior of and BluetoothHidDeviceAppSdpSettings.equals() and BluetoothHidDeviceAppQosSettings.equals() Bug: 63384609 Test: test with HID device apps Change-Id: I933f4ec3034e1f704ddd614e48b76d27fda8b6ff --- .../android/bluetooth/BluetoothHidDevice.java | 36 ++++-- .../BluetoothHidDeviceAppQosSettings.java | 103 +++++++++++++++++- .../BluetoothHidDeviceAppSdpSettings.java | 20 +++- 3 files changed, 148 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index e3d763ab9b1..6692e137fa0 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -350,13 +350,22 @@ public final class BluetoothHidDevice implements BluetoothProfile { * application can be registered at time. When no longer used, application * should be unregistered using * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * The registration status should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related + * to the return value of this method. * * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. + * The HID Device SDP record is required. * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. + * The Incoming QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. + * The Outgoing QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be * sent. - * @return + * The BluetoothHidDeviceCallback object is required. + * @return true if the command is successfully sent; otherwise false. */ public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, @@ -394,12 +403,15 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@link #registerApp * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} + * The registration status should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related + * to the return value of this method. * * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, * BluetoothHidDeviceAppConfiguration, * boolean)} - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { Log.v(TAG, "unregisterApp()"); @@ -426,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in * descriptor. * @param data Report data, not including Report Id. - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; @@ -452,7 +464,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param type Report Type, as in request. * @param id Report Id, as in request. * @param data Report data, not including Report Id. - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); @@ -478,7 +490,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean reportError(BluetoothDevice device, byte error) { Log.v(TAG, "reportError(): device=" + device + " error=" + error); @@ -524,10 +536,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Initiates connection to host which currently has Virtual Cable - * established with device. + * Initiates connection to host which is currently paired with this device. + * If the application is not registered, #connect(BluetoothDevice) will fail. + * The connection state should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related + * to the return value of this method. * - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean connect(BluetoothDevice device) { Log.v(TAG, "connect(): device=" + device); @@ -550,8 +565,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Disconnects from currently connected host. + * The connection state should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related + * to the return value of this method. * - * @return + * @return true if the command is successfully sent; otherwise false. */ public boolean disconnect(BluetoothDevice device) { Log.v(TAG, "disconnect(): device=" + device); diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index ccc3ef4008f..881ae98d988 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -45,6 +45,21 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public static final int MAX = (int) 0xffffffff; + /** + * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. + * The QoS Settings is optional. + * Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * {@see + * https://www.bluetooth.com/specifications/profiles-overview + * + * Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D } + * @param serviceType L2CAP service type + * @param tokenRate L2CAP token rate + * @param tokenBucketSize L2CAP token bucket size + * @param peakBandwidth L2CAP peak bandwidth + * @param latency L2CAP latency + * @param delayVariation L2CAP delay variation + */ public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, int peakBandwidth, int latency, int delayVariation) { this.serviceType = serviceType; @@ -59,7 +74,12 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothHidDeviceAppQosSettings) { BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; - return false; + return this.serviceType == qos.serviceType + && this.tokenRate == qos.tokenRate + && this.tokenBucketSize == qos.tokenBucketSize + && this.peakBandwidth == qos.peakBandwidth + && this.latency == qos.latency + && this.delayVariation == qos.delayVariation; } return false; } @@ -106,4 +126,85 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation }; } + + /** + * A helper to build the BluetoothHidDeviceAppQosSettings object. + */ + public static class Builder { + // Optional parameters - initialized to default values + private int mServiceType = SERVICE_BEST_EFFORT; + private int mTokenRate = 0; + private int mTokenBucketSize = 0; + private int mPeakBandwidth = 0; + private int mLatency = MAX; + private int mDelayVariation = MAX; + + /** + * Set the service type. + * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT, + * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. + * @return BluetoothHidDeviceAppQosSettings Builder with specified service type. + */ + public Builder serviceType(int val) { + mServiceType = val; + return this; + } + /** + * Set the token rate. + * @param val token rate + * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate. + */ + public Builder tokenRate(int val) { + mTokenRate = val; + return this; + } + + /** + * Set the bucket size. + * @param val bucket size + * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size. + */ + public Builder tokenBucketSize(int val) { + mTokenBucketSize = val; + return this; + } + + /** + * Set the peak bandwidth. + * @param val peak bandwidth + * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth. + */ + public Builder peakBandwidth(int val) { + mPeakBandwidth = val; + return this; + } + /** + * Set the latency. + * @param val latency + * @return BluetoothHidDeviceAppQosSettings Builder with specified latency. + */ + public Builder latency(int val) { + mLatency = val; + return this; + } + + /** + * Set the delay variation. + * @param val delay variation + * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation. + */ + public Builder delayVariation(int val) { + mDelayVariation = val; + return this; + } + + /** + * Build the BluetoothHidDeviceAppQosSettings object. + * @return BluetoothHidDeviceAppQosSettings object with current settings. + */ + public BluetoothHidDeviceAppQosSettings build() { + return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize, + mPeakBandwidth, mLatency, mDelayVariation); + } + } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index f01c4932896..4669637043a 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -19,6 +19,8 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; + /** * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth * HID Device application. @@ -39,6 +41,18 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { public final byte subclass; public final byte[] descriptors; + /** + * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. + * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes. + * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes. + * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes. + * @param subclass Subclass of this Bluetooth HID device. + * See + * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2 + * @param descriptors Descriptors of this Bluetooth HID device. + * See + * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6 Maximum length is 2048 bytes. + */ public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, byte subclass, byte[] descriptors) { this.name = name; @@ -52,7 +66,11 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { public boolean equals(Object o) { if (o instanceof BluetoothHidDeviceAppSdpSettings) { BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; - return false; + return this.name.equals(sdp.name) + && this.description.equals(sdp.description) + && this.provider.equals(sdp.provider) + && this.subclass == sdp.subclass + && Arrays.equals(this.descriptors, sdp.descriptors); } return false; } -- GitLab From fb76dc36ad7aba5124b4ef08eeba44acaf4332c3 Mon Sep 17 00:00:00 2001 From: Andre Eisenbach Date: Mon, 10 Apr 2017 13:49:13 -0700 Subject: [PATCH 0859/1408] Bluetooth: Pre-pend empty line for dumpsys section headers Test: manual Change-Id: I2daa5c985e5504606edd9ff558bd5b92af7a7240 --- .../bluetooth/BluetoothManagerService.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c34c30cf6e4..04279a31d2b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2150,31 +2150,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { (int)((onDuration / (1000 * 60)) % 60), (int)((onDuration / 1000) % 60), (int)(onDuration % 1000)); - writer.println(" time since enabled: " + onDurationString + "\n"); + writer.println(" time since enabled: " + onDurationString); } if (mActiveLogs.size() == 0) { - writer.println("Bluetooth never enabled!"); + writer.println("\nBluetooth never enabled!"); } else { - writer.println("Enable log:"); + writer.println("\nEnable log:"); for (ActiveLog log : mActiveLogs) { writer.println(" " + log); } } - writer.println("Bluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); + writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); for (Long time : mCrashTimestamps) { writer.println(" " + timeToLog(time.longValue())); } - String bleAppString = "No BLE Apps registered."; - if (mBleApps.size() == 1) { - bleAppString = "1 BLE App registered:"; - } else if (mBleApps.size() > 1) { - bleAppString = mBleApps.size() + " BLE Apps registered:"; - } - writer.println("\n" + bleAppString); + writer.println("\n" + mBleApps.size() + " BLE app" + + (mBleApps.size() == 1 ? "" : "s") + "registered"); for (ClientDeathRecipient app : mBleApps.values()) { writer.println(" " + app.getPackageName()); } -- GitLab From 691adeee0c1ca9ab93378520f96d131133b9c863 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 30 Nov 2017 16:37:05 -0800 Subject: [PATCH 0860/1408] Bluetooth HID Device: Remove BluetoothHidDeviceAppConfiguration (2/4) Bug: 69981563 Test: HID Device SL4A Test Change-Id: Ifb7002bb4f3f6f29b01a3d337ab68b674d6947c9 --- .../android/bluetooth/BluetoothHidDevice.java | 19 ++--- .../BluetoothHidDeviceAppConfiguration.java | 79 ------------------- .../bluetooth/BluetoothHidDeviceCallback.java | 8 +- 3 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 6692e137fa0..bfee27974f5 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -136,9 +136,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } @Override - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { - mCallback.onAppStatusChanged(pluggedDevice, config, registered); + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, registered); } @Override @@ -349,7 +348,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Device are only possible when application is registered. Only one * application can be registered at time. When no longer used, application * should be unregistered using - * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * {@link #unregisterApp()}. * The registration status should be tracked by the application by handling callback from * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. @@ -382,11 +381,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - BluetoothHidDeviceAppConfiguration config = - new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = new BluetoothHidDeviceCallbackWrapper(callback); - result = service.registerApp(config, sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -407,13 +404,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. * - * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link - * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, - * BluetoothHidDeviceAppConfiguration, - * boolean)} * @return true if the command is successfully sent; otherwise false. */ - public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + public boolean unregisterApp() { Log.v(TAG, "unregisterApp()"); boolean result = false; @@ -421,7 +414,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - result = service.unregisterApp(config); + result = service.unregisterApp(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java deleted file mode 100644 index d1efa2d6fca..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.os.Parcel; -import android.os.Parcelable; - -import java.util.Random; - -/** - * Represents the app configuration for a Bluetooth HID Device application. - * - * The app needs a BluetoothHidDeviceAppConfiguration token to unregister - * the Bluetooth HID Device service. - * - * {@see BluetoothHidDevice} - * - * {@hide} - */ -public final class BluetoothHidDeviceAppConfiguration implements Parcelable { - private final long mHash; - - BluetoothHidDeviceAppConfiguration() { - Random rnd = new Random(); - mHash = rnd.nextLong(); - } - - BluetoothHidDeviceAppConfiguration(long hash) { - mHash = hash; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppConfiguration) { - BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; - return mHash == config.mHash; - } - return false; - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - - @Override - public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { - long hash = in.readLong(); - return new BluetoothHidDeviceAppConfiguration(hash); - } - - @Override - public BluetoothHidDeviceAppConfiguration[] newArray(int size) { - return new BluetoothHidDeviceAppConfiguration[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeLong(mHash); - } -} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 5ccda0dc7dd..dc6f9fa1abf 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -37,21 +37,17 @@ public abstract class BluetoothHidDeviceCallback { * {@link BluetoothHidDevice#registerApp * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} * or - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * {@link BluetoothHidDevice#unregisterApp()} * , but can be also unsolicited in case e.g. Bluetooth was turned off in * which case application is unregistered automatically. * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has * Virtual Cable established with device. Only valid when application is registered, can be * null. - * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token - * required to unregister application using - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}. * @param registered true if application is registered, false * otherwise. */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); } -- GitLab From 097dbc35971517195778478a1c0107d89a777fea Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 30 Nov 2017 16:37:05 -0800 Subject: [PATCH 0861/1408] Bluetooth HID Device: Remove BluetoothHidDeviceAppConfiguration (2/4) Bug: 69981563 Test: HID Device SL4A Test Change-Id: Ifb7002bb4f3f6f29b01a3d337ab68b674d6947c9 --- .../android/bluetooth/BluetoothHidDevice.java | 19 ++--- .../BluetoothHidDeviceAppConfiguration.java | 79 ------------------- .../bluetooth/BluetoothHidDeviceCallback.java | 8 +- 3 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 6692e137fa0..bfee27974f5 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -136,9 +136,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } @Override - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { - mCallback.onAppStatusChanged(pluggedDevice, config, registered); + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, registered); } @Override @@ -349,7 +348,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Device are only possible when application is registered. Only one * application can be registered at time. When no longer used, application * should be unregistered using - * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * {@link #unregisterApp()}. * The registration status should be tracked by the application by handling callback from * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. @@ -382,11 +381,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - BluetoothHidDeviceAppConfiguration config = - new BluetoothHidDeviceAppConfiguration(); BluetoothHidDeviceCallbackWrapper cbw = new BluetoothHidDeviceCallbackWrapper(callback); - result = service.registerApp(config, sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -407,13 +404,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related * to the return value of this method. * - * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link - * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, - * BluetoothHidDeviceAppConfiguration, - * boolean)} * @return true if the command is successfully sent; otherwise false. */ - public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + public boolean unregisterApp() { Log.v(TAG, "unregisterApp()"); boolean result = false; @@ -421,7 +414,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = mService; if (service != null) { try { - result = service.unregisterApp(config); + result = service.unregisterApp(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java deleted file mode 100644 index d1efa2d6fca..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.os.Parcel; -import android.os.Parcelable; - -import java.util.Random; - -/** - * Represents the app configuration for a Bluetooth HID Device application. - * - * The app needs a BluetoothHidDeviceAppConfiguration token to unregister - * the Bluetooth HID Device service. - * - * {@see BluetoothHidDevice} - * - * {@hide} - */ -public final class BluetoothHidDeviceAppConfiguration implements Parcelable { - private final long mHash; - - BluetoothHidDeviceAppConfiguration() { - Random rnd = new Random(); - mHash = rnd.nextLong(); - } - - BluetoothHidDeviceAppConfiguration(long hash) { - mHash = hash; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppConfiguration) { - BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; - return mHash == config.mHash; - } - return false; - } - - @Override - public int describeContents() { - return 0; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - - @Override - public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { - long hash = in.readLong(); - return new BluetoothHidDeviceAppConfiguration(hash); - } - - @Override - public BluetoothHidDeviceAppConfiguration[] newArray(int size) { - return new BluetoothHidDeviceAppConfiguration[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeLong(mHash); - } -} diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 5ccda0dc7dd..dc6f9fa1abf 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -37,21 +37,17 @@ public abstract class BluetoothHidDeviceCallback { * {@link BluetoothHidDevice#registerApp * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} * or - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * {@link BluetoothHidDevice#unregisterApp()} * , but can be also unsolicited in case e.g. Bluetooth was turned off in * which case application is unregistered automatically. * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has * Virtual Cable established with device. Only valid when application is registered, can be * null. - * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token - * required to unregister application using - * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}. * @param registered true if application is registered, false * otherwise. */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, - BluetoothHidDeviceAppConfiguration config, boolean registered) { + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); } -- GitLab From 57dda24cac3ce88b63eea6a505f37ace674bd7c6 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Mon, 27 Nov 2017 16:41:47 -0800 Subject: [PATCH 0862/1408] Auto-format BluetoothAdapter.java Test: build Change-Id: I7bfc729373a24cceee05e443195640ff7f6d43b9 --- .../android/bluetooth/BluetoothAdapter.java | 460 ++++++++++++------ 1 file changed, 302 insertions(+), 158 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3d9651db683..158aebb478a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1,6 +1,6 @@ /* - * Copyright (C) 2009-2016 The Android Open Source Project - * Copyright (C) 2015 Samsung LSI + * Copyright 2009-2016 The Android Open Source Project + * Copyright 2015 Samsung LSI * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,9 +132,8 @@ public final class BluetoothAdapter { * respectively. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_STATE_CHANGED = - "android.bluetooth.adapter.action.STATE_CHANGED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"; /** * Used as an int extra field in {@link #ACTION_STATE_CHANGED} @@ -144,8 +143,7 @@ public final class BluetoothAdapter { * {@link #STATE_ON}, * {@link #STATE_TURNING_OFF}, */ - public static final String EXTRA_STATE = - "android.bluetooth.adapter.extra.STATE"; + public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; /** * Used as an int extra field in {@link #ACTION_STATE_CHANGED} * intents to request the previous power state. Possible values are: @@ -158,11 +156,17 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.extra.PREVIOUS_STATE"; /** @hide */ - @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON, - STATE_BLE_ON, STATE_BLE_TURNING_OFF}) + @IntDef({ + STATE_OFF, + STATE_TURNING_ON, + STATE_ON, + STATE_TURNING_OFF, + STATE_BLE_TURNING_ON, + STATE_BLE_ON, + STATE_BLE_TURNING_OFF + }) @Retention(RetentionPolicy.SOURCE) - public @interface AdapterState { - } + public @interface AdapterState {} /** * Indicates the local Bluetooth adapter is off. @@ -254,9 +258,8 @@ public final class BluetoothAdapter { * application can be notified when the device has ended discoverability. *

        Requires {@link android.Manifest.permission#BLUETOOTH} */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_REQUEST_DISCOVERABLE = - "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String + ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; /** * Used as an optional int extra field in {@link @@ -282,9 +285,8 @@ public final class BluetoothAdapter { * for global notification whenever Bluetooth is turned on or off. *

        Requires {@link android.Manifest.permission#BLUETOOTH} */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_REQUEST_ENABLE = - "android.bluetooth.adapter.action.REQUEST_ENABLE"; + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String + ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; /** * Activity Action: Show a system activity that allows the user to turn off @@ -305,9 +307,8 @@ public final class BluetoothAdapter { * * @hide */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_REQUEST_DISABLE = - "android.bluetooth.adapter.action.REQUEST_DISABLE"; + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String + ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; /** * Activity Action: Show a system activity that allows user to enable BLE scans even when @@ -334,9 +335,8 @@ public final class BluetoothAdapter { * respectively. *

        Requires {@link android.Manifest.permission#BLUETOOTH} */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SCAN_MODE_CHANGED = - "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} @@ -359,8 +359,7 @@ public final class BluetoothAdapter { /** @hide */ @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE}) @Retention(RetentionPolicy.SOURCE) - public @interface ScanMode { - } + public @interface ScanMode {} /** * Indicates that both inquiry scan and page scan are disabled on the local @@ -396,17 +395,15 @@ public final class BluetoothAdapter { * discovery. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_DISCOVERY_STARTED = - "android.bluetooth.adapter.action.DISCOVERY_STARTED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; /** * Broadcast Action: The local Bluetooth adapter has finished the device * discovery process. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_DISCOVERY_FINISHED = - "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; /** * Broadcast Action: The local Bluetooth adapter has changed its friendly @@ -416,9 +413,8 @@ public final class BluetoothAdapter { * the name. *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LOCAL_NAME_CHANGED = - "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; /** * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED} * intents to request the local Bluetooth name. @@ -451,8 +447,8 @@ public final class BluetoothAdapter { * *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String + ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; /** @@ -476,8 +472,7 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi - public static final String ACTION_BLE_STATE_CHANGED = + @SystemApi public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; /** @@ -574,8 +569,7 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; - private final ReentrantReadWriteLock mServiceLock = - new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); private final Object mLock = new Object(); private final Map mLeScanClients; @@ -655,8 +649,9 @@ public final class BluetoothAdapter { if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } - return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", - address[0], address[1], address[2], address[3], address[4], address[5])); + return new BluetoothDevice( + String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], + address[2], address[3], address[4], address[5])); } /** @@ -668,7 +663,9 @@ public final class BluetoothAdapter { * on this device before calling this method. */ public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - if (!getLeAccess()) return null; + if (!getLeAccess()) { + return null; + } synchronized (mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); @@ -698,8 +695,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sPeriodicAdvertisingManager == null) { - sPeriodicAdvertisingManager = - new PeriodicAdvertisingManager(mManagerService); + sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); } } return sPeriodicAdvertisingManager; @@ -709,7 +705,9 @@ public final class BluetoothAdapter { * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ public BluetoothLeScanner getBluetoothLeScanner() { - if (!getLeAccess()) return null; + if (!getLeAccess()) { + return null; + } synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); @@ -729,7 +727,9 @@ public final class BluetoothAdapter { public boolean isEnabled() { try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isEnabled(); + if (mService != null) { + return mService.isEnabled(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -750,7 +750,9 @@ public final class BluetoothAdapter { @SystemApi public boolean isLeEnabled() { final int state = getLeState(); - if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); + if (DBG) { + Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); + } return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); } @@ -781,12 +783,16 @@ public final class BluetoothAdapter { */ @SystemApi public boolean disableBLE() { - if (!isBleScanAlwaysAvailable()) return false; + if (!isBleScanAlwaysAvailable()) { + return false; + } int state = getLeState(); if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { String packageName = ActivityThread.currentPackageName(); - if (DBG) Log.d(TAG, "disableBLE(): de-registering " + packageName); + if (DBG) { + Log.d(TAG, "disableBLE(): de-registering " + packageName); + } try { mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { @@ -795,7 +801,9 @@ public final class BluetoothAdapter { return true; } - if (DBG) Log.d(TAG, "disableBLE(): Already disabled"); + if (DBG) { + Log.d(TAG, "disableBLE(): Already disabled"); + } return false; } @@ -832,16 +840,22 @@ public final class BluetoothAdapter { */ @SystemApi public boolean enableBLE() { - if (!isBleScanAlwaysAvailable()) return false; + if (!isBleScanAlwaysAvailable()) { + return false; + } try { String packageName = ActivityThread.currentPackageName(); mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { - if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + if (DBG) { + Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + } return true; } - if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); + if (DBG) { + Log.d(TAG, "enableBLE(): Calling enable"); + } return mManagerService.enable(packageName); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -877,19 +891,16 @@ public final class BluetoothAdapter { } // Consider all internal states as OFF - if (state == BluetoothAdapter.STATE_BLE_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_ON + if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { if (VDBG) { - Log.d(TAG, - "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); + Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); } state = BluetoothAdapter.STATE_OFF; } if (VDBG) { - Log.d(TAG, - "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState( - state)); + Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState( + state)); } return state; } @@ -926,7 +937,9 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } - if (VDBG) Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); + if (VDBG) { + Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); + } return state; } @@ -967,7 +980,9 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { if (isEnabled()) { - if (DBG) Log.d(TAG, "enable(): BT already enabled!"); + if (DBG) { + Log.d(TAG, "enable(): BT already enabled!"); + } return true; } try { @@ -1093,10 +1108,14 @@ public final class BluetoothAdapter { * @hide */ public ParcelUuid[] getUuids() { - if (getState() != STATE_ON) return null; + if (getState() != STATE_ON) { + return null; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getUuids(); + if (mService != null) { + return mService.getUuids(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1121,10 +1140,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setName(String name) { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setName(name); + if (mService != null) { + return mService.setName(name); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1143,10 +1166,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public BluetoothClass getBluetoothClass() { - if (getState() != STATE_ON) return null; + if (getState() != STATE_ON) { + return null; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getBluetoothClass(); + if (mService != null) { + return mService.getBluetoothClass(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1168,10 +1195,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBluetoothClass(BluetoothClass bluetoothClass) { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setBluetoothClass(bluetoothClass); + if (mService != null) { + return mService.setBluetoothClass(bluetoothClass); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1198,10 +1229,14 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @ScanMode public int getScanMode() { - if (getState() != STATE_ON) return SCAN_MODE_NONE; + if (getState() != STATE_ON) { + return SCAN_MODE_NONE; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getScanMode(); + if (mService != null) { + return mService.getScanMode(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1239,10 +1274,14 @@ public final class BluetoothAdapter { * @hide */ public boolean setScanMode(@ScanMode int mode, int duration) { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setScanMode(mode, duration); + if (mService != null) { + return mService.setScanMode(mode, duration); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1253,17 +1292,23 @@ public final class BluetoothAdapter { /** @hide */ public boolean setScanMode(int mode) { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } /* getDiscoverableTimeout() to use the latest from NV than use 0 */ return setScanMode(mode, getDiscoverableTimeout()); } /** @hide */ public int getDiscoverableTimeout() { - if (getState() != STATE_ON) return -1; + if (getState() != STATE_ON) { + return -1; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getDiscoverableTimeout(); + if (mService != null) { + return mService.getDiscoverableTimeout(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1274,10 +1319,14 @@ public final class BluetoothAdapter { /** @hide */ public void setDiscoverableTimeout(int timeout) { - if (getState() != STATE_ON) return; + if (getState() != STATE_ON) { + return; + } try { mServiceLock.readLock().lock(); - if (mService != null) mService.setDiscoverableTimeout(timeout); + if (mService != null) { + mService.setDiscoverableTimeout(timeout); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1296,7 +1345,9 @@ public final class BluetoothAdapter { public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getDiscoveryEndMillis(); + if (mService != null) { + return mService.getDiscoveryEndMillis(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1336,10 +1387,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery() { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.startDiscovery(); + if (mService != null) { + return mService.startDiscovery(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1366,10 +1421,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelDiscovery() { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.cancelDiscovery(); + if (mService != null) { + return mService.cancelDiscovery(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1398,10 +1457,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isDiscovering() { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isDiscovering(); + if (mService != null) { + return mService.isDiscovering(); + } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -1416,10 +1479,14 @@ public final class BluetoothAdapter { * @return true if Multiple Advertisement feature is supported */ public boolean isMultipleAdvertisementSupported() { - if (getState() != STATE_ON) return false; + if (getState() != STATE_ON) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isMultiAdvertisementSupported(); + if (mService != null) { + return mService.isMultiAdvertisementSupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e); } finally { @@ -1454,10 +1521,14 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isOffloadedFilteringSupported(); + if (mService != null) { + return mService.isOffloadedFilteringSupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); } finally { @@ -1472,10 +1543,14 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isOffloadedScanBatchingSupported(); + if (mService != null) { + return mService.isOffloadedScanBatchingSupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e); } finally { @@ -1490,10 +1565,14 @@ public final class BluetoothAdapter { * @return true if chipset supports LE 2M PHY feature */ public boolean isLe2MPhySupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isLe2MPhySupported(); + if (mService != null) { + return mService.isLe2MPhySupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e); } finally { @@ -1508,10 +1587,14 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Coded PHY feature */ public boolean isLeCodedPhySupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isLeCodedPhySupported(); + if (mService != null) { + return mService.isLeCodedPhySupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e); } finally { @@ -1526,10 +1609,14 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Extended Advertising feature */ public boolean isLeExtendedAdvertisingSupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isLeExtendedAdvertisingSupported(); + if (mService != null) { + return mService.isLeExtendedAdvertisingSupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e); } finally { @@ -1544,10 +1631,14 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Periodic Advertising feature */ public boolean isLePeriodicAdvertisingSupported() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.isLePeriodicAdvertisingSupported(); + if (mService != null) { + return mService.isLePeriodicAdvertisingSupported(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e); } finally { @@ -1563,10 +1654,14 @@ public final class BluetoothAdapter { * @return the maximum LE advertising data length. */ public int getLeMaximumAdvertisingDataLength() { - if (!getLeAccess()) return 0; + if (!getLeAccess()) { + return 0; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeMaximumAdvertisingDataLength(); + if (mService != null) { + return mService.getLeMaximumAdvertisingDataLength(); + } } catch (RemoteException e) { Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e); } finally { @@ -1582,7 +1677,9 @@ public final class BluetoothAdapter { * @hide */ public boolean isHardwareTrackingFiltersAvailable() { - if (!getLeAccess()) return false; + if (!getLeAccess()) { + return false; + } try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { @@ -1669,7 +1766,9 @@ public final class BluetoothAdapter { } try { mServiceLock.readLock().lock(); - if (mService != null) return toDeviceSet(mService.getBondedDevices()); + if (mService != null) { + return toDeviceSet(mService.getBondedDevices()); + } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1723,10 +1822,14 @@ public final class BluetoothAdapter { * @hide */ public int getConnectionState() { - if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED; + if (getState() != STATE_ON) { + return BluetoothAdapter.STATE_DISCONNECTED; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getAdapterConnectionState(); + if (mService != null) { + return mService.getAdapterConnectionState(); + } } catch (RemoteException e) { Log.e(TAG, "getConnectionState:", e); } finally { @@ -1750,10 +1853,14 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int profile) { - if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; + if (getState() != STATE_ON) { + return BluetoothProfile.STATE_DISCONNECTED; + } try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getProfileConnectionState(profile); + if (mService != null) { + return mService.getProfileConnectionState(profile); + } } catch (RemoteException e) { Log.e(TAG, "getProfileConnectionState:", e); } finally { @@ -1790,7 +1897,7 @@ public final class BluetoothAdapter { *

        Valid RFCOMM channels are in range 1 to 30. *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} *

        To auto assign a channel without creating a SDP record use - * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. + * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * * @param channel RFCOMM channel to listen on * @param mitm enforce man-in-the-middle protection for authentication. @@ -1802,10 +1909,10 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, - boolean min16DigitPin) - throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin); + boolean min16DigitPin) throws IOException { + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, + min16DigitPin); int errno = socket.mSocket.bindListen(); if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1913,8 +2020,8 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord( - String name, UUID uuid) throws IOException { + public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) + throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); } @@ -1922,8 +2029,8 @@ public final class BluetoothAdapter { private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { BluetoothServerSocket socket; - socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, - encrypt, new ParcelUuid(uuid)); + socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt, + new ParcelUuid(uuid)); socket.setServiceName(name); int errno = socket.mSocket.bindListen(); if (errno != 0) { @@ -1945,8 +2052,8 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, false, false, port); + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1969,10 +2076,9 @@ public final class BluetoothAdapter { * permissions. * @hide */ - public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) - throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_RFCOMM, false, true, port); + public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException { + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -1996,8 +2102,8 @@ public final class BluetoothAdapter { * @hide */ public static BluetoothServerSocket listenUsingScoOn() throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_SCO, false, false, -1); + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1); int errno = socket.mSocket.bindListen(); if (errno < 0) { //TODO(BT): Throw the same exception error code @@ -2011,7 +2117,7 @@ public final class BluetoothAdapter { * Construct an encrypted, authenticated, L2CAP server socket. * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use - * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * * @param port the PSM to listen on * @param mitm enforce man-in-the-middle protection for authentication. @@ -2024,8 +2130,9 @@ public final class BluetoothAdapter { */ public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin); + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, + min16DigitPin); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -2043,7 +2150,7 @@ public final class BluetoothAdapter { * Construct an encrypted, authenticated, L2CAP server socket. * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use - * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * * @param port the PSM to listen on * @return An L2CAP BluetoothServerSocket @@ -2060,7 +2167,7 @@ public final class BluetoothAdapter { * Construct an insecure L2CAP server socket. * Call #accept to retrieve connections to this socket. *

        To auto assign a port without creating a SDP record use - * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * * @param port the PSM to listen on * @return An L2CAP BluetoothServerSocket @@ -2069,8 +2176,9 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { - BluetoothServerSocket socket = new BluetoothServerSocket( - BluetoothSocket.TYPE_L2CAP, false, false, port, false, false); + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false, + false); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { socket.setChannel(socket.mSocket.getPort()); @@ -2114,7 +2222,9 @@ public final class BluetoothAdapter { */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile) { - if (context == null || listener == null) return false; + if (context == null || listener == null) { + return false; + } if (profile == BluetoothProfile.HEADSET) { BluetoothHeadset headset = new BluetoothHeadset(context, listener); @@ -2172,7 +2282,9 @@ public final class BluetoothAdapter { * @param proxy Profile proxy object */ public void closeProfileProxy(int profile, BluetoothProfile proxy) { - if (proxy == null) return; + if (proxy == null) { + return; + } switch (profile) { case BluetoothProfile.HEADSET: @@ -2241,7 +2353,9 @@ public final class BluetoothAdapter { private final IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + if (DBG) { + Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + } mServiceLock.writeLock().lock(); mService = bluetoothService; @@ -2263,14 +2377,22 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown: " + mService); + } try { mServiceLock.writeLock().lock(); mService = null; - if (mLeScanClients != null) mLeScanClients.clear(); - if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); - if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); + if (mLeScanClients != null) { + mLeScanClients.clear(); + } + if (sBluetoothLeAdvertiser != null) { + sBluetoothLeAdvertiser.cleanup(); + } + if (sBluetoothLeScanner != null) { + sBluetoothLeScanner.cleanup(); + } } finally { mServiceLock.writeLock().unlock(); } @@ -2291,7 +2413,9 @@ public final class BluetoothAdapter { } public void onBrEdrDown() { - if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService); + if (VDBG) { + Log.i(TAG, "onBrEdrDown: " + mService); + } } }; @@ -2303,7 +2427,9 @@ public final class BluetoothAdapter { */ public boolean enableNoAutoConnect() { if (isEnabled()) { - if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); + if (DBG) { + Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); + } return true; } try { @@ -2352,7 +2478,10 @@ public final class BluetoothAdapter { * @hide */ public interface BluetoothStateChangeCallback { - public void onBluetoothStateChange(boolean on); + /** + * @hide + */ + void onBluetoothStateChange(boolean on); } /** @@ -2361,8 +2490,7 @@ public final class BluetoothAdapter { public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { private BluetoothStateChangeCallback mCallback; - StateChangeCallbackWrapper(BluetoothStateChangeCallback - callback) { + StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) { mCallback = callback; } @@ -2459,7 +2587,7 @@ public final class BluetoothAdapter { * if no RSSI value is available. * @param scanRecord The content of the advertisement record offered by the remote device. */ - public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); + void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); } /** @@ -2495,20 +2623,28 @@ public final class BluetoothAdapter { @Deprecated @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { - if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); + if (DBG) { + Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); + } if (callback == null) { - if (DBG) Log.e(TAG, "startLeScan: null callback"); + if (DBG) { + Log.e(TAG, "startLeScan: null callback"); + } return false; } BluetoothLeScanner scanner = getBluetoothLeScanner(); if (scanner == null) { - if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); + if (DBG) { + Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); + } return false; } synchronized (mLeScanClients) { if (mLeScanClients.containsKey(callback)) { - if (DBG) Log.e(TAG, "LE Scan has already started"); + if (DBG) { + Log.e(TAG, "LE Scan has already started"); + } return false; } @@ -2538,7 +2674,9 @@ public final class BluetoothAdapter { } List scanServiceUuids = scanRecord.getServiceUuids(); if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) { - if (DBG) Log.d(TAG, "uuids does not match"); + if (DBG) { + Log.d(TAG, "uuids does not match"); + } return; } } @@ -2546,16 +2684,18 @@ public final class BluetoothAdapter { scanRecord.getBytes()); } }; - ScanSettings settings = new ScanSettings.Builder() - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + ScanSettings settings = new ScanSettings.Builder().setCallbackType( + ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build(); List filters = new ArrayList(); if (serviceUuids != null && serviceUuids.length > 0) { // Note scan filter does not support matching an UUID array so we put one // UUID to hardware and match the whole array in callback. - ScanFilter filter = new ScanFilter.Builder().setServiceUuid( - new ParcelUuid(serviceUuids[0])).build(); + ScanFilter filter = + new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0])) + .build(); filters.add(filter); } scanner.startScan(filters, settings, scanCallback); @@ -2580,7 +2720,9 @@ public final class BluetoothAdapter { @Deprecated @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void stopLeScan(LeScanCallback callback) { - if (DBG) Log.d(TAG, "stopLeScan()"); + if (DBG) { + Log.d(TAG, "stopLeScan()"); + } BluetoothLeScanner scanner = getBluetoothLeScanner(); if (scanner == null) { return; @@ -2588,7 +2730,9 @@ public final class BluetoothAdapter { synchronized (mLeScanClients) { ScanCallback scanCallback = mLeScanClients.remove(callback); if (scanCallback == null) { - if (DBG) Log.d(TAG, "scan not started yet"); + if (DBG) { + Log.d(TAG, "scan not started yet"); + } return; } scanner.stopScan(scanCallback); -- GitLab From bbb0d40a3e8998eaa705943b68e10daec27d8613 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Mon, 27 Nov 2017 16:42:11 -0800 Subject: [PATCH 0863/1408] Auto-format BluetoothManagerService.java Test: build Change-Id: If97c454a8e5aff34c4f8550f7ade3da413a200b7 --- .../bluetooth/BluetoothManagerService.java | 822 ++++++++++-------- 1 file changed, 464 insertions(+), 358 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 04279a31d2b..763a4e49459 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -67,6 +67,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; import java.util.LinkedList; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -79,15 +80,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; - private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid"; - private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address"; - private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid"; + private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address"; + private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name"; private static final int ACTIVE_LOG_MAX_SIZE = 20; private static final int CRASH_LOG_MAX_SIZE = 100; private static final String REASON_AIRPLANE_MODE = "airplane mode"; private static final String REASON_DISALLOWED = "disallowed by system"; - private static final String REASON_SHARING_DISALLOWED = "sharing disallowed by system"; private static final String REASON_RESTARTED = "automatic restart"; private static final String REASON_START_CRASH = "turn-on crash"; private static final String REASON_SYSTEM_BOOT = "system boot"; @@ -130,14 +130,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MAX_ERROR_RESTART_RETRIES = 6; // Bluetooth persisted setting is off - private static final int BLUETOOTH_OFF=0; + private static final int BLUETOOTH_OFF = 0; // Bluetooth persisted setting is on // and Airplane mode won't affect Bluetooth state at start up - private static final int BLUETOOTH_ON_BLUETOOTH=1; + private static final int BLUETOOTH_ON_BLUETOOTH = 1; // Bluetooth persisted setting is on // but Airplane mode will affect Bluetooth state at start up // and Airplane mode will have higher priority. - private static final int BLUETOOTH_ON_AIRPLANE=2; + private static final int BLUETOOTH_ON_AIRPLANE = 2; private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; @@ -154,8 +154,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private IBinder mBluetoothBinder; private IBluetooth mBluetooth; private IBluetoothGatt mBluetoothGatt; - private final ReentrantReadWriteLock mBluetoothLock = - new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; @@ -175,7 +174,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mEnable; private long mTimestamp; - public ActiveLog(String packageName, boolean enable, long timestamp) { + ActiveLog(String packageName, boolean enable, long timestamp) { mPackageName = packageName; mEnable = enable; mTimestamp = timestamp; @@ -186,8 +185,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String toString() { - return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " - + mPackageName; + return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + + mPackageName; } } @@ -203,7 +202,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mEnableExternal; // Map of apps registered to keep BLE scanning on. - private Map mBleApps = new ConcurrentHashMap(); + private Map mBleApps = + new ConcurrentHashMap(); private int mState; private final BluetoothHandler mHandler; @@ -212,51 +212,52 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Save a ProfileServiceConnections object for each of the bound // bluetooth profile services - private final Map mProfileServices = - new HashMap (); + private final Map mProfileServices = + new HashMap(); private final boolean mPermissionReviewRequired; private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override - public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { - Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); + public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { + Message msg = + mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState); mHandler.sendMessage(msg); } }; private final UserRestrictionsListener mUserRestrictionsListener = new UserRestrictionsListener() { - @Override - public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, - Bundle prevRestrictions) { - - if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, - UserManager.DISALLOW_BLUETOOTH_SHARING)) { - updateOppLauncherComponentState(userId, newRestrictions.getBoolean( - UserManager.DISALLOW_BLUETOOTH_SHARING)); - } - - // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. - if (userId == UserHandle.USER_SYSTEM && - UserRestrictionsUtils.restrictionsChanged( - prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { - if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( - UserManager.DISALLOW_BLUETOOTH)) { - updateOppLauncherComponentState(userId, true); // Sharing disallowed - sendDisableMsg(REASON_DISALLOWED); - } else { - updateOppLauncherComponentState(userId, newRestrictions.getBoolean( - UserManager.DISALLOW_BLUETOOTH_SHARING)); + @Override + public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, + Bundle prevRestrictions) { + + if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, + UserManager.DISALLOW_BLUETOOTH_SHARING)) { + updateOppLauncherComponentState(userId, + newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING)); + } + + // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. + if (userId == UserHandle.USER_SYSTEM + && UserRestrictionsUtils.restrictionsChanged(prevRestrictions, + newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { + if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH)) { + updateOppLauncherComponentState(userId, true); // Sharing disallowed + sendDisableMsg(REASON_DISALLOWED); + } else { + updateOppLauncherComponentState(userId, newRestrictions.getBoolean( + UserManager.DISALLOW_BLUETOOTH_SHARING)); + } + } } - } - } - }; + }; private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { - synchronized(this) { + synchronized (this) { if (isBluetoothPersistedStateOn()) { if (isAirplaneModeOn()) { persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); @@ -278,8 +279,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - Slog.d(TAG, "Airplane Mode change - current state: " + - BluetoothAdapter.nameForState(st)); + Slog.d(TAG, + "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( + st)); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off @@ -295,11 +297,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = false; } } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); + Slog.e(TAG, "Unable to call onBrEdrDown", e); } finally { mBluetoothLock.readLock().unlock(); } - } else if (st == BluetoothAdapter.STATE_ON){ + } else if (st == BluetoothAdapter.STATE_ON) { sendDisableMsg(REASON_AIRPLANE_MODE); } } else if (mEnableExternal) { @@ -315,35 +317,42 @@ class BluetoothManagerService extends IBluetoothManager.Stub { String action = intent.getAction(); if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); - if (DBG) Slog.d(TAG, "Bluetooth Adapter name changed to " + newName); + if (DBG) { + Slog.d(TAG, "Bluetooth Adapter name changed to " + newName); + } if (newName != null) { storeNameAndAddress(newName, null); } } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) { String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS); if (newAddress != null) { - if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); + if (DBG) { + Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); + } storeNameAndAddress(null, newAddress); } else { - if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found"); + if (DBG) { + Slog.e(TAG, "No Bluetooth Adapter address parameter found"); + } } } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); if (Settings.Global.BLUETOOTH_ON.equals(name)) { // The Bluetooth On state may be changed during system restore. - final String prevValue = intent.getStringExtra( - Intent.EXTRA_SETTING_PREVIOUS_VALUE); - final String newValue = intent.getStringExtra( - Intent.EXTRA_SETTING_NEW_VALUE); + final String prevValue = + intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE); + final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE); - if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + - prevValue + ", newValue=" + newValue); + if (DBG) { + Slog.d(TAG, + "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue + + ", newValue=" + newValue); + } if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) { Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING, - newValue.equals("0") ? - RESTORE_SETTING_TO_OFF : - RESTORE_SETTING_TO_ON, 0); + newValue.equals("0") ? RESTORE_SETTING_TO_OFF + : RESTORE_SETTING_TO_ON, 0); mHandler.sendMessage(msg); } } @@ -356,8 +365,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; - mPermissionReviewRequired = context.getResources().getBoolean( - com.android.internal.R.bool.config_permissionReviewRequired); + mPermissionReviewRequired = context.getResources() + .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired); mActiveLogs = new LinkedList(); mCrashTimestamps = new LinkedList(); @@ -389,23 +398,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { loadStoredNameAndAddress(); if (isBluetoothPersistedStateOn()) { - if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); + if (DBG) { + Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); + } mEnableExternal = true; } - String airplaneModeRadios = Settings.Global.getString(mContentResolver, - Settings.Global.AIRPLANE_MODE_RADIOS); - if (airplaneModeRadios == null || - airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) { + String airplaneModeRadios = + Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); + if (airplaneModeRadios == null || airplaneModeRadios.contains( + Settings.Global.RADIO_BLUETOOTH)) { mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), - true, mAirplaneModeObserver); + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, + mAirplaneModeObserver); } int systemUiUid = -1; try { - systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + systemUiUid = mContext.getPackageManager() + .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, + UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); @@ -416,7 +428,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Returns true if airplane mode is currently on */ - private final boolean isAirplaneModeOn() { + private boolean isAirplaneModeOn() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } @@ -424,31 +436,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Returns true if the Bluetooth saved state is "on" */ - private final boolean isBluetoothPersistedStateOn() { - int state = Settings.Global.getInt(mContentResolver, - Settings.Global.BLUETOOTH_ON, -1); - if (DBG) Slog.d(TAG, "Bluetooth persisted state: " + state); + private boolean isBluetoothPersistedStateOn() { + int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); + if (DBG) { + Slog.d(TAG, "Bluetooth persisted state: " + state); + } return state != BLUETOOTH_OFF; } /** * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ - private final boolean isBluetoothPersistedStateOnBluetooth() { - return Settings.Global.getInt(mContentResolver, - Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; + private boolean isBluetoothPersistedStateOnBluetooth() { + return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, + BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; } /** * Save the Bluetooth on/off state */ private void persistBluetoothSetting(int value) { - if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value); + if (DBG) { + Slog.d(TAG, "Persisting Bluetooth Setting: " + value); + } // waive WRITE_SECURE_SETTINGS permission check long callingIdentity = Binder.clearCallingIdentity(); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLUETOOTH_ON, - value); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); Binder.restoreCallingIdentity(callingIdentity); } @@ -458,7 +471,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * @return */ private boolean isNameAndAddressSet() { - return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0; + return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0; } /** @@ -466,17 +479,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * in the local cache */ private void loadStoredNameAndAddress() { - if (DBG) Slog.d(TAG, "Loading stored name and address"); - if (mContext.getResources().getBoolean - (com.android.internal.R.bool.config_bluetooth_address_validation) && - Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) { + if (DBG) { + Slog.d(TAG, "Loading stored name and address"); + } + if (mContext.getResources() + .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation) + && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) + == 0) { // if the valid flag is not set, don't load the address and name - if (DBG) Slog.d(TAG, "invalid bluetooth name and address stored"); + if (DBG) { + Slog.d(TAG, "invalid bluetooth name and address stored"); + } return; } mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); - if (DBG) Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); + if (DBG) { + Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); + } } /** @@ -489,15 +509,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (name != null) { Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); mName = name; - if (DBG) Slog.d(TAG,"Stored Bluetooth name: " + - Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME)); + if (DBG) { + Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver, + SECURE_SETTINGS_BLUETOOTH_NAME)); + } } if (address != null) { Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); - mAddress=address; - if (DBG) Slog.d(TAG,"Stored Bluetoothaddress: " + - Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + mAddress = address; + if (DBG) { + Slog.d(TAG, + "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver, + SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + } } if ((name != null) && (address != null)) { @@ -505,7 +530,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public IBluetooth registerAdapter(IBluetoothManagerCallback callback){ + public IBluetooth registerAdapter(IBluetoothManagerCallback callback) { if (callback == null) { Slog.w(TAG, "Callback is null in registerAdapter"); return null; @@ -522,19 +547,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Callback is null in unregisterAdapter"); return; } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); msg.obj = callback; mHandler.sendMessage(msg); } public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (callback == null) { - Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); - return; + Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); + return; } Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; @@ -542,11 +565,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (callback == null) { - Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); - return; + Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); + return; } Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); msg.obj = callback; @@ -554,15 +576,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean isEnabled() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"isEnabled(): not allowed for non-active and non system user"); + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { + Slog.w(TAG, "isEnabled(): not allowed for non-active and non system user"); return false; } try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) return mBluetooth.isEnabled(); + if (mBluetooth != null) { + return mBluetooth.isEnabled(); + } } catch (RemoteException e) { Slog.e(TAG, "isEnabled()", e); } finally { @@ -572,15 +595,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public int getState() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { + if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { Slog.w(TAG, "getState(): report OFF for non-active and non system user"); return BluetoothAdapter.STATE_OFF; } try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) return mBluetooth.getState(); + if (mBluetooth != null) { + return mBluetooth.getState(); + } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); } finally { @@ -592,26 +616,29 @@ class BluetoothManagerService extends IBluetoothManager.Stub { class ClientDeathRecipient implements IBinder.DeathRecipient { private String mPackageName; - public ClientDeathRecipient(String packageName) { + ClientDeathRecipient(String packageName) { mPackageName = packageName; } public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); + if (DBG) { + Slog.d(TAG, "Binder is dead - unregister " + mPackageName); + } if (isBleAppPresent()) { - // Nothing to do, another app is here. - return; + // Nothing to do, another app is here. + return; + } + if (DBG) { + Slog.d(TAG, "Disabling LE only mode after application crash"); } - if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash"); try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null && - mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { + if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { mEnable = false; mBluetooth.onBrEdrDown(); } } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onBrEdrDown", e); + Slog.e(TAG, "Unable to call onBrEdrDown", e); } finally { mBluetoothLock.readLock().unlock(); } @@ -641,15 +668,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onChange(boolean selfChange) { if (isBleScanAlwaysAvailable()) { - // Nothing to do - return; + // Nothing to do + return; } // BLE scan is not available. disableBleScanMode(); clearBleApps(); try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) mBluetooth.onBrEdrDown(); + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + } } catch (RemoteException e) { Slog.e(TAG, "error when disabling bluetooth", e); } finally { @@ -659,8 +688,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { }; mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), - false, contentObserver); + Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false, + contentObserver); } // Disable ble scan only mode. @@ -668,7 +697,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { - if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable"); + if (DBG) { + Slog.d(TAG, "Reseting the mEnable flag for clean disable"); + } mEnable = false; } } catch (RemoteException e) { @@ -688,15 +719,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } mBleApps.put(token, deathRec); - if (DBG) Slog.d(TAG, "Registered for death of " + packageName); + if (DBG) { + Slog.d(TAG, "Registered for death of " + packageName); + } } else if (!enable && r != null) { // Unregister death recipient as the app goes away. token.unlinkToDeath(r, 0); mBleApps.remove(token); - if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); + if (DBG) { + Slog.d(TAG, "Unregistered for death of " + packageName); + } } int appCount = mBleApps.size(); - if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); + if (DBG) { + Slog.d(TAG, appCount + " registered Ble Apps"); + } if (appCount == 0 && mEnable) { disableBleScanMode(); } @@ -713,7 +750,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** @hide */ public boolean isBleAppPresent() { - if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); + if (DBG) { + Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); + } return mBleApps.size() > 0; } @@ -721,17 +760,23 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Action taken when GattService is turned on */ private void onBluetoothGattServiceUp() { - if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up"); + if (DBG) { + Slog.d(TAG, "BluetoothGatt Service is Up"); + } try { mBluetoothLock.readLock().lock(); if (mBluetooth == null) { - if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); + if (DBG) { + Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); + } return; } int st = mBluetooth.getState(); if (st != BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " + - BluetoothAdapter.nameForState(st)); + if (DBG) { + Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " + + BluetoothAdapter.nameForState(st)); + } return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { @@ -740,7 +785,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } } catch (RemoteException e) { - Slog.e(TAG,"Unable to call onServiceUp", e); + Slog.e(TAG, "Unable to call onServiceUp", e); } finally { mBluetoothLock.readLock().unlock(); } @@ -751,7 +796,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * and turn off all service and stack if no LE app needs it */ private void sendBrEdrDownCallback() { - if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks"); + if (DBG) { + Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks"); + } if (mBluetooth == null) { Slog.w(TAG, "Bluetooth handle is null"); @@ -768,7 +815,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { try { mBluetoothLock.readLock().lock(); - if (mBluetooth != null) mBluetooth.onBrEdrDown(); + if (mBluetooth != null) { + mBluetooth.onBrEdrDown(); + } } catch (RemoteException e) { Slog.e(TAG, "Call to onBrEdrDown() failed.", e); } finally { @@ -778,8 +827,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } - public boolean enableNoAutoConnect(String packageName) - { + public boolean enableNoAutoConnect(String packageName) { if (isBluetoothDisallowed()) { if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); @@ -788,11 +836,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); + "Need BLUETOOTH ADMIN permission"); if (DBG) { - Slog.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding); + Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " + + mBinding); } int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); @@ -800,7 +848,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { throw new SecurityException("no permission to enable Bluetooth quietly"); } - synchronized(mReceiver) { + synchronized (mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; sendEnableMsg(true, packageName); @@ -814,7 +862,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBluetoothDisallowed()) { if (DBG) { - Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); + Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); } return false; } @@ -828,26 +876,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (!isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { return false; } } if (DBG) { - Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + - " mBinding = " + mBinding + " mState = " + - BluetoothAdapter.nameForState(mState)); + Slog.d(TAG, "enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } - synchronized(mReceiver) { + synchronized (mReceiver) { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check sendEnableMsg(false, packageName); } - if (DBG) Slog.d(TAG, "enable returning"); + if (DBG) { + Slog.d(TAG, "enable returning"); + } return true; } @@ -864,19 +912,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (isEnabled() && mPermissionReviewRequired - && startConsentUiIfNeeded(packageName, callingUid, - BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { return false; } } if (DBG) { - Slog.d(TAG,"disable(): mBluetooth = " + mBluetooth + - " mBinding = " + mBinding); + Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding); } - synchronized(mReceiver) { + synchronized (mReceiver) { if (persist) { persistBluetoothSetting(BLUETOOTH_OFF); } @@ -886,8 +932,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - private boolean startConsentUiIfNeeded(String packageName, - int callingUid, String intentAction) throws RemoteException { + private boolean startConsentUiIfNeeded(String packageName, int callingUid, String intentAction) + throws RemoteException { try { // Validate the package only if we are going to use it ApplicationInfo applicationInfo = mContext.getPackageManager() @@ -895,14 +941,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); if (applicationInfo.uid != callingUid) { - throw new SecurityException("Package " + callingUid - + " not in uid " + callingUid); + throw new SecurityException("Package " + callingUid + " not in uid " + callingUid); } Intent intent = new Intent(intentAction); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { @@ -918,13 +963,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindAndFinish() { if (DBG) { - Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + - " mBinding = " + mBinding + " mUnbinding = " + mUnbinding); + Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding + + " mUnbinding = " + mUnbinding); } try { mBluetoothLock.writeLock().lock(); - if (mUnbinding) return; + if (mUnbinding) { + return; + } mUnbinding = true; mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE); @@ -933,7 +980,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetooth.unregisterCallback(mBluetoothCallback); } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister BluetoothCallback",re); + Slog.e(TAG, "Unable to unregister BluetoothCallback", re); } mBluetoothBinder = null; mBluetooth = null; @@ -959,8 +1006,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IBluetoothProfileServiceConnection proxy) { if (!mEnable) { if (DBG) { - Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile + - ", while Bluetooth was disabled"); + Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile + + ", while Bluetooth was disabled"); } return false; } @@ -968,15 +1015,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); if (psc == null) { if (DBG) { - Slog.d(TAG, "Creating new ProfileServiceConnections object for" - + " profile: " + bluetoothProfile); + Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: " + + bluetoothProfile); } - if (bluetoothProfile != BluetoothProfile.HEADSET) return false; + if (bluetoothProfile != BluetoothProfile.HEADSET) { + return false; + } Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); - if (!psc.bindService()) return false; + if (!psc.bindService()) { + return false; + } mProfileServices.put(new Integer(bluetoothProfile), psc); } @@ -1022,7 +1073,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * PHASE_SYSTEM_SERVICES_READY. */ public void handleOnBootPhase() { - if (DBG) Slog.d(TAG, "Bluetooth boot completed"); + if (DBG) { + Slog.d(TAG, "Bluetooth boot completed"); + } UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); @@ -1031,10 +1084,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { - if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); + if (DBG) { + Slog.d(TAG, "Auto-enabling Bluetooth."); + } sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); } else if (!isNameAndAddressSet()) { - if (DBG) Slog.d(TAG, "Getting adapter name and address"); + if (DBG) { + Slog.d(TAG, "Getting adapter name and address"); + } Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } @@ -1044,7 +1101,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Called when switching to a different foreground user. */ public void handleOnSwitchUser(int userHandle) { - if (DBG) Slog.d(TAG, "User " + userHandle + " switched"); + if (DBG) { + Slog.d(TAG, "User " + userHandle + " switched"); + } mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget(); } @@ -1052,7 +1111,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Called when user is unlocked. */ public void handleOnUnlockUser(int userHandle) { - if (DBG) Slog.d(TAG, "User " + userHandle + " unlocked"); + if (DBG) { + Slog.d(TAG, "User " + userHandle + " unlocked"); + } mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget(); } @@ -1060,10 +1121,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * This class manages the clients connected to a given ProfileService * and maintains the connection with that service. */ - final private class ProfileServiceConnections implements ServiceConnection, - IBinder.DeathRecipient { + private final class ProfileServiceConnections + implements ServiceConnection, IBinder.DeathRecipient { final RemoteCallbackList mProxies = - new RemoteCallbackList (); + new RemoteCallbackList(); IBinder mService; ComponentName mClassName; Intent mIntent; @@ -1076,8 +1137,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean bindService() { - if (mIntent != null && mService == null && - doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { + if (mIntent != null && mService == null && doBind(mIntent, this, 0, + UserHandle.CURRENT_OR_SELF)) { Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); msg.obj = this; mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); @@ -1090,7 +1151,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void addProxy(IBluetoothProfileServiceConnection proxy) { mProxies.register(proxy); if (mService != null) { - try{ + try { proxy.onServiceConnected(mClassName, mService); } catch (RemoteException e) { Slog.e(TAG, "Unable to connect to proxy", e); @@ -1158,7 +1219,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onServiceDisconnected(ComponentName className) { - if (mService == null) return; + if (mService == null) { + return; + } mService.unlinkToDeath(this, 0); mService = null; mClassName = null; @@ -1187,8 +1250,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void binderDied() { if (DBG) { - Slog.w(TAG, "Profile service for profile: " + mClassName - + " died."); + Slog.w(TAG, "Profile service for profile: " + mClassName + " died."); } onServiceDisconnected(mClassName); // Trigger rebind @@ -1201,12 +1263,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void sendBluetoothStateCallback(boolean isUp) { try { int n = mStateChangeCallbacks.beginBroadcast(); - if (DBG) Slog.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); - for (int i=0; i " + - BluetoothAdapter.nameForState(newState)); + Slog.d(TAG, + "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState( + prevState) + " > " + BluetoothAdapter.nameForState( + newState)); } mState = newState; bluetoothStateChangeHandler(prevState, newState); // handle error state transition case from TURNING_ON to OFF // unbind and rebind bluetooth service and enable bluetooth - if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && - (newState == BluetoothAdapter.STATE_OFF) && - (mBluetooth != null) && mEnable) { + if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState + == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) { recoverBluetoothServiceFromError(false); } - if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && - (newState == BluetoothAdapter.STATE_BLE_ON) && - (mBluetooth != null) && mEnable) { + if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState + == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) { recoverBluetoothServiceFromError(true); } // If we tried to enable BT while BT was in the process of shutting down, // wait for the BT process to fully tear down and then force a restart // here. This is a bit of a hack (b/29363429). - if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && - (newState == BluetoothAdapter.STATE_OFF)) { + if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState + == BluetoothAdapter.STATE_OFF)) { if (mEnable) { Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); waitForOnOff(false, true); - Message restartMsg = mHandler.obtainMessage( - MESSAGE_RESTART_BLUETOOTH_SERVICE); + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); } } - if (newState == BluetoothAdapter.STATE_ON || - newState == BluetoothAdapter.STATE_BLE_ON) { + if (newState == BluetoothAdapter.STATE_ON + || newState == BluetoothAdapter.STATE_BLE_ON) { // bluetooth is working, reset the counter if (mErrorRecoveryRetryCounter != 0) { Slog.w(TAG, "bluetooth is recovered from error"); @@ -1618,14 +1695,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } break; } - case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: - { + case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")"); try { mBluetoothLock.writeLock().lock(); if (msg.arg1 == SERVICE_IBLUETOOTH) { // if service is unbinded already, do nothing and return - if (mBluetooth == null) break; + if (mBluetooth == null) { + break; + } mBluetooth = null; } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { mBluetoothGatt = null; @@ -1644,33 +1722,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mEnable) { mEnable = false; // Send a Bluetooth Restart message - Message restartMsg = mHandler.obtainMessage( - MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, - SERVICE_RESTART_TIME_MS); + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, SERVICE_RESTART_TIME_MS); } sendBluetoothServiceDownCallback(); // Send BT state broadcast to update // the BT icon correctly - if ((mState == BluetoothAdapter.STATE_TURNING_ON) || - (mState == BluetoothAdapter.STATE_ON)) { + if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState + == BluetoothAdapter.STATE_ON)) { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); + BluetoothAdapter.STATE_TURNING_OFF); mState = BluetoothAdapter.STATE_TURNING_OFF; } if (mState == BluetoothAdapter.STATE_TURNING_OFF) { bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); + BluetoothAdapter.STATE_OFF); } mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; break; } - case MESSAGE_RESTART_BLUETOOTH_SERVICE: - { + case MESSAGE_RESTART_BLUETOOTH_SERVICE: { Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE"); /* Enable without persisting the setting as it doesnt change when IBluetooth @@ -1687,8 +1763,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().unlock(); break; } - case MESSAGE_TIMEOUT_UNBIND: - { + case MESSAGE_TIMEOUT_UNBIND: { Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); mBluetoothLock.writeLock().lock(); mUnbinding = false; @@ -1697,7 +1772,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_USER_SWITCHED: { - if (DBG) Slog.d(TAG, "MESSAGE_USER_SWITCHED"); + if (DBG) { + Slog.d(TAG, "MESSAGE_USER_SWITCHED"); + } mHandler.removeMessages(MESSAGE_USER_SWITCHED); /* disable and enable BT when detect a user switch */ @@ -1735,12 +1812,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { handleDisable(); // Pbap service need receive STATE_TURNING_OFF intent to close bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); + BluetoothAdapter.STATE_TURNING_OFF); boolean didDisableTimeout = !waitForOnOff(false, true); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); + BluetoothAdapter.STATE_OFF); sendBluetoothServiceDownCallback(); try { @@ -1785,14 +1862,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } case MESSAGE_USER_UNLOCKED: { - if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED"); + if (DBG) { + Slog.d(TAG, "MESSAGE_USER_UNLOCKED"); + } mHandler.removeMessages(MESSAGE_USER_SWITCHED); if (mEnable && !mBinding && (mBluetooth == null)) { // We should be connected, but we gave up for some // reason; maybe the Bluetooth service wasn't encryption // aware, so try binding again. - if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock"); + if (DBG) { + Slog.d(TAG, "Enabled but not bound; retrying after unlock"); + } handleEnable(mQuietEnable); } } @@ -1807,10 +1888,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { //Start bind timeout and bind - Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); - mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS); + Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); + mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); Intent i = new Intent(IBluetooth.class.getName()); - if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT)) { mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); } else { @@ -1820,17 +1901,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Enable bluetooth try { if (!mQuietEnable) { - if(!mBluetooth.enable()) { - Slog.e(TAG,"IBluetooth.enable() returned false"); + if (!mBluetooth.enable()) { + Slog.e(TAG, "IBluetooth.enable() returned false"); } - } - else { - if(!mBluetooth.enableNoAutoConnect()) { - Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false"); + } else { + if (!mBluetooth.enableNoAutoConnect()) { + Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false"); } } } catch (RemoteException e) { - Slog.e(TAG,"Unable to call enable()",e); + Slog.e(TAG, "Unable to call enable()", e); } } } finally { @@ -1852,13 +1932,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - if (DBG) Slog.d(TAG,"Sending off request."); + if (DBG) { + Slog.d(TAG, "Sending off request."); + } if (!mBluetooth.disable()) { - Slog.e(TAG,"IBluetooth.disable() returned false"); + Slog.e(TAG, "IBluetooth.disable() returned false"); } } } catch (RemoteException e) { - Slog.e(TAG,"Unable to call disable()",e); + Slog.e(TAG, "Unable to call disable()", e); } finally { mBluetoothLock.readLock().unlock(); } @@ -1876,15 +1958,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); - valid = (callingUser == foregroundUser) || - parentUser == foregroundUser || - callingAppId == Process.NFC_UID || - callingAppId == mSystemUiUid; + valid = (callingUser == foregroundUser) || parentUser == foregroundUser + || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; if (DBG && !valid) { - Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid - + " callingUser=" + callingUser - + " parentUser=" + parentUser - + " foregroundUser=" + foregroundUser); + Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + + callingUser + " parentUser=" + parentUser + " foregroundUser=" + + foregroundUser); } } finally { Binder.restoreCallingIdentity(callingIdentity); @@ -1893,8 +1972,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendBleStateChanged(int prevState, int newState) { - if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + - " > " + BluetoothAdapter.nameForState(newState)); + if (DBG) { + Slog.d(TAG, + "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > " + + BluetoothAdapter.nameForState(newState)); + } // Send broadcast message to everyone else Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); @@ -1909,14 +1991,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return; } // Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_BLE_ON || - newState == BluetoothAdapter.STATE_OFF) { + if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) { boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF - && newState == BluetoothAdapter.STATE_BLE_ON); + && newState == BluetoothAdapter.STATE_BLE_ON); if (newState == BluetoothAdapter.STATE_OFF) { // If Bluetooth is off, send service down event to proxy objects, and unbind - if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down"); + if (DBG) { + Slog.d(TAG, "Bluetooth is complete send Service Down"); + } sendBluetoothServiceDownCallback(); unbindAndFinish(); sendBleStateChanged(prevState, newState); @@ -1925,16 +2008,23 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (!intermediate_off) { // connect to GattService - if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode"); + if (DBG) { + Slog.d(TAG, "Bluetooth is in LE only mode"); + } if (mBluetoothGatt != null) { - if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp"); + if (DBG) { + Slog.d(TAG, "Calling BluetoothGattServiceUp"); + } onBluetoothGattServiceUp(); } else { - if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service"); - if (mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_BLUETOOTH_LE)) { + if (DBG) { + Slog.d(TAG, "Binding Bluetooth GATT service"); + } + if (mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT); } } sendBleStateChanged(prevState, newState); @@ -1942,7 +2032,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { isStandardBroadcast = false; } else if (intermediate_off) { - if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode"); + if (DBG) { + Slog.d(TAG, "Intermediate off, back to LE only mode"); + } // For LE only mode, broadcast as is sendBleStateChanged(prevState, newState); sendBluetoothStateCallback(false); // BT is OFF for general users @@ -1955,13 +2047,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothStateCallback(isUp); sendBleStateChanged(prevState, newState); - } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON || - newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) { + } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON + || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) { sendBleStateChanged(prevState, newState); isStandardBroadcast = false; - } else if (newState == BluetoothAdapter.STATE_TURNING_ON || - newState == BluetoothAdapter.STATE_TURNING_OFF) { + } else if (newState == BluetoothAdapter.STATE_TURNING_ON + || newState == BluetoothAdapter.STATE_TURNING_OFF) { sendBleStateChanged(prevState, newState); } @@ -1988,13 +2080,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { while (i < 10) { try { mBluetoothLock.readLock().lock(); - if (mBluetooth == null) break; + if (mBluetooth == null) { + break; + } if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; + if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) { + return true; + } } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; + if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) { + return true; + } } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; + if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) { + return true; + } } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); @@ -2009,7 +2109,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } i++; } - Slog.e(TAG,"waitForOnOff time out"); + Slog.e(TAG, "waitForOnOff time out"); return false; } @@ -2019,8 +2119,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendEnableMsg(boolean quietMode, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, - quietMode ? 1 : 0, 0)); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); addActiveLog(packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } @@ -2035,15 +2134,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void addCrashLog() { - synchronized (mCrashTimestamps) { - if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) mCrashTimestamps.removeFirst(); - mCrashTimestamps.add(System.currentTimeMillis()); - mCrashes++; - } + synchronized (mCrashTimestamps) { + if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) { + mCrashTimestamps.removeFirst(); + } + mCrashTimestamps.add(System.currentTimeMillis()); + mCrashes++; + } } private void recoverBluetoothServiceFromError(boolean clearBle) { - Slog.e(TAG,"recoverBluetoothServiceFromError"); + Slog.e(TAG, "recoverBluetoothServiceFromError"); try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { @@ -2082,15 +2183,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_OFF; if (clearBle) { - clearBleApps(); + clearBleApps(); } mEnable = false; if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { // Send a Bluetooth Restart message to reenable bluetooth - Message restartMsg = mHandler.obtainMessage( - MESSAGE_RESTART_BLUETOOTH_SERVICE); + Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); } else { // todo: notify user to power down and power up phone to make bluetooth work. @@ -2118,9 +2218,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) { final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); - final int newState = bluetoothSharingDisallowed - ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED - : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + final int newState = + bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED + : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; try { final IPackageManager imp = AppGlobals.getPackageManager(); imp.setComponentEnabledSetting(oppLauncherComponent, newState, @@ -2132,7 +2232,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; + if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) { + return; + } String errorMsg = null; boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); @@ -2145,11 +2247,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" name: " + mName); if (mEnable) { long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime; - String onDurationString = String.format("%02d:%02d:%02d.%03d", - (int)(onDuration / (1000 * 60 * 60)), - (int)((onDuration / (1000 * 60)) % 60), - (int)((onDuration / 1000) % 60), - (int)(onDuration % 1000)); + String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d", + (int) (onDuration / (1000 * 60 * 60)), + (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60), + (int) (onDuration % 1000)); writer.println(" time since enabled: " + onDurationString); } @@ -2162,14 +2263,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); - if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); + writer.println( + "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); + if (mCrashes == CRASH_LOG_MAX_SIZE) { + writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); + } for (Long time : mCrashTimestamps) { - writer.println(" " + timeToLog(time.longValue())); + writer.println(" " + timeToLog(time)); } - writer.println("\n" + mBleApps.size() + " BLE app" + - (mBleApps.size() == 1 ? "" : "s") + "registered"); + writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s") + + "registered"); for (ClientDeathRecipient app : mBleApps.values()) { writer.println(" " + app.getPackageName()); } @@ -2194,7 +2298,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format - if (protoOut) return; + if (protoOut) { + return; + } writer.println(errorMsg); } } -- GitLab From eee7fd31e74da3b898ca019b71436ce818eb7817 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 4 Dec 2017 10:31:30 -0800 Subject: [PATCH 0864/1408] Bluetooth HID Device: format code, fix docstring, hide unplug() * Reformat code with google-java-format * Add the note that When an application is registered, the HID Host service will be disabled until it is unregistered * Fix the links in docstring * Add @hide to unplug(); this is not a public API Bug: 63384609 Test: make Change-Id: I5dfcaab58b02c19e5745461c16602064a0ad8b83 --- .../android/bluetooth/BluetoothHidDevice.java | 177 ++++++++---------- .../BluetoothHidDeviceAppQosSettings.java | 39 ++-- .../BluetoothHidDeviceAppSdpSettings.java | 25 ++- .../bluetooth/BluetoothHidDeviceCallback.java | 73 ++++---- 4 files changed, 148 insertions(+), 166 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index bfee27974f5..4a0048673c2 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -31,36 +31,33 @@ import java.util.Arrays; import java.util.List; /** - * Provides the public APIs to control the Bluetooth HID Device - * profile. + * Provides the public APIs to control the Bluetooth HID Device profile. * - * BluetoothHidDevice is a proxy object for controlling the Bluetooth HID - * Device Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHidDevice proxy object. + *

        BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. + * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. * - * {@hide} + *

        {@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** - * Intent used to broadcast the change in connection state of the Input - * Host profile. + * Intent used to broadcast the change in connection state of the Input Host profile. * *

        This intent will have 3 extras: + * *

          - *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • - *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • - *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        • {@link #EXTRA_STATE} - The current state of the profile. + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. *
        * - *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link + * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link + * #STATE_DISCONNECTING}. * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = @@ -69,9 +66,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Constants representing device subclass. * - * @see #registerApp - * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; @@ -110,8 +106,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { public static final byte ERROR_RSP_UNKNOWN = (byte) 14; /** - * Constants representing protocol mode used set by host. Default is always - * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. + * Constants representing protocol mode used set by host. Default is always {@link + * #PROTOCOL_REPORT_MODE} unless notified otherwise. * * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte) */ @@ -126,8 +122,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { private BluetoothAdapter mAdapter; - private static class BluetoothHidDeviceCallbackWrapper extends - IBluetoothHidDeviceCallback.Stub { + private static class BluetoothHidDeviceCallbackWrapper + extends IBluetoothHidDeviceCallback.Stub { private BluetoothHidDeviceCallback mCallback; @@ -184,13 +180,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { doBind(); } } catch (IllegalStateException e) { - Log.e(TAG, - "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); + Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", e); } catch (SecurityException e) { - Log.e(TAG, - "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); + Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " + + "service: ", e); } } else { Log.d(TAG, "Unbinding service..."); @@ -200,23 +194,25 @@ public final class BluetoothHidDevice implements BluetoothProfile { } }; - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, - BluetoothHidDevice.this); - } - } - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } - } - }; + private final ServiceConnection mConnection = + new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected()"); + mService = IBluetoothHidDevice.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected( + BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected()"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + } + } + }; BluetoothHidDevice(Context context, ServiceListener listener) { Log.v(TAG, "BluetoothHidDevice"); @@ -280,9 +276,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { mServiceListener = null; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public List getConnectedDevices() { Log.v(TAG, "getConnectedDevices()"); @@ -301,9 +295,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { return new ArrayList(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public List getDevicesMatchingConnectionStates(int[] states) { Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); @@ -322,9 +314,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { return new ArrayList(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { Log.v(TAG, "getConnectionState(): device=" + device); @@ -344,33 +334,31 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Registers application to be used for HID device. Connections to HID - * Device are only possible when application is registered. Only one - * application can be registered at time. When no longer used, application - * should be unregistered using - * {@link #unregisterApp()}. - * The registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related - * to the return value of this method. + * Registers application to be used for HID device. Connections to HID Device are only possible + * when application is registered. Only one application can be registered at one time. When an + * application is registered, the HID Host service will be disabled until it is unregistered. + * When no longer used, application should be unregistered using {@link #unregisterApp()}. The + * registration status should be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to + * the return value of this method. * - * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. - * The HID Device SDP record is required. - * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. - * The Incoming QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. - * The Outgoing QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID + * Device SDP record is required. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The + * Incoming QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The + * Outgoing QoS Settings is not required. Use null or default + * BluetoothHidDeviceAppQosSettings.Builder for default values. * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be - * sent. - * The BluetoothHidDeviceCallback object is required. + * sent. The BluetoothHidDeviceCallback object is required. * @return true if the command is successfully sent; otherwise false. */ public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, BluetoothHidDeviceCallback callback) { Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos - + " callback=" + callback); + + " callback=" + callback); boolean result = false; @@ -395,14 +383,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Unregisters application. Active connection will be disconnected and no - * new connections will be allowed until registered again using - * {@link #registerApp + * Unregisters application. Active connection will be disconnected and no new connections will + * be allowed until registered again using {@link #registerApp * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} - * The registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related - * to the return value of this method. + * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should + * be tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to + * the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ @@ -429,7 +416,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Sends report to remote host using interrupt channel. * * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in - * descriptor. + * descriptor. * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ @@ -451,8 +438,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Sends report to remote host as reply for GET_REPORT request from - * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. + * Sends report to remote host as reply for GET_REPORT request from {@link + * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. * * @param type Report Type, as in request. * @param id Report Id, as in request. @@ -479,8 +466,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Sends error handshake message as reply for invalid SET_REPORT request - * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. + * Sends error handshake message as reply for invalid SET_REPORT request from {@link + * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. @@ -508,6 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Sends Virtual Cable Unplug to currently connected host. * * @return + * {@hide} */ public boolean unplug(BluetoothDevice device) { Log.v(TAG, "unplug(): device=" + device); @@ -529,11 +517,11 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Initiates connection to host which is currently paired with this device. - * If the application is not registered, #connect(BluetoothDevice) will fail. - * The connection state should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related - * to the return value of this method. + * Initiates connection to host which is currently paired with this device. If the application + * is not registered, #connect(BluetoothDevice) will fail. The connection state should be + * tracked by the application by handling callback from + * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to + * the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ @@ -557,10 +545,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { } /** - * Disconnects from currently connected host. - * The connection state should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related - * to the return value of this method. + * Disconnects from currently connected host. The connection state should be tracked by the + * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged. + * The connection state is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 881ae98d988..4609d52df0c 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -20,15 +20,14 @@ import android.os.Parcel; import android.os.Parcelable; /** - * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device - * application. + * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application. * - * The BluetoothHidDevice framework will update the L2CAP QoS settings for the - * app during registration. + *

        The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during + * registration. * - * {@see BluetoothHidDevice} + *

        {@see BluetoothHidDevice} * - * {@hide} + *

        {@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,13 +45,12 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { public static final int MAX = (int) 0xffffffff; /** - * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. - * The QoS Settings is optional. - * Recommended to use BluetoothHidDeviceAppQosSettings.Builder. - * {@see - * https://www.bluetooth.com/specifications/profiles-overview - * - * Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D } + * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see + * https://www.bluetooth.com/specifications/profiles-overview Bluetooth HID Specfication + * v1.1.1 Section 5.2 and Appendix D } + * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate * @param tokenBucketSize L2CAP token bucket size @@ -123,13 +121,11 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** @return an int array representation of this instance */ public int[] toArray() { return new int[] { - serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation + serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation }; } - /** - * A helper to build the BluetoothHidDeviceAppQosSettings object. - */ + /** A helper to build the BluetoothHidDeviceAppQosSettings object. */ public static class Builder { // Optional parameters - initialized to default values private int mServiceType = SERVICE_BEST_EFFORT; @@ -141,8 +137,9 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the service type. + * * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT, - * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. + * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. * @return BluetoothHidDeviceAppQosSettings Builder with specified service type. */ public Builder serviceType(int val) { @@ -151,6 +148,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } /** * Set the token rate. + * * @param val token rate * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate. */ @@ -161,6 +159,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the bucket size. + * * @param val bucket size * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size. */ @@ -171,6 +170,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the peak bandwidth. + * * @param val peak bandwidth * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth. */ @@ -180,6 +180,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { } /** * Set the latency. + * * @param val latency * @return BluetoothHidDeviceAppQosSettings Builder with specified latency. */ @@ -190,6 +191,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Set the delay variation. + * * @param val delay variation * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation. */ @@ -200,6 +202,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Build the BluetoothHidDeviceAppQosSettings object. + * * @return BluetoothHidDeviceAppQosSettings object with current settings. */ public BluetoothHidDeviceAppQosSettings build() { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 4669637043a..2da64e5a502 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -22,16 +22,14 @@ import android.os.Parcelable; import java.util.Arrays; /** - * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth - * HID Device application. + * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application. * - * The BluetoothHidDevice framework adds the SDP record during app - * registration, so that the Android device can be discovered as a Bluetooth - * HID Device. + *

        The BluetoothHidDevice framework adds the SDP record during app registration, so that the + * Android device can be discovered as a Bluetooth HID Device. * - * {@see BluetoothHidDevice} + *

        {@see BluetoothHidDevice} * - * {@hide} + *

        {@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @@ -43,18 +41,19 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. + * * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes. * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes. * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes. - * @param subclass Subclass of this Bluetooth HID device. - * See + * @param subclass Subclass of this Bluetooth HID device. See * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2 - * @param descriptors Descriptors of this Bluetooth HID device. - * See + * @param descriptors Descriptors of this Bluetooth HID device. See * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6 Maximum length is 2048 bytes. */ - public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, - byte subclass, byte[] descriptors) { + public BluetoothHidDeviceAppSdpSettings( + String name, String description, String provider, byte subclass, byte[] descriptors) { this.name = name; this.description = description; this.provider = provider; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index dc6f9fa1abf..6ed19654b4c 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -19,46 +19,43 @@ package android.bluetooth; import android.util.Log; /** - * The template class that applications use to call callback functions on - * events from the HID host. Callback functions are wrapped in this class and - * registered to the Android system during app registration. + * The template class that applications use to call callback functions on events from the HID host. + * Callback functions are wrapped in this class and registered to the Android system during app + * registration. * - * {@see BluetoothHidDevice} + *

        {@see BluetoothHidDevice} * - * {@hide} + *

        {@hide} */ public abstract class BluetoothHidDeviceCallback { private static final String TAG = "BluetoothHidDevCallback"; /** - * Callback called when application registration state changes. Usually it's - * called due to either - * {@link BluetoothHidDevice#registerApp - * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)} - * or - * {@link BluetoothHidDevice#unregisterApp()} - * , but can be also unsolicited in case e.g. Bluetooth was turned off in - * which case application is unregistered automatically. + * Callback called when application registration state changes. Usually it's called due to + * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], + * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also + * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered + * automatically. * * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has - * Virtual Cable established with device. Only valid when application is registered, can be - * null. + * Virtual Cable established with device. Only valid when application is registered, can be + * null. * @param registered true if application is registered, false - * otherwise. + * otherwise. */ public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" - + registered); + Log.d(TAG, + "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); } /** - * Callback called when connection state with remote host was changed. - * Application can assume than Virtual Cable is established when called with - * {@link BluetoothProfile#STATE_CONNECTED} state. + * Callback called when connection state with remote host was changed. Application can assume + * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED} + * state. * * @param device {@link BluetoothDevice} object representing host device which connection state - * was changed. + * was changed. * @param state Connection state as defined in {@link BluetoothProfile}. */ public void onConnectionStateChanged(BluetoothDevice device, int state) { @@ -66,14 +63,14 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when GET_REPORT is received from remote host. Should be - * replied by application using - * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}. + * Callback called when GET_REPORT is received from remote host. Should be replied by + * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, + * byte[])}. * * @param type Requested Report Type. * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. * @param bufferSize Requested buffer size, application shall respond with at least given number - * of bytes. + * of bytes. */ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" @@ -81,9 +78,9 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when SET_REPORT is received from remote host. In case - * received data are invalid, application shall respond with - * {@link BluetoothHidDevice#reportError(BluetoothDevice, byte)}. + * Callback called when SET_REPORT is received from remote host. In case received data are + * invalid, application shall respond with {@link + * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. * * @param type Report Type. * @param id Report Id. @@ -94,10 +91,9 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when SET_PROTOCOL is received from remote host. - * Application shall use this information to send only reports valid for - * given protocol mode. By default, - * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * Callback called when SET_PROTOCOL is received from remote host. Application shall use this + * information to send only reports valid for given protocol mode. By default, {@link + * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. * * @param protocol Protocol Mode. */ @@ -106,9 +102,8 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when report data is received over interrupt channel. - * Report Type is assumed to be - * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * Callback called when report data is received over interrupt channel. Report Type is assumed + * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. * * @param reportId Report Id. * @param data Report data. @@ -118,10 +113,8 @@ public abstract class BluetoothHidDeviceCallback { } /** - * Callback called when Virtual Cable is removed. This can be either due to - * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote - * side. After this callback is received connection will be disconnected - * automatically. + * Callback called when Virtual Cable is removed. After this callback is + * received connection will be disconnected automatically. */ public void onVirtualCableUnplug(BluetoothDevice device) { Log.d(TAG, "onVirtualCableUnplug: device=" + device); -- GitLab From 5ae9ddae0efd2ef9c595a71e67b3f6c0f23fb0df Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 7 Dec 2017 12:06:21 -0800 Subject: [PATCH 0865/1408] BT: Remove dead code in BluetoothHeadset The following hidden API methods are not implemented by the stack and should be removed: - getBatteryUsageHint() - acceptIncomingConnect() - rejectIncomingConnect() The following hidden API methods are no longer needed due to lack of usage and should be removed: - enableWBS() - disableWBS() - bindResponse() Bug: 70336991 Test: make Change-Id: I75bd6735573e4cbc61b5bef8722b8badac2e9170 --- .../android/bluetooth/BluetoothHeadset.java | 138 ------------------ 1 file changed, 138 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 85550c7720a..90ae0e6313d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -669,33 +669,6 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - /** - * Get battery usage hint for Bluetooth Headset service. - * This is a monotonically increasing integer. Wraps to 0 at - * Integer.MAX_INT, and at boot. - * Current implementation returns the number of AT commands handled since - * boot. This is a good indicator for spammy headset/handsfree units that - * can keep the device awake by polling for cellular status updates. As a - * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms - * - * @param device the bluetooth headset. - * @return monotonically increasing battery usage hint, or a negative error code on error - * @hide - */ - public int getBatteryUsageHint(BluetoothDevice device) { - if (VDBG) log("getBatteryUsageHint()"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getBatteryUsageHint(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return -1; - } - /** * Indicates if current platform supports voice dialing over bluetooth SCO. * @@ -707,49 +680,6 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_sco_off_call); } - /** - * Accept the incoming connection. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - public boolean acceptIncomingConnect(BluetoothDevice device) { - if (DBG) log("acceptIncomingConnect"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.acceptIncomingConnect(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; - } - - /** - * Reject the incoming connection. - * - * @hide - */ - public boolean rejectIncomingConnect(BluetoothDevice device) { - if (DBG) log("rejectIncomingConnect"); - final IBluetoothHeadset service = mService; - if (service != null) { - try { - return service.rejectIncomingConnect(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; - } - /** * Get the current audio state of the Headset. * Note: This is an internal function and shouldn't be exposed @@ -1044,50 +974,6 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - /** - * enable WBS codec setting. - * - * @return true if successful false if there was some error such as there is no connected - * headset - * @hide - */ - public boolean enableWBS() { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.enableWBS(); - } 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; - } - - /** - * disable WBS codec settting. It set NBS codec. - * - * @return true if successful false if there was some error such as there is no connected - * headset - * @hide - */ - public boolean disableWBS() { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.disableWBS(); - } 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; - } - /** * check if in-band ringing is supported for this platform. * @@ -1099,30 +985,6 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); } - /** - * Send Headset the BIND response from AG to report change in the status of the - * HF indicators to the headset - * - * @param indId Assigned Number of the indicator (defined by SIG) - * @param indStatus possible values- false-Indicator is disabled, no value changes shall be - * sent for this indicator true-Indicator is enabled, value changes may be sent for this - * indicator - * @hide - */ - public void bindResponse(int indId, boolean indStatus) { - final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - service.bindResponse(indId, indStatus); - } 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())); - } - } - private final IBluetoothProfileServiceConnection mConnection = new IBluetoothProfileServiceConnection.Stub() { @Override -- GitLab From dbc5021a4bcb60a4c8b04fab532f78eb5abe07ae Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Fri, 8 Dec 2017 00:02:42 +0000 Subject: [PATCH 0866/1408] Stop statically including legacy-android-test Statically including legacy-android-test leads to duplicate classes which causes build time problems (with Proguard) and runtime problems on older SDK versions. This change: * Stops statically including legacy-android-test. * Adds compile time dependencies on andoid.test.base, android.test.mock and android.test.runner where necessary. * Adds to any affected package to ensure that the classes that were included by legacy-android-test are still available at runtime. That also adds a dependency on android.test.base and android.test.mock. The following change descriptions were generated automatically and so may be a little repetitive. They are provided to give the reviewer enough information to check the comments match what has actually been changed and check the reasoning behind the changes. * apct-tests/perftests/core/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in CorePerfTests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because CorePerfTests's source depends on its classes and because of these changes they are no longer present on the compilation path. * core/tests/ConnectivityManagerTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because ConnectivityManagerTest's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ConnectivityManagerTest results in duplicate classes which leads to build time and compile time issues. * core/tests/bandwidthtests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because BandwidthTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in BandwidthTests results in duplicate classes which leads to build time and compile time issues. * core/tests/bluetoothtests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because BluetoothTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in BluetoothTests results in duplicate classes which leads to build time and compile time issues. * core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in DownloadManagerTestApp results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because DownloadManagerTestApp's source depends on its classes and because of these changes they are no longer present on the compilation path. * core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ExternalSharedPermsTestApp results in duplicate classes which leads to build time and compile time issues. * core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ExternalSharedPermsBTTestApp results in duplicate classes which leads to build time and compile time issues. * core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ExternalSharedPermsDiffKeyTestApp results in duplicate classes which leads to build time and compile time issues. * core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ExternalSharedPermsFLTestApp results in duplicate classes which leads to build time and compile time issues. * core/tests/notificationtests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because NotificationStressTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in NotificationStressTests results in duplicate classes which leads to build time and compile time issues. * keystore/tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in KeystoreTests results in duplicate classes which leads to build time and compile time issues. * media/mca/tests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because CameraEffectsTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in CameraEffectsTests results in duplicate classes which leads to build time and compile time issues. * media/tests/MediaFrameworkTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because mediaframeworktest's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in mediaframeworktest results in duplicate classes which leads to build time and compile time issues. * nfc-extras/tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in NfcExtrasTests results in duplicate classes which leads to build time and compile time issues. * packages/CarrierDefaultApp/tests/unit/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because CarrierDefaultAppUnitTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in CarrierDefaultAppUnitTests results in duplicate classes which leads to build time and compile time issues. * packages/ExtServices/tests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because ExtServicesUnitTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ExtServicesUnitTests results in duplicate classes which leads to build time and compile time issues. * packages/MtpDocumentsProvider/tests/Android.mk Added 'android.test.base' and 'android.test.mock' to LOCAL_JAVA_LIBRARIES because MtpDocumentsProviderTests's source depends on their classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in MtpDocumentsProviderTests results in duplicate classes which leads to build time and compile time issues. * packages/SettingsLib/tests/integ/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because SettingsLibTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SettingsLibTests results in duplicate classes which leads to build time and compile time issues. * packages/SettingsProvider/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SettingsProvider results in duplicate classes which leads to build time and compile time issues. * packages/SettingsProvider/AndroidManifest.xml Add uses-library for android.test.runner because otherwise this change would change the set of files available to SettingsProvider at runtime. * packages/Shell/tests/Android.mk Added 'android.test.base' and 'android.test.mock' to LOCAL_JAVA_LIBRARIES because ShellTests's source depends on their classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ShellTests results in duplicate classes which leads to build time and compile time issues. * packages/SystemUI/shared/tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SystemUISharedLibTests results in duplicate classes which leads to build time and compile time issues. * packages/SystemUI/tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SystemUITests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because SystemUITests's source depends on its classes and because of these changes they are no longer present on the compilation path. * packages/WAPPushManager/tests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because WAPPushManagerTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in WAPPushManagerTests results in duplicate classes which leads to build time and compile time issues. * sax/tests/saxtests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because FrameworksSaxTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in FrameworksSaxTests results in duplicate classes which leads to build time and compile time issues. * tests/BrowserPowerTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because BrowserPowerTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in BrowserPowerTests results in duplicate classes which leads to build time and compile time issues. * tests/CanvasCompare/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because CanvasCompare's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in CanvasCompare results in duplicate classes which leads to build time and compile time issues. * tests/CoreTests/android/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because CoreTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in CoreTests results in duplicate classes which leads to build time and compile time issues. * tests/DataIdleTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because DataIdleTest's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in DataIdleTest results in duplicate classes which leads to build time and compile time issues. * tests/FrameworkPerf/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because FrameworkPerf's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in FrameworkPerf results in duplicate classes which leads to build time and compile time issues. * tests/HierarchyViewerTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because HierarchyViewerTest's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in HierarchyViewerTest results in duplicate classes which leads to build time and compile time issues. * tests/ImfTest/tests/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because ImfTestTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in ImfTestTests results in duplicate classes which leads to build time and compile time issues. * tests/Internal/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in InternalTests results in duplicate classes which leads to build time and compile time issues. * tests/MemoryUsage/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because MemoryUsage's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in MemoryUsage results in duplicate classes which leads to build time and compile time issues. * tests/NetworkSecurityConfigTest/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because NetworkSecurityConfigTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in NetworkSecurityConfigTests results in duplicate classes which leads to build time and compile time issues. * tests/SoundTriggerTests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SoundTriggerTests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because SoundTriggerTests's source depends on its classes and because of these changes they are no longer present on the compilation path. * tests/SurfaceComposition/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in SurfaceComposition results in duplicate classes which leads to build time and compile time issues. Added 'android.test.runner.stubs' to LOCAL_JAVA_LIBRARIES because SurfaceComposition's source depends on its classes and because of these changes they are no longer present on the compilation path. * tests/TtsTests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in TtsTests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because TtsTests's source depends on its classes and because of these changes they are no longer present on the compilation path. * tests/WindowAnimationJank/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in WindowAnimationJank results in duplicate classes which leads to build time and compile time issues. * tests/permission/Android.mk Added 'android.test.base' to LOCAL_JAVA_LIBRARIES because FrameworkPermissionTests's source depends on its classes and because of these changes they are no longer present on the compilation path. Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in FrameworkPermissionTests results in duplicate classes which leads to build time and compile time issues. * tests/testables/tests/Android.mk Removed legacy-android-test from LOCAL_STATIC_JAVA_LIBRARIES because statically including the classes in TestablesTests results in duplicate classes which leads to build time and compile time issues. Added 'android.test.base' and 'android.test.mock' to LOCAL_JAVA_LIBRARIES because TestablesTests's source depends on their classes and because of these changes they are no longer present on the compilation path. Bug: 30188076 Test: make checkbuild Change-Id: Iacfc939c97415314366ed61c5f3b7aa1a40f0ec9 --- framework/tests/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk index f53419ab53c..744e5b04974 100644 --- a/framework/tests/Android.mk +++ b/framework/tests/Android.mk @@ -8,8 +8,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test +LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base +LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BluetoothTests LOCAL_CERTIFICATE := platform -- GitLab From d2a40d98233d253a9d67f7447b67b13774dfcd6e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 7 Dec 2017 22:56:03 -0800 Subject: [PATCH 0867/1408] Fix bad type for txPower in PeriodicAdvertisingReport serialization Bug: 69634768 Test: compilation Change-Id: Icedfbaf1ba933637e935ada0fd98aea42c73f2b2 --- .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 55c3a730a83..73a2e74de5b 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -71,7 +71,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mSyncHandle); - dest.writeLong(mTxPower); + dest.writeInt(mTxPower); dest.writeInt(mRssi); dest.writeInt(mDataStatus); if (mData != null) { -- GitLab From 6fa8f5a5e2d566b2d3d3f7de6b79c4137f76ec9a Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 8 Dec 2017 12:21:37 -0800 Subject: [PATCH 0868/1408] Bluetooth: Fix boolean logic in quiet mode enable * Change If97c454a8e5aff34c4f8550f7ade3da413a200b7 introduced a bug that flipped the logic of quiet mode enable in the handler for MESSAGE_BLUETOOTH_SERVICE_CONNECTED, causing quiet mode to be enabled when toggling from Settings and system UI. Bug: 70395489 Test: make, toggle bluetooth on/off Change-Id: I405fb462783df0d52d9ce84d2ebe959e56a1aa30 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 763a4e49459..d9713a517a9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1628,7 +1628,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (mQuietEnable) { + if (!mQuietEnable) { if (!mBluetooth.enable()) { Slog.e(TAG, "IBluetooth.enable() returned false"); } -- GitLab From d5e696d23f3a84f6a23f65eb9d693c5a12f6a26c Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 8 Dec 2017 12:21:37 -0800 Subject: [PATCH 0869/1408] Bluetooth: Fix boolean logic in quiet mode enable * Change If97c454a8e5aff34c4f8550f7ade3da413a200b7 introduced a bug that flipped the logic of quiet mode enable in the handler for MESSAGE_BLUETOOTH_SERVICE_CONNECTED, causing quiet mode to be enabled when toggling from Settings and system UI. Bug: 70395489 Test: make, toggle bluetooth on/off Change-Id: I405fb462783df0d52d9ce84d2ebe959e56a1aa30 (cherry picked from commit a6e031c9f5a39b40452d29c1ba91b02771960210) --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 763a4e49459..d9713a517a9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1628,7 +1628,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (mQuietEnable) { + if (!mQuietEnable) { if (!mBluetooth.enable()) { Slog.e(TAG, "IBluetooth.enable() returned false"); } -- GitLab From 93df94095ff3cb657ad281290b227d84b6d2224a Mon Sep 17 00:00:00 2001 From: Jack He Date: Mon, 11 Dec 2017 13:11:20 -0800 Subject: [PATCH 0870/1408] Bluetooth: Add convenience method to convert connection state to string * Add method to convert the following state values to string - BluetoothHeadset.STATE_DISCONNECTED - BluetoothHeadset.STATE_CONNECTING - BluetoothHeadset.STATE_CONNECTED - BluetoothHeadset.STATE_DISCONNECTING Test: make Change-Id: Iaa5b6e35d3214ded8edbe29d626e0869651417d1 --- .../android/bluetooth/BluetoothProfile.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 46a230b5060..ebbc710922c 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -254,4 +254,28 @@ public interface BluetoothProfile { */ public void onServiceDisconnected(int profile); } + + /** + * Convert an integer value of connection state into human readable string + * + * @param connectionState - One of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, or {@link #STATE_DISCONNECTED} + * @return a string representation of the connection state, STATE_UNKNOWN if the state + * is not defined + * @hide + */ + static String getConnectionStateName(int connectionState) { + switch (connectionState) { + case STATE_DISCONNECTED: + return "STATE_DISCONNECTED"; + case STATE_CONNECTING: + return "STATE_CONNECTING"; + case STATE_CONNECTED: + return "STATE_CONNECTED"; + case STATE_DISCONNECTING: + return "STATE_DISCONNECTING"; + default: + return "STATE_UNKNOWN"; + } + } } -- GitLab From ba84dbb0fa61295eb2ce077257ae849ef0912d0a Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 8 Dec 2017 16:05:55 -0800 Subject: [PATCH 0871/1408] Make BluetoothPbap implement BluetoothProfile (1/3) * BluetoothPbap will implement BluetoothProfile to be consistent with other profiles * Changed the method names to match the BluetoothProfile method signatures * Removed doesClassMatchSink() as it is never used * Disable default DEBUG logs Test: test with carkits Bug: 69417672 Change-Id: I0766188f77468808d7505780c781f5fd8608af60 --- .../java/android/bluetooth/BluetoothPbap.java | 153 ++++++++---------- 1 file changed, 70 insertions(+), 83 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index a1a9347df69..794435457f2 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -25,6 +25,10 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * The Android Bluetooth API is not finalized, and *will* change. Use at your * own risk. @@ -48,11 +52,10 @@ import android.util.Log; * * @hide */ -public class BluetoothPbap { +public class BluetoothPbap implements BluetoothProfile { private static final String TAG = "BluetoothPbap"; - private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean DBG = false; /** * Intent used to broadcast the change in connection state of the PBAP @@ -111,9 +114,9 @@ public class BluetoothPbap { private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + log("onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); + log("Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -126,7 +129,7 @@ public class BluetoothPbap { synchronized (mConnection) { try { if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); + log("Binding service..."); doBind(); } } catch (Exception re) { @@ -205,47 +208,60 @@ public class BluetoothPbap { } /** - * Get the current state of the BluetoothPbap service. - * - * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED} - * if this proxy object is currently not connected to the Pbap service. + * {@inheritDoc} */ - public int getState() { - if (VDBG) log("getState()"); + @Override + public List getConnectedDevices() { + log("getConnectedDevices()"); final IBluetoothPbap service = mService; - if (service != null) { - try { - return service.getState(); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + try { + return service.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + log("getConnectionState: device=" + device); + final IBluetoothPbap service = mService; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + try { + return service.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); } return BluetoothProfile.STATE_DISCONNECTED; } /** - * Get the currently connected remote Bluetooth device (PCE). - * - * @return The remote Bluetooth device, or null if not in connected or connecting state, or if - * this proxy object is not connected to the Pbap service. + * {@inheritDoc} */ - public BluetoothDevice getClient() { - if (VDBG) log("getClient()"); + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); final IBluetoothPbap service = mService; - if (service != null) { - try { - return service.getClient(); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + try { + return service.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); } - return null; + return new ArrayList(); } /** @@ -253,20 +269,9 @@ public class BluetoothPbap { * include connecting). Returns false if not connected, or if this proxy * object is not currently connected to the Pbap service. */ + // TODO: This is currently being used by SettingsLib and internal app. public boolean isConnected(BluetoothDevice device) { - if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothPbap service = mService; - if (service != null) { - try { - return service.isConnected(device); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } - return false; + return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED; } /** @@ -274,47 +279,27 @@ public class BluetoothPbap { * it may soon be made asynchronous. Returns false if this proxy object is * not currently connected to the Pbap service. */ - public boolean disconnect() { - if (DBG) log("disconnect()"); + // TODO: This is currently being used by SettingsLib and will be used in the future. + // TODO: Must specify target device. Implement this in the service. + public boolean disconnect(BluetoothDevice device) { + log("disconnect()"); final IBluetoothPbap service = mService; - if (service != null) { - try { - service.disconnect(); - return true; - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); + return false; } - return false; - } - - /** - * Check class bits for possible PBAP support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support PBAP. It is not accurate for all - * devices. It tries to err on the side of false positives. - * - * @return True if this device might support PBAP. - */ - public static boolean doesClassMatchSink(BluetoothClass btClass) { - // TODO optimize the rule - switch (btClass.getDeviceClass()) { - case BluetoothClass.Device.COMPUTER_DESKTOP: - case BluetoothClass.Device.COMPUTER_LAPTOP: - case BluetoothClass.Device.COMPUTER_SERVER: - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - return true; - default: - return false; + try { + service.disconnect(device); + return true; + } catch (RemoteException e) { + Log.e(TAG, e.toString()); } + return false; } private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); + log("Proxy object connected"); mService = IBluetoothPbap.Stub.asInterface(service); if (mServiceListener != null) { mServiceListener.onServiceConnected(BluetoothPbap.this); @@ -322,7 +307,7 @@ public class BluetoothPbap { } public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); + log("Proxy object disconnected"); mService = null; if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); @@ -331,6 +316,8 @@ public class BluetoothPbap { }; private static void log(String msg) { - Log.d(TAG, msg); + if (DBG) { + Log.d(TAG, msg); + } } } -- GitLab From e0a0509b6a4e9259dc525a28e46e7433b1adcd87 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 13 Dec 2017 20:05:05 -0700 Subject: [PATCH 0872/1408] Add more IntDef prefixes for auto-documenting. Test: builds, boots Bug: 70177949 Exempt-From-Owner-Approval: annotation-only changes Change-Id: I76dde6054e06f52240bd4b1a0f196dcb74623608 --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c7be0f36ece..3290d57f13a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -156,7 +156,7 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.extra.PREVIOUS_STATE"; /** @hide */ - @IntDef({ + @IntDef(prefix = { "STATE_" }, value = { STATE_OFF, STATE_TURNING_ON, STATE_ON, @@ -357,7 +357,11 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; /** @hide */ - @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE}) + @IntDef(prefix = { "SCAN_" }, value = { + SCAN_MODE_NONE, + SCAN_MODE_CONNECTABLE, + SCAN_MODE_CONNECTABLE_DISCOVERABLE + }) @Retention(RetentionPolicy.SOURCE) public @interface ScanMode {} -- GitLab From c4551d42cd839c19d009eef4c56f12ead00c415a Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 6 Dec 2017 16:20:22 -0800 Subject: [PATCH 0873/1408] Publish Bluetooth HID Device Profile Service API Enable the BluetoothHidDevice API in framework. Bug: 63384609 Test: SL4A HID test; test with apps using BluetoothHidDevice Change-Id: I52ca4674f11179f865bdff22e0289dfe893c40f5 --- framework/java/android/bluetooth/BluetoothHidDevice.java | 2 -- .../bluetooth/BluetoothHidDeviceAppQosSettings.java | 8 ++------ .../bluetooth/BluetoothHidDeviceAppSdpSettings.java | 2 -- .../android/bluetooth/BluetoothHidDeviceCallback.java | 2 -- framework/java/android/bluetooth/BluetoothProfile.java | 2 -- 5 files changed, 2 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 4a0048673c2..f38e462eab4 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -35,8 +35,6 @@ import java.util.List; * *

        BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - * - *

        {@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 4609d52df0c..c05df2d23e4 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -26,8 +26,6 @@ import android.os.Parcelable; * registration. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,10 +44,8 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see - * https://www.bluetooth.com/specifications/profiles-overview Bluetooth HID Specfication - * v1.1.1 Section 5.2 and Appendix D } + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2da64e5a502..562c559eddc 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -28,8 +28,6 @@ import java.util.Arrays; * Android device can be discovered as a Bluetooth HID Device. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 6ed19654b4c..bd19955b4e6 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -24,8 +24,6 @@ import android.util.Log; * registration. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public abstract class BluetoothHidDeviceCallback { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 46a230b5060..5fdde020e87 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -153,8 +153,6 @@ public interface BluetoothProfile { /** * HID Device - * - * @hide */ public static final int HID_DEVICE = 19; -- GitLab From 362004ef30a1cd67d6866d7ee0373a169ca59ac9 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 6 Dec 2017 16:20:22 -0800 Subject: [PATCH 0874/1408] Publish Bluetooth HID Device Profile Service API Enable the BluetoothHidDevice API in framework. Bug: 63384609 Test: SL4A HID test; test with apps using BluetoothHidDevice Merged-In: I52ca4674f11179f865bdff22e0289dfe893c40f5 Change-Id: I52ca4674f11179f865bdff22e0289dfe893c40f5 (cherry picked from commit c4551d42cd839c19d009eef4c56f12ead00c415a) --- framework/java/android/bluetooth/BluetoothHidDevice.java | 4 +--- .../bluetooth/BluetoothHidDeviceAppQosSettings.java | 8 ++------ .../bluetooth/BluetoothHidDeviceAppSdpSettings.java | 2 -- .../android/bluetooth/BluetoothHidDeviceCallback.java | 2 -- framework/java/android/bluetooth/BluetoothProfile.java | 2 -- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 330a0bfad32..f38e462eab4 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -35,8 +35,6 @@ import java.util.List; * *

        BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - * - *

        {@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { @@ -79,7 +77,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 4609d52df0c..c05df2d23e4 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -26,8 +26,6 @@ import android.os.Parcelable; * registration. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,10 +44,8 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see - * https://www.bluetooth.com/specifications/profiles-overview Bluetooth HID Specfication - * v1.1.1 Section 5.2 and Appendix D } + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2da64e5a502..562c559eddc 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -28,8 +28,6 @@ import java.util.Arrays; * Android device can be discovered as a Bluetooth HID Device. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index 6ed19654b4c..bd19955b4e6 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -24,8 +24,6 @@ import android.util.Log; * registration. * *

        {@see BluetoothHidDevice} - * - *

        {@hide} */ public abstract class BluetoothHidDeviceCallback { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index ebbc710922c..df2028a5535 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -153,8 +153,6 @@ public interface BluetoothProfile { /** * HID Device - * - * @hide */ public static final int HID_DEVICE = 19; -- GitLab From 73f2041559f77fda5fb2a3c4b51b093f57e207b5 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 18 Dec 2017 15:18:39 -0800 Subject: [PATCH 0875/1408] BluetoothHidDevice: rename onIntrData to onInterruptData (1/5) Bug: 63384609 Test: make Change-Id: I3b55f7383d2a84162d681ebf3740ddc9e8a55bbb --- framework/java/android/bluetooth/BluetoothHidDevice.java | 6 +++--- .../java/android/bluetooth/BluetoothHidDeviceCallback.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index f38e462eab4..2fab305ba8b 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -85,7 +85,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int) * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see BluetoothHidDeviceCallback#onIntrData(BluetoothDevice, byte, byte[]) + * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[]) */ public static final byte REPORT_TYPE_INPUT = (byte) 1; public static final byte REPORT_TYPE_OUTPUT = (byte) 2; @@ -155,8 +155,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } @Override - public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { - mCallback.onIntrData(device, reportId, data); + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + mCallback.onInterruptData(device, reportId, data); } @Override diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index bd19955b4e6..e71b00f2349 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -106,8 +106,8 @@ public abstract class BluetoothHidDeviceCallback { * @param reportId Report Id. * @param data Report data. */ - public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { - Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId); + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); } /** -- GitLab From 35258da3867dd10d4c3e2f2ab5c99cbbac206d44 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 18 Dec 2017 15:18:39 -0800 Subject: [PATCH 0876/1408] BluetoothHidDevice: rename onIntrData to onInterruptData (1/5) Bug: 63384609 Test: make Merged-In: I3b55f7383d2a84162d681ebf3740ddc9e8a55bbb Change-Id: I3b55f7383d2a84162d681ebf3740ddc9e8a55bbb (cherry picked from commit 73f2041559f77fda5fb2a3c4b51b093f57e207b5) --- framework/java/android/bluetooth/BluetoothHidDevice.java | 6 +++--- .../java/android/bluetooth/BluetoothHidDeviceCallback.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index f38e462eab4..2fab305ba8b 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -85,7 +85,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int) * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see BluetoothHidDeviceCallback#onIntrData(BluetoothDevice, byte, byte[]) + * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[]) */ public static final byte REPORT_TYPE_INPUT = (byte) 1; public static final byte REPORT_TYPE_OUTPUT = (byte) 2; @@ -155,8 +155,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } @Override - public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { - mCallback.onIntrData(device, reportId, data); + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + mCallback.onInterruptData(device, reportId, data); } @Override diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java index bd19955b4e6..e71b00f2349 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -106,8 +106,8 @@ public abstract class BluetoothHidDeviceCallback { * @param reportId Report Id. * @param data Report data. */ - public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) { - Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId); + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); } /** -- GitLab From 1354d26992e127a147e3383c129d9ae9173e78d9 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 28 Dec 2017 15:48:07 +0000 Subject: [PATCH 0877/1408] BatteryStats: Add WorkChain support for WiFi events. The changes are straightforward, the only change outside of BatteryStats is to use the new WorkSource.isEmpty API to account for WorkChains in a given WorkSource. Bug: 62390666 Test: BatteryStatsBackgroundStatsTest, BatteryStatsNoteTest, WifiLockManagerTest Change-Id: I1dff43b6f2a09877e3af4442bfe8a8fd80b1ba74 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index a189e271379..347fc4df4eb 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -387,7 +387,7 @@ public final class BluetoothLeScanner { if (mScannerId > 0) { mLeScanClients.put(mScanCallback, this); } else { - // Registration timed out or got exception, reset scannerId to -1 so no + // Registration timed out or got exception, reset RscannerId to -1 so no // subsequent operations can proceed. if (mScannerId == 0) mScannerId = -1; -- GitLab From 2b398cc5f87bb70d0295f83e7dde2c434299ed2c Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Tue, 19 Dec 2017 13:20:06 -0800 Subject: [PATCH 0878/1408] Added internal API to get/set the A2DP Active Device 1. Call BluetoothA2dp.setActiveDevice(BluetoothDevice device) to set a connected A2DP device as active. The value of "device" could be null to clear the active device and stop streaming audio to a Bluetooth device. 2. Listen for BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED intent that will contain the latest active device (in EXTRA_DEVICE field). If the active device could not be changed, the EXTRA_DEVICE field could be null. 3. If setActiveDevice() is NOT in-progress, BluetoothA2dp.getActiveDevice() can be used. If setActiveDevice() is in-progress, the result is undefined. 4. BluetoothA2dp.setActiveDevice() could be called by some other parts of the system, so interested parties should always listen for BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED intents and prepared for active device updates. Bug: 71555243 Test: Manual Change-Id: I661b6882e8e6b437db50210aec1dd12a10199969 --- .../java/android/bluetooth/BluetoothA2dp.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 7841b83cf92..35a21a4eaf9 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -102,6 +103,24 @@ public final class BluetoothA2dp implements BluetoothProfile { public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; + /** + * Intent used to broadcast the selection of a connected device as active. + * + *

        This intent will have one extra: + *

          + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; + /** * Intent used to broadcast the change in the Audio Codec state of the * A2DP Source profile. @@ -424,6 +443,75 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, A2DP audio streaming + * is to the active A2DP Sink device. If a remote device is not + * connected, it cannot be selected as active. + * + *

        This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + return mService.setActiveDevice(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the connected device that is active. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @Nullable + public BluetoothDevice getActiveDevice() { + if (VDBG) log("getActiveDevice()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getActiveDevice(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Set priority of the profile * -- GitLab From a117cb378778a5b123fc1be76f887ee6ed5428ec Mon Sep 17 00:00:00 2001 From: Selim Gurun Date: Tue, 17 Oct 2017 17:01:38 -0700 Subject: [PATCH 0879/1408] Add SystemApis annotations There are some number of places where bluetooth APIs are used via reflection from GMSCore. Add proper annotations. Bug: 67052734 Test: Manual - and using make update-api Change-Id: Ib6e3aa1ff5b6f9cdc78367f9be13ed00542d6f65 (cherry picked from commit d9bf8fb5edc93a9f8154943af8ac36e9447df9ca) --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 ++ framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++++++ framework/java/android/bluetooth/BluetoothHeadset.java | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 158aebb478a..c7be0f36ece 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2425,6 +2425,8 @@ public final class BluetoothAdapter { * * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect() { if (isEnabled()) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d982bb7ffb4..ad7a93cd6bb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1098,6 +1098,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess() { final IBluetooth service = sService; if (service == null) { @@ -1125,6 +1127,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond() { final IBluetooth service = sService; if (service == null) { @@ -1174,6 +1178,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected() { final IBluetooth service = sService; if (service == null) { @@ -1197,6 +1202,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted() { final IBluetooth service = sService; if (service == null) { @@ -1444,6 +1450,8 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 90ae0e6313d..838d3153d54 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -416,6 +418,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -456,6 +460,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -543,6 +549,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); final IBluetoothHeadset service = mService; -- GitLab From 889d234e8cb0d82f40f532ff95a4ef94f304d670 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 3 Jan 2018 12:13:26 -0800 Subject: [PATCH 0880/1408] HFP: Add APIs for set and get active device (1/3) 1. Call BluetoothHeadset.setActiveDevice(BluetoothDevice device) to set a connected HFP/HSP device as active. 2. Listen for BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED intent that will contain the latest active device (in EXTRA_DEVICE field). If the active device could not be changed, the EXTRA_DEVICE field could be null. 3. If setActiveDevice() is NOT in-progress, BluetoothA2dp.getActiveDevice() can be used. If setActiveDevice() is in-progress, the result is undefined. 4. BluetoothHeadset.setActiveDevice() could be called by some other parts of the system, so interested parties should always listen for BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED intents and prepared for active device updates. Bug: 68951996 Test: manual Change-Id: I22ca639a04fed7bf17df59c405ddeda90dafb8ff --- .../android/bluetooth/BluetoothHeadset.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 838d3153d54..55a6b4c6b4d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -93,6 +94,23 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; + /** + * Intent used to broadcast the selection of a connected device as active. + * + *

        This intent will have one extra: + *

          + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; /** * Intent used to broadcast that the headset has posted a @@ -982,6 +1000,76 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, in HFP and HSP profiles, + * it is the device used for phone call audio. If a remote device is not + * connected, it cannot be selected as active. + * + *

        This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device, could be null if phone call audio should not be + * streamed to a headset + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "setActiveDevice: " + device); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && (device == null || isValidDevice(device))) { + try { + return service.setActiveDevice(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } + + /** + * Get the connected device that is active. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active. + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + public BluetoothDevice getActiveDevice() { + if (VDBG) { + Log.d(TAG, "getActiveDevice"); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { + try { + return service.getActiveDevice(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } + /** * check if in-band ringing is supported for this platform. * -- GitLab From 21fc9ab28b729a5904708d39b49eb8cb076119f3 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 3 Jan 2018 12:13:26 -0800 Subject: [PATCH 0881/1408] HFP: Add APIs for set and get active device (1/3) 1. Call BluetoothHeadset.setActiveDevice(BluetoothDevice device) to set a connected HFP/HSP device as active. 2. Listen for BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED intent that will contain the latest active device (in EXTRA_DEVICE field). If the active device could not be changed, the EXTRA_DEVICE field could be null. 3. If setActiveDevice() is NOT in-progress, BluetoothA2dp.getActiveDevice() can be used. If setActiveDevice() is in-progress, the result is undefined. 4. BluetoothHeadset.setActiveDevice() could be called by some other parts of the system, so interested parties should always listen for BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED intents and prepared for active device updates. Bug: 68951996 Test: manual Change-Id: I22ca639a04fed7bf17df59c405ddeda90dafb8ff (cherry picked from commit 889d234e8cb0d82f40f532ff95a4ef94f304d670) --- .../android/bluetooth/BluetoothHeadset.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 838d3153d54..55a6b4c6b4d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -93,6 +94,23 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; + /** + * Intent used to broadcast the selection of a connected device as active. + * + *

        This intent will have one extra: + *

          + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; /** * Intent used to broadcast that the headset has posted a @@ -982,6 +1000,76 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, in HFP and HSP profiles, + * it is the device used for phone call audio. If a remote device is not + * connected, it cannot be selected as active. + * + *

        This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device, could be null if phone call audio should not be + * streamed to a headset + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "setActiveDevice: " + device); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && (device == null || isValidDevice(device))) { + try { + return service.setActiveDevice(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } + + /** + * Get the connected device that is active. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active. + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + public BluetoothDevice getActiveDevice() { + if (VDBG) { + Log.d(TAG, "getActiveDevice"); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { + try { + return service.getActiveDevice(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } + /** * check if in-band ringing is supported for this platform. * -- GitLab From 31318973431dfed37eaad41c265693ab15614699 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 5 Jan 2018 13:54:34 -0800 Subject: [PATCH 0882/1408] BluetoothProfile: Add Object Push Profile (OPP) Test: toggle Bluetooth Change-Id: I5aec940ba3d6c6364c2de667426d98d0b3cea0c6 --- framework/java/android/bluetooth/BluetoothProfile.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index df2028a5535..41cf809afd7 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -156,13 +156,20 @@ public interface BluetoothProfile { */ public static final int HID_DEVICE = 19; + /** + * Object Push Profile (OPP) + * + * @hide + */ + public static final int OPP = 20; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - public static final int MAX_PROFILE_ID = 19; + public static final int MAX_PROFILE_ID = 20; /** * Default priority for devices that we try to auto-connect to and -- GitLab From c5fde734b4b11024064f837c5974de789576859b Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 5 Jan 2018 17:17:06 -0800 Subject: [PATCH 0883/1408] HFP: Add isInbandRingingEnabled() API (1/4) * Use BluetoothHeadset.isInbandRingingEnabled() API to check whether in-band ringing is currently enabled in the system when deciding on audio routes for ringtone * Add this as a hidden internal API for system components Bug: 71646213 Test: make, toggle in-band ringing from Development Settings and observe whether Telecom service tries to open SCO when there is an incoming call Change-Id: I1ef0dd2b54ace7649ddd1f043f0ef5847743a5c4 --- .../android/bluetooth/BluetoothHeadset.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 55a6b4c6b4d..c94540a48ec 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -1071,9 +1071,35 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * check if in-band ringing is supported for this platform. + * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an + * active connection. * - * @return true if in-band ringing is supported false if in-band ringing is not supported + * @return true if in-band ringing is enabled, false if in-band ringing is disabled + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + public boolean isInbandRingingEnabled() { + if (DBG) { + log("isInbandRingingEnabled()"); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { + try { + return service.isInbandRingingEnabled(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } + + /** + * Check if in-band ringing is supported for this platform. + * + * @return true if in-band ringing is supported, false if in-band ringing is not supported * @hide */ public static boolean isInbandRingingSupported(Context context) { -- GitLab From 9e6b35bec29e29d098f51b5c2388704734969960 Mon Sep 17 00:00:00 2001 From: Selim Gurun Date: Tue, 9 Jan 2018 14:35:19 -0800 Subject: [PATCH 0884/1408] Make Bluetooth constants used by gmscore a system api These constants are used by GMSCore car module via reflection. Make them system API. Bug: 67052734 Test: Manual - and using make update-api Change-Id: I0709c0e0eb9fcb9fb29d575e9b74927a70b2a924 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 +++ framework/java/android/bluetooth/BluetoothHeadset.java | 4 ++-- framework/java/android/bluetooth/BluetoothProfile.java | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ad7a93cd6bb..9b736b7e5f9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -618,6 +618,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @SystemApi public static final int ACCESS_UNKNOWN = 0; /** @@ -626,6 +627,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @SystemApi public static final int ACCESS_ALLOWED = 1; /** @@ -634,6 +636,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @SystemApi public static final int ACCESS_REJECTED = 2; /** diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 55a6b4c6b4d..a1382428a61 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -556,8 +556,8 @@ public final class BluetoothHeadset 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}, + * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or + * {@link BluetoothProfile#PRIORITY_OFF}, * *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * permission. diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 41cf809afd7..0e2263f773b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -19,6 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import java.util.List; @@ -185,6 +186,7 @@ public interface BluetoothProfile { * * @hide **/ + @SystemApi public static final int PRIORITY_ON = 100; /** @@ -193,6 +195,7 @@ public interface BluetoothProfile { * * @hide **/ + @SystemApi public static final int PRIORITY_OFF = 0; /** -- GitLab From 4a50680fe838d0b1b2390833ff0661427ef9c8d6 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Wed, 3 Jan 2018 08:59:57 -0800 Subject: [PATCH 0885/1408] Bluetooth in band ring Add a flag to the BluetoothHeadsetClientCall indicating the current status of in band ring on the connected phone. Bug: 65673832 Test: runtest bluetooth -c com.android.bluetooth.hfpclient.HeadsetClientStateMachineTest Change-Id: I7e839f2790b1a27d336528e93bc8a4c8d8ff3036 (cherry picked from commit f780364a9a1f6171860cbdf4e1b41a01ee7d88c6) --- .../bluetooth/BluetoothHeadsetClientCall.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index dc00d63043f..d46b2e37467 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -73,17 +73,18 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private final boolean mOutgoing; private final UUID mUUID; private final long mCreationElapsedMilli; + private final boolean mInBandRing; /** * Creates BluetoothHeadsetClientCall instance. */ public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, - boolean multiParty, boolean outgoing) { - this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing); + boolean multiParty, boolean outgoing, boolean inBandRing) { + this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing); } public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state, - String number, boolean multiParty, boolean outgoing) { + String number, boolean multiParty, boolean outgoing, boolean inBandRing) { mDevice = device; mId = id; mUUID = uuid; @@ -91,6 +92,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mNumber = number != null ? number : ""; mMultiParty = multiParty; mOutgoing = outgoing; + mInBandRing = inBandRing; mCreationElapsedMilli = SystemClock.elapsedRealtime(); } @@ -200,6 +202,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mOutgoing; } + /** + * Checks if the ringtone will be generated by the connected phone + * + * @return true if in band ring is enabled, false otherwise. + */ + public boolean isInBandRing() { + return mInBandRing; + } + + @Override public String toString() { return toString(false); @@ -253,6 +265,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { builder.append(mMultiParty); builder.append(", mOutgoing: "); builder.append(mOutgoing); + builder.append(", mInBandRing: "); + builder.append(mInBandRing); builder.append("}"); return builder.toString(); } @@ -266,7 +280,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public BluetoothHeadsetClientCall createFromParcel(Parcel in) { return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null), in.readInt(), UUID.fromString(in.readString()), in.readInt(), - in.readString(), in.readInt() == 1, in.readInt() == 1); + in.readString(), in.readInt() == 1, in.readInt() == 1, + in.readInt() == 1); } @Override @@ -284,6 +299,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { out.writeString(mNumber); out.writeInt(mMultiParty ? 1 : 0); out.writeInt(mOutgoing ? 1 : 0); + out.writeInt(mInBandRing ? 1 : 0); } @Override -- GitLab From 500febee6219c4509c06d11c11758915556be02f Mon Sep 17 00:00:00 2001 From: Arthur Hsu Date: Thu, 11 Jan 2018 16:46:22 -0800 Subject: [PATCH 0886/1408] Skip SystemUI check in BluetoothServiceManager if noHome. When device is configured with config_noHomeScreen, there will be no SystemUI. Checking SystemUI UID is meaningless. Bug: 62413935 69965109 Test: On sailfish, logcat shows expected SystemUI UID by default Change-Id: Iaccc0f10cb82da454077056824c3f6eea6a37c4e --- .../server/bluetooth/BluetoothManagerService.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d9713a517a9..337406d58f9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.server.pm.UserRestrictionsUtils; @@ -415,9 +416,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int systemUiUid = -1; try { - systemUiUid = mContext.getPackageManager() - .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, - UserHandle.USER_SYSTEM); + // Check if device is configured with no home screen, which implies no SystemUI. + boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen); + if (!noHome) { + systemUiUid = mContext.getPackageManager() + .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, + UserHandle.USER_SYSTEM); + } + Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid)); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); -- GitLab From d67d5e4f1e8c1afd98f11b11ca8ca26792da9d6b Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Wed, 22 Nov 2017 16:04:40 -0800 Subject: [PATCH 0887/1408] Added APIs for Connection-oriented channels Experimental and hidden APIs are defined for the Connection-oriented Channel (CoC) features. The APIs using PSM are implemented. Test: Can compile Bug: 70683224 Change-Id: Icdb5fa190b0e21881a60437fa48cd575371ee1e4 --- .../android/bluetooth/BluetoothAdapter.java | 126 +++++++++++++++++- .../android/bluetooth/BluetoothDevice.java | 71 ++++++++++ .../bluetooth/BluetoothServerSocket.java | 20 +++ .../android/bluetooth/BluetoothSocket.java | 19 ++- 4 files changed, 227 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 158aebb478a..4775bde420e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -79,8 +79,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link BluetoothDevice} objects representing all paired devices with * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to - * listen for incoming connection requests with - * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for + * listen for incoming RFComm connection requests with {@link + * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented + * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

        *

        This class is thread safe.

        @@ -209,6 +210,14 @@ public final class BluetoothAdapter { */ public static final int STATE_BLE_TURNING_OFF = 16; + /** + * UUID of the GATT Read Characteristics for LE_PSM value. + * + * @hide + */ + public static final UUID LE_PSM_CHARACTERISTIC_UUID = + UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); + /** * Human-readable string helper for AdapterState * @@ -2135,7 +2144,9 @@ public final class BluetoothAdapter { min16DigitPin); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); + int assignedChannel = socket.mSocket.getPort(); + if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel); + socket.setChannel(assignedChannel); } if (errno != 0) { //TODO(BT): Throw the same exception error code @@ -2176,12 +2187,18 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { + Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false, - false); + false); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); + int assignedChannel = socket.mSocket.getPort(); + if (DBG) { + Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to " + + assignedChannel); + } + socket.setChannel(assignedChannel); } if (errno != 0) { //TODO(BT): Throw the same exception error code @@ -2738,4 +2755,103 @@ public final class BluetoothAdapter { scanner.stopScan(scanCallback); } } + + /** + * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and + * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen + * for incoming connections. + *

        A remote device connecting to this socket will be authenticated and communication on this + * socket will be encrypted. + *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening + * {@link BluetoothServerSocket}. + *

        The system will assign a dynamic PSM value. This PSM value can be read from the {#link + * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is + * closed, Bluetooth is turned off, or the application exits unexpectedly. + *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is + * defined and performed by the application. + *

        Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server + * socket from another Android device that is given the PSM value. + * + * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} + * @return an L2CAP CoC BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or unable to start this CoC + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingL2capCoc(int transport) + throws IOException { + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + throw new IOException("Error: " + errno); + } + + int assignedPsm = socket.mSocket.getPort(); + if (assignedPsm == 0) { + throw new IOException("Error: Unable to assign PSM value"); + } + if (DBG) { + Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to " + + assignedPsm); + } + socket.setChannel(assignedPsm); + + return socket; + } + + /** + * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and + * assign a dynamic PSM value. This socket can be used to listen for incoming connections. + *

        The link key is not required to be authenticated, i.e the communication may be vulnerable + * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and + * authenticated communication channel is desired. + *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening + * {@link BluetoothServerSocket}. + *

        The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value + * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released + * when this server socket is closed, Bluetooth is turned off, or the application exits + * unexpectedly. + *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is + * defined and performed by the application. + *

        Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this + * server socket from another Android device that is given the PSM value. + * + * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} + * @return an L2CAP CoC BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or unable to start this CoC + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) + throws IOException { + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + throw new IOException("Error: " + errno); + } + + int assignedPsm = socket.mSocket.getPort(); + if (assignedPsm == 0) { + throw new IOException("Error: Unable to assign PSM value"); + } + if (DBG) { + Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to " + + assignedPsm); + } + socket.setChannel(assignedPsm); + + return socket; + } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d982bb7ffb4..f4dda809f5b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1910,4 +1910,75 @@ public final class BluetoothDevice implements Parcelable { } return null; } + + /** + * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can + * be used to start a secure outgoing connection to the remote device with the same dynamic + * protocol/service multiplexer (PSM) value. + *

        This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for + * peer-peer Bluetooth applications. + *

        Use {@link BluetoothSocket#connect} to initiate the outgoing connection. + *

        Application using this API is responsible for obtaining PSM value from remote device. + *

        The remote device will be authenticated and communication on this socket will be + * encrypted. + *

        Use this socket if an authenticated socket link is possible. Authentication refers + * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a + * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, + * int)}. + * + * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} + * @param psm dynamic PSM value from remote device + * @return a CoC #BluetoothSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { + if (!isBluetoothEnabled()) { + Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled"); + throw new IOException(); + } + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm); + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm, + null); + } + + /** + * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can + * be used to start a secure outgoing connection to the remote device with the same dynamic + * protocol/service multiplexer (PSM) value. + *

        This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)} + * for peer-peer Bluetooth applications. + *

        Use {@link BluetoothSocket#connect} to initiate the outgoing connection. + *

        Application using this API is responsible for obtaining PSM value from remote device. + *

        The communication channel may not have an authenticated link key, i.e. it may be subject + * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and + * authenticated communication channel is possible. + * + * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} + * @param psm dynamic PSM value from remote device + * @return a CoC #BluetoothSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { + if (!isBluetoothEnabled()) { + Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled"); + throw new IOException(); + } + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + if (DBG) { + Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm); + } + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm, + null); + } } diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 58d090dc287..ebb7f187aea 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -68,6 +68,7 @@ import java.io.IOException; public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; + private static final boolean DBG = false; /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; @@ -169,6 +170,7 @@ public final class BluetoothServerSocket implements Closeable { * close any {@link BluetoothSocket} received from {@link #accept()}. */ public void close() throws IOException { + if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel); synchronized (this) { if (mHandler != null) { mHandler.obtainMessage(mMessage).sendToTarget(); @@ -196,6 +198,20 @@ public final class BluetoothServerSocket implements Closeable { return mChannel; } + /** + * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP + * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the + * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link + * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this + * method is called on non-L2CAP server sockets. + * + * @return the assigned PSM or LE_PSM value depending on transport + * @hide + */ + public int getPsm() { + return mChannel; + } + /** * Sets the channel on which future sockets are bound. * Currently used only when a channel is auto generated. @@ -227,6 +243,10 @@ public final class BluetoothServerSocket implements Closeable { sb.append("TYPE_L2CAP"); break; } + case BluetoothSocket.TYPE_L2CAP_LE: { + sb.append("TYPE_L2CAP_LE"); + break; + } case BluetoothSocket.TYPE_SCO: { sb.append("TYPE_SCO"); break; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 0569913435a..09f96840f91 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -99,6 +99,16 @@ public final class BluetoothSocket implements Closeable { /** L2CAP socket */ public static final int TYPE_L2CAP = 3; + /** L2CAP socket on BR/EDR transport + * @hide + */ + public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP; + + /** L2CAP socket on LE transport + * @hide + */ + public static final int TYPE_L2CAP_LE = 4; + /*package*/ static final int EBADFD = 77; /*package*/ static final int EADDRINUSE = 98; @@ -417,6 +427,7 @@ public final class BluetoothSocket implements Closeable { return -1; } try { + if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType); mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName, mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { @@ -451,7 +462,7 @@ public final class BluetoothSocket implements Closeable { mSocketState = SocketState.LISTENING; } } - if (DBG) Log.d(TAG, "channel: " + channel); + if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort); if (mPort <= -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -515,7 +526,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { int bytesToRead = length; if (VDBG) { Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length @@ -558,7 +569,7 @@ public final class BluetoothSocket implements Closeable { // Rfcomm uses dynamic allocation, and should not have any bindings // to the actual message length. if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { if (length <= mMaxTxPacketSize) { mSocketOS.write(b, offset, length); } else { @@ -702,7 +713,7 @@ public final class BluetoothSocket implements Closeable { } private void createL2capRxBuffer() { - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { // Allocate the buffer to use for reads. if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); -- GitLab From cc816bb0a93e42e6ffa4ec1447a2bf69a23b50d1 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 17 Jan 2018 02:09:53 -0800 Subject: [PATCH 0888/1408] Added internal API getMaxConnectedAudioDevices() The API can be used to obtain the maximum number of connected devices for A2DP or HFP. Test: Manual Bug: 64767509 Change-Id: I80b8a1c85e33ae0b23fdc4812f8991a4202d9abc --- .../android/bluetooth/BluetoothAdapter.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c7be0f36ece..cdc881a25aa 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1670,6 +1670,27 @@ public final class BluetoothAdapter { return 0; } + /** + * Get the maximum number of connected audio devices. + * + * @return the maximum number of connected audio devices + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getMaxConnectedAudioDevices() { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getMaxConnectedAudioDevices(); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return 1; + } + /** * Return true if hardware has entries available for matching beacons * -- GitLab From aa8b0f1d512e618dd39a647246073e27bf2f7b84 Mon Sep 17 00:00:00 2001 From: Arthur Hsu Date: Thu, 11 Jan 2018 11:05:11 -0800 Subject: [PATCH 0889/1408] Add config_supportBluetoothPersistedState. Add a config to indicate whether a device supports Bluetooth persisted state and defaulted to true. In ARC++, Bluetooth adapter state can be out-of-sync from its container if the user changes it before the container being available. When set to false, this config prevents Android to auto start the Bluetooth. ARC++ will set this to false and sync Bluetooth state later with Android. Related CL is ag/931281 Bug: 62413625, 69965109 Test: On sailfish, Bluetooth on/off correctly and state persisted across reboot Change-Id: Ie298929eb6b882108a12e8b95f61e3c9f8eb63ea --- .../server/bluetooth/BluetoothManagerService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 337406d58f9..20777901a3a 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -439,10 +439,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } + private boolean supportBluetoothPersistedState() { + return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState); + } + /** * Returns true if the Bluetooth saved state is "on" */ private boolean isBluetoothPersistedStateOn() { + if (!supportBluetoothPersistedState()) { + return false; + } int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); if (DBG) { Slog.d(TAG, "Bluetooth persisted state: " + state); @@ -454,6 +461,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ private boolean isBluetoothPersistedStateOnBluetooth() { + if (!supportBluetoothPersistedState()) { + return false; + } return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; } -- GitLab From 164b38b96f225cef9eab6edebf589215071aec48 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 3 Jan 2018 19:38:39 -0800 Subject: [PATCH 0890/1408] Add support for Multi-A2DP state machines per device * Update usage of A2dpService API calls that take BluetoothDevice as an additional argument * Update the description for BluetoothA2dp.connect() Exempt-From-Owner-Approval: De-facto owner of the relevant changes is the Bluetooth team. Bug: 69269748 Test: Manual Change-Id: I190ed48ef65bbc8b88b45f84ebd6ab3d21cf0b4e --- .../java/android/bluetooth/BluetoothA2dp.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 35a21a4eaf9..b255a43cbe3 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -300,11 +300,7 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * Initiate connection to a profile of the remote bluetooth device. - * - *

        Currently, the system supports only 1 connection to the - * A2DP profile. The API will automatically disconnect connected - * devices before connecting. + * Initiate connection to a profile of the remote Bluetooth device. * *

        This API returns false in scenarios like the profile on the * device is already connected or Bluetooth is not turned on. @@ -699,15 +695,17 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Gets the current codec status (configuration and capability). * + * @param device the remote Bluetooth device. If null, use the current + * active A2DP Bluetooth device. * @return the current codec status * @hide */ - public BluetoothCodecStatus getCodecStatus() { - if (DBG) Log.d(TAG, "getCodecStatus"); + public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - return mService.getCodecStatus(); + return mService.getCodecStatus(device); } if (mService == null) { Log.w(TAG, "Proxy not attached to service"); @@ -724,15 +722,18 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Sets the codec configuration preference. * + * @param device the remote Bluetooth device. If null, use the current + * active A2DP Bluetooth device. * @param codecConfig the codec configuration preference * @hide */ - public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { - if (DBG) Log.d(TAG, "setCodecConfigPreference"); + public void setCodecConfigPreference(BluetoothDevice device, + BluetoothCodecConfig codecConfig) { + if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(codecConfig); + mService.setCodecConfigPreference(device, codecConfig); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -747,36 +748,42 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Enables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @hide */ - public void enableOptionalCodecs() { - if (DBG) Log.d(TAG, "enableOptionalCodecs"); - enableDisableOptionalCodecs(true); + public void enableOptionalCodecs(BluetoothDevice device) { + if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + enableDisableOptionalCodecs(device, true); } /** * Disables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @hide */ - public void disableOptionalCodecs() { - if (DBG) Log.d(TAG, "disableOptionalCodecs"); - enableDisableOptionalCodecs(false); + public void disableOptionalCodecs(BluetoothDevice device) { + if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + enableDisableOptionalCodecs(device, false); } /** * Enables or disables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @param enable if true, enable the optional codecs, other disable them */ - private void enableDisableOptionalCodecs(boolean enable) { + private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(); + mService.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(); + mService.disableOptionalCodecs(device); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); -- GitLab From 42044b47619069bdfdeeb151bea505c59c2e6510 Mon Sep 17 00:00:00 2001 From: Ricardo Loo Foronda Date: Wed, 24 Jan 2018 17:49:18 -0800 Subject: [PATCH 0891/1408] docs: Added a missing "be" in ACTION_CONNECTION_STATE_CHANGED Status: Ready for writer review. Note: This is a javadoc only change. Test: Build the docs with "make ds-docs" and staged updated content. Staged content: go/dac-stage/reference/android/bluetooth/BluetoothAdapter.html#ACTION_CONNECTION_STATE_CHANGED Bug: 69097875 Change-Id: Ibffea2373ded825558beaa669ae4daf3c0ff3d2f --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index fc3a72482a7..1553eec8da6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -415,7 +415,7 @@ public final class BluetoothAdapter { * Intent used to broadcast the change in connection state of the local * Bluetooth adapter to a profile of the remote device. When the adapter is * not connected to any profiles of any remote devices and it attempts a - * connection to a profile this intent will sent. Once connected, this intent + * connection to a profile this intent will be sent. Once connected, this intent * will not be sent for any more connection attempts to any profiles of any * remote device. When the adapter disconnects from the last profile its * connected to of any remote device, this intent will be sent. -- GitLab From 7f195cee6651eddc4ff4bc9af829bd7d8b5343b7 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Wed, 3 Jan 2018 19:38:39 -0800 Subject: [PATCH 0892/1408] Add support for Multi-A2DP state machines per device * Update usage of A2dpService API calls that take BluetoothDevice as an additional argument * Update the description for BluetoothA2dp.connect() Exempt-From-Owner-Approval: De-facto owner of the relevant changes is the Bluetooth team. Bug: 69269748 Test: Manual Change-Id: I190ed48ef65bbc8b88b45f84ebd6ab3d21cf0b4e Merged-In: I190ed48ef65bbc8b88b45f84ebd6ab3d21cf0b4e (cherry picked from commit 164b38b96f225cef9eab6edebf589215071aec48) --- .../java/android/bluetooth/BluetoothA2dp.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 35a21a4eaf9..b255a43cbe3 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -300,11 +300,7 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * Initiate connection to a profile of the remote bluetooth device. - * - *

        Currently, the system supports only 1 connection to the - * A2DP profile. The API will automatically disconnect connected - * devices before connecting. + * Initiate connection to a profile of the remote Bluetooth device. * *

        This API returns false in scenarios like the profile on the * device is already connected or Bluetooth is not turned on. @@ -699,15 +695,17 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Gets the current codec status (configuration and capability). * + * @param device the remote Bluetooth device. If null, use the current + * active A2DP Bluetooth device. * @return the current codec status * @hide */ - public BluetoothCodecStatus getCodecStatus() { - if (DBG) Log.d(TAG, "getCodecStatus"); + public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - return mService.getCodecStatus(); + return mService.getCodecStatus(device); } if (mService == null) { Log.w(TAG, "Proxy not attached to service"); @@ -724,15 +722,18 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Sets the codec configuration preference. * + * @param device the remote Bluetooth device. If null, use the current + * active A2DP Bluetooth device. * @param codecConfig the codec configuration preference * @hide */ - public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { - if (DBG) Log.d(TAG, "setCodecConfigPreference"); + public void setCodecConfigPreference(BluetoothDevice device, + BluetoothCodecConfig codecConfig) { + if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(codecConfig); + mService.setCodecConfigPreference(device, codecConfig); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -747,36 +748,42 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Enables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @hide */ - public void enableOptionalCodecs() { - if (DBG) Log.d(TAG, "enableOptionalCodecs"); - enableDisableOptionalCodecs(true); + public void enableOptionalCodecs(BluetoothDevice device) { + if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + enableDisableOptionalCodecs(device, true); } /** * Disables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @hide */ - public void disableOptionalCodecs() { - if (DBG) Log.d(TAG, "disableOptionalCodecs"); - enableDisableOptionalCodecs(false); + public void disableOptionalCodecs(BluetoothDevice device) { + if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + enableDisableOptionalCodecs(device, false); } /** * Enables or disables the optional codecs. * + * @param device the remote Bluetooth device. If null, use the currect + * active A2DP Bluetooth device. * @param enable if true, enable the optional codecs, other disable them */ - private void enableDisableOptionalCodecs(boolean enable) { + private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(); + mService.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(); + mService.disableOptionalCodecs(device); } } if (mService == null) Log.w(TAG, "Proxy not attached to service"); -- GitLab From 03ac71e3be32d96e0baf2dafca5d327a8c38aa08 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 7 Dec 2017 22:56:03 -0800 Subject: [PATCH 0893/1408] Fix bad type for txPower in PeriodicAdvertisingReport serialization Bug: 69634768 Test: compilation Change-Id: Icedfbaf1ba933637e935ada0fd98aea42c73f2b2 Merged-In: Icedfbaf1ba933637e935ada0fd98aea42c73f2b2 --- .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 51b93cbd64d..6fc8d553946 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -71,7 +71,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(syncHandle); - dest.writeLong(txPower); + dest.writeInt(txPower); dest.writeInt(rssi); dest.writeInt(dataStatus); if (data != null) { -- GitLab From 65be80345d2c5a8812a9a196889cac5d209706a9 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 4 Jan 2018 15:42:25 -0800 Subject: [PATCH 0894/1408] Add function to update LE connection parameters As part of new SL4A tests for LE CoC to measure data throughput performance, this commit will add a function to enable the CoC Facade to modify the LE Connection Parameters especially the Connection Intervals. Test: Ran the new ACTS Tests for LE CoC Bug: 70683224 Change-Id: Ie071b09a44d68fe063198a39eb788c434b092442 --- .../java/android/bluetooth/BluetoothGatt.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index a2af3422eae..3df433643b0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1506,6 +1506,38 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } + /** + * Request an LE connection parameter update. + * + *

        This function will send an LE connection parameters update request to the remote device. + * + * @return true, if the request is send to the Bluetooth stack. + * @hide + */ + public boolean requestLeConnectionUpdate(int minConnectionInterval, + int maxConnectionInterval, + int slaveLatency, int supervisionTimeout) { + if (DBG) { + Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval + + ")" + (1.25 * minConnectionInterval) + + "msec, max=(" + maxConnectionInterval + ")" + + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency + + ", timeout=" + supervisionTimeout + "msec"); + } + if (mService == null || mClientIf == 0) return false; + + try { + mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), + minConnectionInterval, maxConnectionInterval, + slaveLatency, supervisionTimeout); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + + return true; + } + /** * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument -- GitLab From 6edd25f33e4e4bed4fdd9eb815012111e59ad2a7 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 2 Feb 2018 13:25:31 -0700 Subject: [PATCH 0895/1408] 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/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothA2dpSink.java | 2 +- framework/java/android/bluetooth/BluetoothAvrcpController.java | 2 +- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- framework/java/android/bluetooth/BluetoothHealth.java | 2 +- framework/java/android/bluetooth/BluetoothHidDevice.java | 2 +- framework/java/android/bluetooth/BluetoothHidHost.java | 2 +- framework/java/android/bluetooth/BluetoothMap.java | 2 +- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- framework/java/android/bluetooth/BluetoothPan.java | 2 +- framework/java/android/bluetooth/BluetoothPbap.java | 2 +- framework/java/android/bluetooth/BluetoothPbapClient.java | 2 +- framework/java/android/bluetooth/BluetoothSap.java | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b255a43cbe3..a2e714eb348 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -262,7 +262,7 @@ public final class BluetoothA2dp 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 A2DP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index faab000a899..13f0aaf47f0 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -182,7 +182,7 @@ public final class BluetoothA2dpSink 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 A2DP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 5f0e5d97447..e7c8944788f 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -138,7 +138,7 @@ public final class BluetoothAvrcpController 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 AVRCP Controller Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 031287f5ee1..397b90656f3 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -427,7 +427,7 @@ public final class BluetoothHeadsetClient 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 Headset Client Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 57a019755f8..b967fb20f02 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -491,7 +491,7 @@ public final class BluetoothHealth 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 Health Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 2fab305ba8b..77ab7319046 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -236,7 +236,7 @@ public final class BluetoothHidDevice 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 HID Device Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 8ad0f9d064f..0ca39f169a7 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -279,7 +279,7 @@ public final class BluetoothHidHost 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 HID Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 5b55b23680c..0fa1d5d6295 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -109,7 +109,7 @@ public final class BluetoothMap 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 Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index af3b662d6a4..4f21d936e56 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; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 866b0630835..9f401eb3cef 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -147,7 +147,7 @@ public final class BluetoothPan 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 Pan Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 794435457f2..c60e9e075c8 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -163,7 +163,7 @@ public class BluetoothPbap 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 Pbap Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 01b3f6e0e84..1446adc8b9c 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -116,7 +116,7 @@ public final class BluetoothPbapClient 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 PBAP Client Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48481620c97..c51e39a7418 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -147,7 +147,7 @@ public final class BluetoothSap 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 SAP Service with " + intent); return false; } -- GitLab From cf3cfd339a2635b0ccd94ad9a3eaa7229a8f3d54 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 16 Jan 2018 10:39:32 -0800 Subject: [PATCH 0896/1408] Add function to change LE Tx Data Length As part of new SL4A tests for LE CoC to measure data throughput, this commit adds a function to set the LE Tx Data Length parameter to its maximum. Test: Ran the new ACTS Tests for LE CoC (BleCocTest and BleCoc2ConnTest) Bug: 70683224 Change-Id: Iea93f6cb9f4f7cc484f121afa158d7dae18d1ef1 --- .../android/bluetooth/BluetoothSocket.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 09f96840f91..09a5b593e52 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -676,6 +676,35 @@ public final class BluetoothSocket implements Closeable { mExcludeSdp = excludeSdp; } + /** + * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This + * parameter is used by the BT Controller to set the maximum transmission packet size on this + * connection. This function is currently used for testing only. + * @hide + */ + public void requestMaximumTxDataLength() throws IOException { + if (mDevice == null) { + throw new IOException("requestMaximumTxDataLength is called on null device"); + } + + try { + if (mSocketState == SocketState.CLOSED) { + throw new IOException("socket closed"); + } + IBluetooth bluetoothProxy = + BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + throw new IOException("Bluetooth is off"); + } + + if (DBG) Log.d(TAG, "requestMaximumTxDataLength"); + bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + throw new IOException("unable to send RPC: " + e.getMessage()); + } + } + private String convertAddr(final byte[] addr) { return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); -- GitLab From 2b6bd69eeb4f5d51632b54513c3363c6bb1f9e3a Mon Sep 17 00:00:00 2001 From: baisheng Date: Thu, 25 Jan 2018 18:07:24 +0800 Subject: [PATCH 0897/1408] Add dedicated signature permissions to platform manifest and shell manifest which could enable/disable BT and Wifi state on watch Skip Bluetooth consent UI if running on shell, also fix a typo in log message. Test: Manual test running `adb root; adb shell service call bluetooth_manager 6` and see if BT is on without consent UI. Bug: 69872231 Change-Id: Ie513794a7fc13041259fd84734bfc651495ba5cf --- .../bluetooth/BluetoothManagerService.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 20777901a3a..55ad8cc9bc9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -948,8 +948,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - private boolean startConsentUiIfNeeded(String packageName, int callingUid, String intentAction) - throws RemoteException { + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + if (checkBluetoothPermissionWhenPermissionReviewRequired()) { + return false; + } try { // Validate the package only if we are going to use it ApplicationInfo applicationInfo = mContext.getPackageManager() @@ -957,7 +960,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); if (applicationInfo.uid != callingUid) { - throw new SecurityException("Package " + callingUid + " not in uid " + callingUid); + throw new SecurityException("Package " + packageName + + " not in uid " + callingUid); } Intent intent = new Intent(intentAction); @@ -977,6 +981,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Check if the caller must still pass permission check or if the caller is exempted + * from the consent UI via the MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED check. + * + * Commands from some callers may be exempted from triggering the consent UI when + * enabling bluetooth. This exemption is checked via the + * MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED and allows calls to skip + * the consent UI where it may otherwise be required. + * + * @hide + */ + private boolean checkBluetoothPermissionWhenPermissionReviewRequired() { + if (!mPermissionReviewRequired) { + return false; + } + int result = mContext.checkCallingPermission( + android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED); + return result == PackageManager.PERMISSION_GRANTED; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding -- GitLab From 3f23873b7e0bc6eca4c9778caeea1fd7ae41bf15 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 22 Nov 2017 11:02:34 -0800 Subject: [PATCH 0898/1408] Hearing Aid profile This is implementation of Hearing Aid Profile that will in future be connected to Bluetooth Manager - see TODOs in BluetoothHearingAid.java Bug: 69623109 Test: compilation. Manual test with HA. Change-Id: I79643ea1e14e9df7f5771169359c964a60c56618 --- .../android/bluetooth/BluetoothAdapter.java | 6 + .../bluetooth/BluetoothHearingAid.java | 693 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- .../java/android/bluetooth/BluetoothUuid.java | 3 + 4 files changed, 710 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothHearingAid.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6c8fe2e399f..dc761521fbd 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2303,6 +2303,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HID_DEVICE) { BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; + } else if (profile == BluetoothProfile.HEARING_AID) { + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + return true; } else { return false; } @@ -2385,6 +2388,9 @@ public final class BluetoothAdapter { BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; hidDevice.close(); break; + case BluetoothProfile.HEARING_AID: + BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; + hearingAid.close(); } } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java new file mode 100644 index 00000000000..647e0d033fb --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -0,0 +1,693 @@ +/* + * Copyright 2018 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.Manifest; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This class provides the public APIs to control the Bluetooth Hearing Aid + * profile. + * + *

        BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHearingAid proxy object. + * + *

        Each method is protected with its appropriate permission. + * @hide + */ +public final class BluetoothHearingAid implements BluetoothProfile { + private static final String TAG = "BluetoothHearingAid"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the Hearing Aid + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the change in the Playing state of the Hearing Aid + * profile. + * + *

        This intent will have 3 extras: + *

          + *
        • {@link #EXTRA_STATE} - The current state of the profile.
        • + *
        • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
        • + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
        • + *
        + * + *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PLAYING_STATE_CHANGED = + "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED"; + + /** + * Intent used to broadcast the selection of a connected device as active. + * + *

        This intent will have one extra: + *

          + *
        • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
        • + *
        + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; + + /** + * Hearing Aid device is streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_PLAYING = 10; + + /** + * Hearing Aid device is NOT streaming music. This state can be one of + * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of + * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + */ + public static final int STATE_NOT_PLAYING = 11; + + /** This device represents Left Hearing Aid. */ + public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; + + /** This device represents Right Hearing Aid. */ + public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT; + + /** This device is Monaural. */ + public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL; + + /** This device is Binaural (should receive only left or right audio). */ + public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL; + + /** Can't read ClientID for this device */ + public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; + + private Context mContext; + private ServiceListener mServiceListener; + private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("mServiceLock") + private IBluetoothHearingAid mService; + private BluetoothAdapter mAdapter; + + 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..."); + try { + mServiceLock.writeLock().lock(); + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); + } + } else { + try { + mServiceLock.readLock().lock(); + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + doBind(); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.readLock().unlock(); + } + } + } + }; + + /** + * Create a BluetoothHearingAid proxy object for interacting with the local + * Bluetooth Hearing Aid service. + */ + /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { + 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(); + } + + void doBind() { + Intent intent = new Intent(IBluetoothHearingAid.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 Hearing Aid Service with " + intent); + return; + } + } + + /*package*/ void close() { + mServiceListener = null; + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + + try { + mServiceLock.writeLock().lock(); + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } finally { + mServiceLock.writeLock().unlock(); + } + } + + @Override + public void finalize() { + // The empty finalize needs to be kept or the + // cts signature tests would fail. + } + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + *

        This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + public boolean connect(BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.connect(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Initiate disconnection from a profile + * + *

        This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

        If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() && isValidDevice(device)) { + return mService.disconnect(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getConnectedDevices(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getDevicesMatchingConnectionStates(states); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getConnectionState(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Set priority of the profile + * + *

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

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + return mService.setPriority(device, priority); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * 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 + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getPriority(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + case STATE_PLAYING: + return "playing"; + case STATE_NOT_PLAYING: + return "not playing"; + default: + return ""; + } + } + + /** + * Get the volume of the device. + * + *

        The volume is between -128 dB (mute) to 0 dB. + * + * @return volume of the hearing aid device. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getVolume() { + if (VDBG) { + log("getVolume()"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getVolume(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return 0; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return 0; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Tells remote device to adjust volume. Uses the following values: + *

          + *
        • {@link AudioManager#ADJUST_LOWER}
        • + *
        • {@link AudioManager#ADJUST_RAISE}
        • + *
        • {@link AudioManager#ADJUST_MUTE}
        • + *
        • {@link AudioManager#ADJUST_UNMUTE}
        • + *
        + * + * @param direction One of the supported adjust values. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public void adjustVolume(int direction) { + if (DBG) log("adjustVolume(" + direction + ")"); + + try { + mServiceLock.readLock().lock(); + + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return; + } + + if (!isEnabled()) return; + + mService.adjustVolume(direction); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Tells remote device to set an absolute volume. + * + * @param volume Absolute volume to be set on remote + * @hide + */ + public void setVolume(int volume) { + if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + + try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return; + } + + if (!isEnabled()) return; + + mService.setVolume(volume); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the CustomerId of the device. + * + * @param device Bluetooth device + * @return the CustomerId of the device + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public long getHiSyncId(BluetoothDevice device) { + if (VDBG) { + log("getCustomerId(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService == null) { + Log.w(TAG, "Proxy not attached to service"); + return HI_SYNC_ID_INVALID; + } + + if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; + + return mService.getHiSyncId(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return HI_SYNC_ID_INVALID; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the side of the device. + * + * @param device Bluetooth device. + * @return SIDE_LEFT or SIDE_RIGHT + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getDeviceSide(BluetoothDevice device) { + if (VDBG) { + log("getDeviceSide(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getDeviceSide(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return SIDE_LEFT; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return SIDE_LEFT; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the mode of the device. + * + * @param device Bluetooth device + * @return MODE_MONAURAL or MODE_BINAURAL + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getDeviceMode(BluetoothDevice device) { + if (VDBG) { + log("getDeviceMode(" + device + ")"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && isValidDevice(device)) { + return mService.getDeviceMode(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return MODE_MONAURAL; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return MODE_MONAURAL; + } finally { + mServiceLock.readLock().unlock(); + } + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + try { + mServiceLock.writeLock().lock(); + mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); + } finally { + mServiceLock.writeLock().unlock(); + } + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, + BluetoothHearingAid.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + try { + mServiceLock.writeLock().lock(); + mService = null; + } finally { + mServiceLock.writeLock().unlock(); + } + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 0e2263f773b..656188fbdfb 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -164,13 +164,20 @@ public interface BluetoothProfile { */ public static final int OPP = 20; + /** + * Hearing Aid Device + * + * @hide + */ + int HEARING_AID = 21; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - public static final int MAX_PROFILE_ID = 20; + int MAX_PROFILE_ID = 21; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 76cb3f5b548..0a0d2149803 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -79,6 +79,9 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); + /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */ + public static final ParcelUuid HearingAid = + ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05"); public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From 7f827c29ebf892754bf4103d02cad3122e6b3946 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 16 Feb 2018 10:14:57 -0700 Subject: [PATCH 0899/1408] Add RequiresFeature annotation. Certain APIs require that a device have a specific feature to operate correctly, so start annotating them. Test: builds, boots Bug: 72284763 Change-Id: Ie2f30284bdfdb6acc4067f434eba3b5433837606 Exempt-From-Owner-Approval: simple annotations --- framework/java/android/bluetooth/BluetoothManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 7e3bb05fe02..11f8ab7551c 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -17,9 +17,11 @@ package android.bluetooth; import android.Manifest; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; +import android.content.pm.PackageManager; import android.os.RemoteException; import android.util.Log; @@ -47,6 +49,7 @@ import java.util.List; * @see BluetoothAdapter#getDefaultAdapter() */ @SystemService(Context.BLUETOOTH_SERVICE) +@RequiresFeature(PackageManager.FEATURE_BLUETOOTH) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = true; -- GitLab From 79c54da6a883436a3bc13e3ac8b4e3f7b909af27 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 22 Feb 2018 15:10:08 -0800 Subject: [PATCH 0900/1408] Use assigned UUID for Hearing Aid Service Test: manual Bug: 64038649 Change-Id: Iab748db944df3bba8927973d0a55d95514b9f8ed --- framework/java/android/bluetooth/BluetoothUuid.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 0a0d2149803..605dbd21993 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -79,9 +79,8 @@ public final class BluetoothUuid { ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); - /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */ public static final ParcelUuid HearingAid = - ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05"); + ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From 28bce625064420d8d4e0d279a52c3ad014af46dd Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 23 Feb 2018 12:57:51 +0000 Subject: [PATCH 0901/1408] frameworks/base: Set LOCAL_SDK_VERSION where possible. This change sets LOCAL_SDK_VERSION for all packages where this is possible without breaking the build, and LOCAL_PRIVATE_PLATFORM_APIS := true otherwise. Setting one of these two will be made required soon, and this is a change in preparation for that. Not setting LOCAL_SDK_VERSION makes the app implicitly depend on the bootclasspath, which is often not required. This change effectively makes depending on private apis opt-in rather than opt-out. Test: make relevant packages Bug: 73535841 Change-Id: I4233b9091d9066c4fa69f3d24aaf367ea500f760 --- framework/tests/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk index 744e5b04974..bb4e302b75c 100644 --- a/framework/tests/Android.mk +++ b/framework/tests/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \ LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_PACKAGE_NAME := BluetoothTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) -- GitLab From 11fb2cf659fbf6d415d42d3215b26dde2f760b35 Mon Sep 17 00:00:00 2001 From: Anton Hansson Date: Fri, 23 Feb 2018 12:57:51 +0000 Subject: [PATCH 0902/1408] frameworks/base: Set LOCAL_SDK_VERSION where possible. This change sets LOCAL_SDK_VERSION for all packages where this is possible without breaking the build, and LOCAL_PRIVATE_PLATFORM_APIS := true otherwise. Setting one of these two will be made required soon, and this is a change in preparation for that. Not setting LOCAL_SDK_VERSION makes the app implicitly depend on the bootclasspath, which is often not required. This change effectively makes depending on private apis opt-in rather than opt-out. Test: make relevant packages Bug: 73535841 Change-Id: Ibcffec873a693d1c792ca210fb597d2bf37e9068 Merged-In: I4233b9091d9066c4fa69f3d24aaf367ea500f760 --- framework/tests/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk index f53419ab53c..41582deb7fe 100644 --- a/framework/tests/Android.mk +++ b/framework/tests/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \ LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test LOCAL_PACKAGE_NAME := BluetoothTests +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) -- GitLab From 65f2ff484f056ee95035e72ffefbf085352acae1 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 28 Feb 2018 11:36:07 -0800 Subject: [PATCH 0903/1408] Bluetooth: Remove adjustAvrcpAbsoluteVolume The AudioManager now handles all calculations for volume stepping now and no longer uses adjustAvrcpAbsoluteVolume. Bug: 68812037 Test: Compile Change-Id: I9cbf7989e49738c7a43fe3142aced5803111271e --- .../java/android/bluetooth/BluetoothA2dp.java | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b255a43cbe3..419eda3a85e 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -25,7 +25,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.media.AudioManager; import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; @@ -598,34 +597,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - /** - * Tells remote device to adjust volume. Only if absolute volume is - * supported. Uses the following values: - *
          - *
        • {@link AudioManager#ADJUST_LOWER}
        • - *
        • {@link AudioManager#ADJUST_RAISE}
        • - *
        • {@link AudioManager#ADJUST_MUTE}
        • - *
        • {@link AudioManager#ADJUST_UNMUTE}
        • - *
        - * - * @param direction One of the supported adjust values. - * @hide - */ - public void adjustAvrcpAbsoluteVolume(int direction) { - if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume"); - try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.adjustAvrcpAbsoluteVolume(direction); - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - /** * Tells remote device to set an absolute volume. Only if absolute volume is supported * -- GitLab From 88c08ef5922e945a65d5f6e37dd1fb873aae4972 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 2 Mar 2018 13:11:27 -0800 Subject: [PATCH 0904/1408] Bluetooth: Use enums for adapter connection states Bug: 69478930 Test: make, no user visible change Change-Id: I999d0c445fe3b24aca72961c40c3428901542bc1 --- framework/java/android/bluetooth/BluetoothAdapter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index dc761521fbd..be4e207e587 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -537,13 +537,14 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = 3; + public static final int STATE_DISCONNECTING = + BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; -- GitLab From 27128c930b398b0e24074db93396b793058c204f Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 2 Mar 2018 13:11:27 -0800 Subject: [PATCH 0905/1408] Bluetooth: Use enums for adapter connection states Bug: 69478930 Test: make, no user visible change Change-Id: I999d0c445fe3b24aca72961c40c3428901542bc1 (cherry picked from commit ee81f9915041875a8c9b6b71a39cb0508f6d7b22) --- framework/java/android/bluetooth/BluetoothAdapter.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1dc7549e763..ee667c22077 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -541,13 +541,14 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = 3; + public static final int STATE_DISCONNECTING = + BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; -- GitLab From 0b80e8686e31fc090f27596968c37257080fbf76 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 2 Mar 2018 13:08:36 -0800 Subject: [PATCH 0906/1408] Bluetooth: Separate enable and disable Reasons from package name * Reasons to enable or disable Bluetooth should be in a separate namespace away from package names that initiated the enable or disable * Delcare reason code in android/bluetooth/enums.proto as EnableDisableReasonEnum and use these reason code together with package names to log Bluetooth enable and disable events * When reason is ENABLE_DISABLE_REASON_APPLICATION_REQUEST, the external package name is the real reason why Bluetooth is enabled or disabled * Change START_CRASH to START_ERROR since it represents a disable event when Bluetooth failed to start * Change UNEXPECTED_CRASH to CRASH since no crash is expected * Add getEnableDisableReasonString() method to covert reason code to human readable strings in dumpsys output Bug: 74090881 Test: Toggle Bluetooth, airplane mode, BLE scanning Change-Id: I06aa413e2a0a5f016c87759a1b694ef643c9cdee --- .../bluetooth/BluetoothManagerService.java | 145 +++++++++++------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 20777901a3a..029dc7b86d5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -21,6 +21,7 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothGatt; @@ -87,14 +88,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ACTIVE_LOG_MAX_SIZE = 20; private static final int CRASH_LOG_MAX_SIZE = 100; - private static final String REASON_AIRPLANE_MODE = "airplane mode"; - private static final String REASON_DISALLOWED = "disallowed by system"; - private static final String REASON_RESTARTED = "automatic restart"; - private static final String REASON_START_CRASH = "turn-on crash"; - private static final String REASON_SYSTEM_BOOT = "system boot"; - private static final String REASON_UNEXPECTED = "unexpected crash"; - private static final String REASON_USER_SWITCH = "user switch"; - private static final String REASON_RESTORE_USER_SETTING = "restore user setting"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind //Maximum msec to wait for service restart @@ -163,7 +156,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mQuietEnable = false; private boolean mEnable; - private CharSequence timeToLog(long timestamp) { + private static CharSequence timeToLog(long timestamp) { return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp); } @@ -171,29 +164,27 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Used for tracking apps that enabled / disabled Bluetooth. */ private class ActiveLog { + private int mReason; private String mPackageName; private boolean mEnable; private long mTimestamp; - ActiveLog(String packageName, boolean enable, long timestamp) { + ActiveLog(int reason, String packageName, boolean enable, long timestamp) { + mReason = reason; mPackageName = packageName; mEnable = enable; mTimestamp = timestamp; } - public long getTime() { - return mTimestamp; - } - public String toString() { - return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " - + mPackageName; + return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName; } } - private LinkedList mActiveLogs; - private LinkedList mCrashTimestamps; + private final LinkedList mActiveLogs = new LinkedList<>(); + private final LinkedList mCrashTimestamps = new LinkedList<>(); private int mCrashes; private long mLastEnabledTime; @@ -213,8 +204,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Save a ProfileServiceConnections object for each of the bound // bluetooth profile services - private final Map mProfileServices = - new HashMap(); + private final Map mProfileServices = new HashMap<>(); private final boolean mPermissionReviewRequired; @@ -246,7 +236,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( UserManager.DISALLOW_BLUETOOTH)) { updateOppLauncherComponentState(userId, true); // Sharing disallowed - sendDisableMsg(REASON_DISALLOWED); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED, + mContext.getPackageName()); } else { updateOppLauncherComponentState(userId, newRestrictions.getBoolean( UserManager.DISALLOW_BLUETOOTH_SHARING)); @@ -303,10 +294,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(REASON_AIRPLANE_MODE); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE); + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } } @@ -369,8 +363,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mPermissionReviewRequired = context.getResources() .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired); - mActiveLogs = new LinkedList(); - mCrashTimestamps = new LinkedList(); mCrashes = 0; mBluetooth = null; mBluetoothBinder = null; @@ -671,8 +663,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } try { - return (Settings.Global.getInt(mContentResolver, - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; + return Settings.Global.getInt(mContentResolver, + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0; } catch (SettingNotFoundException e) { } return false; @@ -867,7 +859,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized (mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; - sendEnableMsg(true, packageName); + sendEnableMsg(true, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } return true; } @@ -907,7 +900,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, packageName); + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } if (DBG) { Slog.d(TAG, "enable returning"); @@ -943,7 +937,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; - sendDisableMsg(packageName); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName); } return true; } @@ -1103,7 +1098,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Auto-enabling Bluetooth."); } - sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT, + mContext.getPackageName()); } else if (!isNameAndAddressSet()) { if (DBG) { Slog.d(TAG, "Getting adapter name and address"); @@ -1545,20 +1542,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_RESTORE_USER_SETTING: - try { - if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to disabled"); - } - disable(REASON_RESTORE_USER_SETTING, true); - } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to enabled"); - } - enable(REASON_RESTORE_USER_SETTING); + if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { + if (DBG) { + Slog.d(TAG, "Restore Bluetooth state to disabled"); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to change Bluetooth On setting", e); + persistBluetoothSetting(BLUETOOTH_OFF); + mEnableExternal = false; + sendDisableMsg( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, + mContext.getPackageName()); + } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { + if (DBG) { + Slog.d(TAG, "Restore Bluetooth state to enabled"); + } + mQuietEnableExternal = false; + mEnableExternal = true; + // waive WRITE_SECURE_SETTINGS permission check + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, + mContext.getPackageName()); } break; @@ -1585,7 +1587,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } case MESSAGE_ADD_PROXY_DELAYED: { - ProfileServiceConnections psc = mProfileServices.get(new Integer(msg.arg1)); + ProfileServiceConnections psc = mProfileServices.get(msg.arg1); if (psc == null) { break; } @@ -1734,7 +1736,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // log the unexpected crash addCrashLog(); - addActiveLog(REASON_UNEXPECTED, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH, + mContext.getPackageName(), false); if (mEnable) { mEnable = false; // Send a Bluetooth Restart message @@ -1768,7 +1771,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { it doesnt change when IBluetooth service restarts */ mEnable = true; - addActiveLog(REASON_RESTARTED, true); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED, + mContext.getPackageName(), true); handleEnable(mQuietEnable); break; } @@ -1824,7 +1828,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { unbindAllBluetoothProfileServices(); // disable - addActiveLog(REASON_USER_SWITCH, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), false); handleDisable(); // Pbap service need receive STATE_TURNING_OFF intent to close bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, @@ -1862,7 +1867,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; // enable - addActiveLog(REASON_USER_SWITCH, true); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), true); // mEnable flag could have been reset on disableBLE. Reenable it. mEnable = true; handleEnable(mQuietEnable); @@ -2129,23 +2135,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - private void sendDisableMsg(String packageName) { + private void sendDisableMsg(int reason, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); - addActiveLog(packageName, false); + addActiveLog(reason, packageName, false); } - private void sendEnableMsg(boolean quietMode, String packageName) { + private void sendEnableMsg(boolean quietMode, int reason, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); - addActiveLog(packageName, true); + addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } - private void addActiveLog(String packageName, boolean enable) { + private void addActiveLog(int reason, String packageName, boolean enable) { synchronized (mActiveLogs) { if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) { mActiveLogs.remove(); } - mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); + mActiveLogs.add( + new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } } @@ -2176,7 +2183,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { SystemClock.sleep(500); // disable - addActiveLog(REASON_START_CRASH, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR, + mContext.getPackageName(), false); handleDisable(); waitForOnOff(false, true); @@ -2320,4 +2328,29 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(errorMsg); } } + + private static String getEnableDisableReasonString(int reason) { + switch (reason) { + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST: + return "APPLICATION_REQUEST"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE: + return "AIRPLANE_MODE"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED: + return "DISALLOWED"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED: + return "RESTARTED"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR: + return "START_ERROR"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT: + return "SYSTEM_BOOT"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH: + return "CRASH"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH: + return "USER_SWITCH"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: + return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: + default: return "UNKNOWN[" + reason + "]"; + } + } } -- GitLab From f2f77ed3ada721e23e636f07648a254584f7c979 Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 2 Mar 2018 13:08:36 -0800 Subject: [PATCH 0907/1408] Bluetooth: Separate enable and disable Reasons from package name * Reasons to enable or disable Bluetooth should be in a separate namespace away from package names that initiated the enable or disable * Delcare reason code in android/bluetooth/enums.proto as EnableDisableReasonEnum and use these reason code together with package names to log Bluetooth enable and disable events * When reason is ENABLE_DISABLE_REASON_APPLICATION_REQUEST, the external package name is the real reason why Bluetooth is enabled or disabled * Change START_CRASH to START_ERROR since it represents a disable event when Bluetooth failed to start * Change UNEXPECTED_CRASH to CRASH since no crash is expected * Add getEnableDisableReasonString() method to covert reason code to human readable strings in dumpsys output Bug: 74090881 Test: Toggle Bluetooth, airplane mode, BLE scanning Change-Id: I06aa413e2a0a5f016c87759a1b694ef643c9cdee (cherry picked from commit 0dbe66460fa2a5ebb05ca757e58141c213038bc1) --- .../bluetooth/BluetoothManagerService.java | 145 +++++++++++------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 55ad8cc9bc9..02b13807154 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -21,6 +21,7 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothGatt; @@ -87,14 +88,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ACTIVE_LOG_MAX_SIZE = 20; private static final int CRASH_LOG_MAX_SIZE = 100; - private static final String REASON_AIRPLANE_MODE = "airplane mode"; - private static final String REASON_DISALLOWED = "disallowed by system"; - private static final String REASON_RESTARTED = "automatic restart"; - private static final String REASON_START_CRASH = "turn-on crash"; - private static final String REASON_SYSTEM_BOOT = "system boot"; - private static final String REASON_UNEXPECTED = "unexpected crash"; - private static final String REASON_USER_SWITCH = "user switch"; - private static final String REASON_RESTORE_USER_SETTING = "restore user setting"; private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind //Maximum msec to wait for service restart @@ -163,7 +156,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mQuietEnable = false; private boolean mEnable; - private CharSequence timeToLog(long timestamp) { + private static CharSequence timeToLog(long timestamp) { return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp); } @@ -171,29 +164,27 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Used for tracking apps that enabled / disabled Bluetooth. */ private class ActiveLog { + private int mReason; private String mPackageName; private boolean mEnable; private long mTimestamp; - ActiveLog(String packageName, boolean enable, long timestamp) { + ActiveLog(int reason, String packageName, boolean enable, long timestamp) { + mReason = reason; mPackageName = packageName; mEnable = enable; mTimestamp = timestamp; } - public long getTime() { - return mTimestamp; - } - public String toString() { - return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " - + mPackageName; + return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName; } } - private LinkedList mActiveLogs; - private LinkedList mCrashTimestamps; + private final LinkedList mActiveLogs = new LinkedList<>(); + private final LinkedList mCrashTimestamps = new LinkedList<>(); private int mCrashes; private long mLastEnabledTime; @@ -213,8 +204,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Save a ProfileServiceConnections object for each of the bound // bluetooth profile services - private final Map mProfileServices = - new HashMap(); + private final Map mProfileServices = new HashMap<>(); private final boolean mPermissionReviewRequired; @@ -246,7 +236,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( UserManager.DISALLOW_BLUETOOTH)) { updateOppLauncherComponentState(userId, true); // Sharing disallowed - sendDisableMsg(REASON_DISALLOWED); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED, + mContext.getPackageName()); } else { updateOppLauncherComponentState(userId, newRestrictions.getBoolean( UserManager.DISALLOW_BLUETOOTH_SHARING)); @@ -303,10 +294,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(REASON_AIRPLANE_MODE); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE); + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } } @@ -369,8 +363,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mPermissionReviewRequired = context.getResources() .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired); - mActiveLogs = new LinkedList(); - mCrashTimestamps = new LinkedList(); mCrashes = 0; mBluetooth = null; mBluetoothBinder = null; @@ -671,8 +663,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } try { - return (Settings.Global.getInt(mContentResolver, - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; + return Settings.Global.getInt(mContentResolver, + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0; } catch (SettingNotFoundException e) { } return false; @@ -867,7 +859,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized (mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; - sendEnableMsg(true, packageName); + sendEnableMsg(true, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } return true; } @@ -907,7 +900,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, packageName); + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } if (DBG) { Slog.d(TAG, "enable returning"); @@ -943,7 +937,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; - sendDisableMsg(packageName); + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName); } return true; } @@ -1127,7 +1122,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Auto-enabling Bluetooth."); } - sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT); + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT, + mContext.getPackageName()); } else if (!isNameAndAddressSet()) { if (DBG) { Slog.d(TAG, "Getting adapter name and address"); @@ -1569,20 +1566,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_RESTORE_USER_SETTING: - try { - if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to disabled"); - } - disable(REASON_RESTORE_USER_SETTING, true); - } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to enabled"); - } - enable(REASON_RESTORE_USER_SETTING); + if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { + if (DBG) { + Slog.d(TAG, "Restore Bluetooth state to disabled"); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to change Bluetooth On setting", e); + persistBluetoothSetting(BLUETOOTH_OFF); + mEnableExternal = false; + sendDisableMsg( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, + mContext.getPackageName()); + } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { + if (DBG) { + Slog.d(TAG, "Restore Bluetooth state to enabled"); + } + mQuietEnableExternal = false; + mEnableExternal = true; + // waive WRITE_SECURE_SETTINGS permission check + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, + mContext.getPackageName()); } break; @@ -1609,7 +1611,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } case MESSAGE_ADD_PROXY_DELAYED: { - ProfileServiceConnections psc = mProfileServices.get(new Integer(msg.arg1)); + ProfileServiceConnections psc = mProfileServices.get(msg.arg1); if (psc == null) { break; } @@ -1758,7 +1760,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // log the unexpected crash addCrashLog(); - addActiveLog(REASON_UNEXPECTED, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH, + mContext.getPackageName(), false); if (mEnable) { mEnable = false; // Send a Bluetooth Restart message @@ -1792,7 +1795,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { it doesnt change when IBluetooth service restarts */ mEnable = true; - addActiveLog(REASON_RESTARTED, true); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED, + mContext.getPackageName(), true); handleEnable(mQuietEnable); break; } @@ -1848,7 +1852,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { unbindAllBluetoothProfileServices(); // disable - addActiveLog(REASON_USER_SWITCH, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), false); handleDisable(); // Pbap service need receive STATE_TURNING_OFF intent to close bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, @@ -1886,7 +1891,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; // enable - addActiveLog(REASON_USER_SWITCH, true); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), true); // mEnable flag could have been reset on disableBLE. Reenable it. mEnable = true; handleEnable(mQuietEnable); @@ -2153,23 +2159,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - private void sendDisableMsg(String packageName) { + private void sendDisableMsg(int reason, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); - addActiveLog(packageName, false); + addActiveLog(reason, packageName, false); } - private void sendEnableMsg(boolean quietMode, String packageName) { + private void sendEnableMsg(boolean quietMode, int reason, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); - addActiveLog(packageName, true); + addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } - private void addActiveLog(String packageName, boolean enable) { + private void addActiveLog(int reason, String packageName, boolean enable) { synchronized (mActiveLogs) { if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) { mActiveLogs.remove(); } - mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); + mActiveLogs.add( + new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } } @@ -2200,7 +2207,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { SystemClock.sleep(500); // disable - addActiveLog(REASON_START_CRASH, false); + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR, + mContext.getPackageName(), false); handleDisable(); waitForOnOff(false, true); @@ -2344,4 +2352,29 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(errorMsg); } } + + private static String getEnableDisableReasonString(int reason) { + switch (reason) { + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST: + return "APPLICATION_REQUEST"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE: + return "AIRPLANE_MODE"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED: + return "DISALLOWED"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED: + return "RESTARTED"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR: + return "START_ERROR"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT: + return "SYSTEM_BOOT"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH: + return "CRASH"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH: + return "USER_SWITCH"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: + return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: + default: return "UNKNOWN[" + reason + "]"; + } + } } -- GitLab From 5a96fe77b8727c8077f82007af1a87d4ff7aeacb Mon Sep 17 00:00:00 2001 From: Miao-chen Chou Date: Wed, 14 Mar 2018 15:00:23 -0700 Subject: [PATCH 0908/1408] Bluetooth: preserve one advertisement slot for GMS core This brings back the check on the multiple advertisement support before granting any Bluetooth LE advertiser. In other words, one slot is preserved for GMS core. Bug: 74819586 Test: Run an BLE application and verify the number of advertisements Change-Id: Iac3b47c76c6f699018982a69e9b04a9d8c631bfb --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index be4e207e587..6aabe18ba8f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -676,6 +676,10 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return null; } + if (!isMultipleAdvertisementSupported()) { + Log.e(TAG, "Bluetooth LE advertising not supported"); + return null; + } synchronized (mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); -- GitLab From 882bbae29f30c4e82308d2e1058191413cf4e686 Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Tue, 13 Mar 2018 13:37:18 -0700 Subject: [PATCH 0909/1408] Logging: Bluetooth Enabled Logs bluetooth enabled state changed. Test: verified logs appeared in logcat Change-Id: I2c553aa68e566f9dfd0a5e353a5a6e15a06e7893 --- .../android/server/bluetooth/BluetoothManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 029dc7b86d5..9d62667c167 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import android.util.StatsLog; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -2154,6 +2155,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mActiveLogs.add( new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } + + int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : + StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; + StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, + Binder.getCallingUid(), null, state, reason, packageName); } private void addCrashLog() { -- GitLab From bfcc00d027dc32e25c9c309120af8d5281988219 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 16 Mar 2018 04:00:27 -0700 Subject: [PATCH 0910/1408] Add the AVRCP Target Service (2/2) Add the AVRCP Target constant to Bluetooth Profile. Also remove redundant public static final modifier on the constants. Bug: 68854188 Test: Compiles and local test with service enabled Change-Id: If2ec607fc704c225f8903d438fe970dfafac25f1 --- .../android/bluetooth/BluetoothProfile.java | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 656188fbdfb..6aeb94da117 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -38,7 +38,7 @@ public interface BluetoothProfile { * This extra represents the current connection state of the profile of the * Bluetooth device. */ - public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; + String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; /** * Extra for the connection state intents of the individual profiles. @@ -46,123 +46,130 @@ public interface BluetoothProfile { * This extra represents the previous connection state of the profile of the * Bluetooth device. */ - public static final String EXTRA_PREVIOUS_STATE = + String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + int STATE_CONNECTING = 1; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + int STATE_CONNECTED = 2; /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = 3; + int STATE_DISCONNECTING = 3; /** * Headset and Handsfree profile */ - public static final int HEADSET = 1; + int HEADSET = 1; /** * A2DP profile. */ - public static final int A2DP = 2; + int A2DP = 2; /** * Health Profile */ - public static final int HEALTH = 3; + int HEALTH = 3; /** * HID Host * * @hide */ - public static final int HID_HOST = 4; + int HID_HOST = 4; /** * PAN Profile * * @hide */ - public static final int PAN = 5; + int PAN = 5; /** * PBAP * * @hide */ - public static final int PBAP = 6; + int PBAP = 6; /** * GATT */ - public static final int GATT = 7; + int GATT = 7; /** * GATT_SERVER */ - public static final int GATT_SERVER = 8; + int GATT_SERVER = 8; /** * MAP Profile * * @hide */ - public static final int MAP = 9; + int MAP = 9; /* * SAP Profile * @hide */ - public static final int SAP = 10; + int SAP = 10; /** * A2DP Sink Profile * * @hide */ - public static final int A2DP_SINK = 11; + int A2DP_SINK = 11; /** * AVRCP Controller Profile * * @hide */ - public static final int AVRCP_CONTROLLER = 12; + int AVRCP_CONTROLLER = 12; + + /** + * AVRCP Target Profile + * + * @hide + */ + int AVRCP = 13; /** * Headset Client - HFP HF Role * * @hide */ - public static final int HEADSET_CLIENT = 16; + int HEADSET_CLIENT = 16; /** * PBAP Client * * @hide */ - public static final int PBAP_CLIENT = 17; + int PBAP_CLIENT = 17; /** * MAP Messaging Client Equipment (MCE) * * @hide */ - public static final int MAP_CLIENT = 18; + int MAP_CLIENT = 18; /** * HID Device */ - public static final int HID_DEVICE = 19; + int HID_DEVICE = 19; /** * Object Push Profile (OPP) * * @hide */ - public static final int OPP = 20; + int OPP = 20; /** * Hearing Aid Device @@ -185,7 +192,7 @@ public interface BluetoothProfile { * * @hide **/ - public static final int PRIORITY_AUTO_CONNECT = 1000; + int PRIORITY_AUTO_CONNECT = 1000; /** * Default priority for devices that allow incoming @@ -194,7 +201,7 @@ public interface BluetoothProfile { * @hide **/ @SystemApi - public static final int PRIORITY_ON = 100; + int PRIORITY_ON = 100; /** * Default priority for devices that does not allow incoming @@ -203,14 +210,14 @@ public interface BluetoothProfile { * @hide **/ @SystemApi - public static final int PRIORITY_OFF = 0; + int PRIORITY_OFF = 0; /** * Default priority when not set or when the device is unpaired * * @hide */ - public static final int PRIORITY_UNDEFINED = -1; + int PRIORITY_UNDEFINED = -1; /** * Get connected devices for this specific profile. -- GitLab From a70a16b3e9d2433b7517e679f630d9dab33eea8a Mon Sep 17 00:00:00 2001 From: Qiyu Hu Date: Fri, 16 Mar 2018 12:22:16 -0700 Subject: [PATCH 0911/1408] Bluetooth: preserve one advertisement slot for GMS core This brings back the check on the multiple advertisement support before granting any Bluetooth LE advertiser. In other words, one slot is preserved for GMS core. Bug: 74819586 Test: Run an BLE application and verify the number of advertisements Change-Id: I05014e597bd5868f18a81842dc341e47991f2c79 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ee667c22077..b9e80e40679 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -680,6 +680,10 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return null; } + if (!isMultipleAdvertisementSupported()) { + Log.e(TAG, "Bluetooth LE advertising not supported"); + return null; + } synchronized (mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); -- GitLab From 406d4f911c2a2808f9687348a8484f74320734b6 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 19 Mar 2018 13:06:45 -0700 Subject: [PATCH 0912/1408] Remove BLE App entry from mBleApps list when App died When a registered BLE App unexpectedly dies, its entry in mBleApps list needs to be cleanup/removed. Test: Manual test by repeatedly killing the gms core process. Bug: 74076974 Change-Id: I2dc86b782dd6b07017a360a0b709504f0a375969 --- .../server/bluetooth/BluetoothManagerService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 20777901a3a..a93370253de 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -640,6 +640,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Binder is dead - unregister " + mPackageName); } + + for (Map.Entry entry : mBleApps.entrySet()) { + IBinder token = entry.getKey(); + ClientDeathRecipient deathRec = entry.getValue(); + if (deathRec.equals(this)) { + mBleApps.remove(token); + break; + } + } + if (isBleAppPresent()) { // Nothing to do, another app is here. return; -- GitLab From befadbc4a5450e3140293c37ecd5533581659687 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 16 Mar 2018 09:15:48 -0700 Subject: [PATCH 0913/1408] Hearing Aid Profile: set and get activeDevice Add setActiveDevice() for Hearing Aid Profile in SettingsLib Bug: 69623109 Test: robolectric test and manual test Change-Id: I70eafe030747053876e2ab8a125d5dd01c5e0eb9 --- .../bluetooth/BluetoothHearingAid.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 647e0d033fb..8f8083ed73e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -378,6 +379,76 @@ public final class BluetoothHearingAid implements BluetoothProfile { } } + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, Hearing Aid audio + * streaming is to the active Hearing Aid device. If a remote device + * is not connected, it cannot be selected as active. + * + *

        This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + mService.setActiveDevice(device); + return true; + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Check whether the device is active. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isActiveDevice(@Nullable BluetoothDevice device) { + if (VDBG) log("isActiveDevice()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + return mService.isActiveDevice(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Set priority of the profile * -- GitLab From 71dbfbc68629e9cfdb3e383ab290d13c4caf291f Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 20 Mar 2018 16:54:27 -0700 Subject: [PATCH 0914/1408] Calls unlinkToDeath in binderDied to deregister When a registered BLE App unexpectedly dies and the binderDied callback is called, the unlinkToDeath is called to remove linkage. Also, refactor code to use an existing function. Test: Manual test by repeatedly killing the gms core process. Bug: 74076974 Change-Id: If47a534ecafe7fceae14f8cf8526987cabd279cd --- .../bluetooth/BluetoothManagerService.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index a93370253de..f3333c0126d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -645,29 +645,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IBinder token = entry.getKey(); ClientDeathRecipient deathRec = entry.getValue(); if (deathRec.equals(this)) { - mBleApps.remove(token); + updateBleAppCount(token, false, mPackageName); break; } } - - if (isBleAppPresent()) { - // Nothing to do, another app is here. - return; - } - if (DBG) { - Slog.d(TAG, "Disabling LE only mode after application crash"); - } - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { - mEnable = false; - mBluetooth.onBrEdrDown(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } } public String getPackageName() { -- GitLab From cc8b9b19d5f1990d72f95ec43e081aa79bf84c8c Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Tue, 27 Feb 2018 17:58:16 +0000 Subject: [PATCH 0915/1408] HIDD: Address API Review concerns * Replace bare field usage with getter methods; * Remove Builder; * Move BluetoothHidDeviceCallback to inner class; * Remove toArray() and equals(); * Throw IllegalArgumentException where applicable; * Add an Executor parameter before Callback; Bug: 72168436, 72168126 Test: make update-api, make, make sl4a.Common Change-Id: I13095458bf3ded7a376e8d20fd13df12ef426693 --- .../android/bluetooth/BluetoothHidDevice.java | 358 ++++++++++++++---- .../BluetoothHidDeviceAppQosSettings.java | 188 +++------ .../BluetoothHidDeviceAppSdpSettings.java | 60 +-- .../bluetooth/BluetoothHidDeviceCallback.java | 120 ------ 4 files changed, 369 insertions(+), 357 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceCallback.java diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 2fab305ba8b..a3d6968e4c7 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -27,8 +27,8 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.concurrent.Executor; /** * Provides the public APIs to control the Bluetooth HID Device profile. @@ -37,7 +37,6 @@ import java.util.List; * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. */ public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** @@ -62,106 +61,327 @@ public final class BluetoothHidDevice implements BluetoothProfile { "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; /** - * Constants representing device subclass. + * Constant representing unspecified HID device subclass. * * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) + * BluetoothHidDeviceAppQosSettings, Executor, Callback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; + /** + * Constant representing keyboard subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; + /** + * Constant representing mouse subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_MOUSE = (byte) 0x80; + /** + * Constant representing combo keyboard and mouse subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_COMBO = (byte) 0xC0; + /** + * Constant representing uncategorized HID device subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; + /** + * Constant representing joystick subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; + /** + * Constant representing gamepad subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; + /** + * Constant representing remote control subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; + /** + * Constant representing sensing device subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; + /** + * Constant representing digitizer tablet subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; + /** + * Constant representing card reader subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** - * Constants representing report types. + * Constant representing HID Input Report type. * - * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int) - * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[]) + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) */ public static final byte REPORT_TYPE_INPUT = (byte) 1; + /** + * Constant representing HID Output Report type. + * + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) + */ public static final byte REPORT_TYPE_OUTPUT = (byte) 2; + /** + * Constant representing HID Feature Report type. + * + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) + */ public static final byte REPORT_TYPE_FEATURE = (byte) 3; /** - * Constants representing error response for Set Report. + * Constant representing success response for Set Report. * - * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) */ public static final byte ERROR_RSP_SUCCESS = (byte) 0; + /** + * Constant representing error response for Set Report due to "not ready". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_NOT_READY = (byte) 1; + /** + * Constant representing error response for Set Report due to "invalid report ID". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; + /** + * Constant representing error response for Set Report due to "unsupported request". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; + /** + * Constant representing error response for Set Report due to "invalid parameter". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; + /** + * Constant representing error response for Set Report with unknown reason. + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_UNKNOWN = (byte) 14; /** - * Constants representing protocol mode used set by host. Default is always {@link + * Constant representing boot protocol mode used set by host. Default is always {@link * #PROTOCOL_REPORT_MODE} unless notified otherwise. * - * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte) + * @see Callback#onSetProtocol(BluetoothDevice, byte) */ public static final byte PROTOCOL_BOOT_MODE = (byte) 0; + /** + * Constant representing report protocol mode used set by host. Default is always {@link + * #PROTOCOL_REPORT_MODE} unless notified otherwise. + * + * @see Callback#onSetProtocol(BluetoothDevice, byte) + */ public static final byte PROTOCOL_REPORT_MODE = (byte) 1; - private Context mContext; + /** + * The template class that applications use to call callback functions on events from the HID + * host. Callback functions are wrapped in this class and registered to the Android system + * during app registration. + */ + public abstract static class Callback { + + private static final String TAG = "BluetoothHidDevCallback"; + + /** + * Callback called when application registration state changes. Usually it's called due to + * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], + * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also + * unsolicited in case e.g. Bluetooth was turned off in which case application is + * unregistered automatically. + * + * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently + * has Virtual Cable established with device. Only valid when application is registered, + * can be null. + * @param registered true if application is registered, false + * otherwise. + */ + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + Log.d( + TAG, + "onAppStatusChanged: pluggedDevice=" + + pluggedDevice + + " registered=" + + registered); + } - private ServiceListener mServiceListener; + /** + * Callback called when connection state with remote host was changed. Application can + * assume than Virtual Cable is established when called with {@link + * BluetoothProfile#STATE_CONNECTED} state. + * + * @param device {@link BluetoothDevice} object representing host device which connection + * state was changed. + * @param state Connection state as defined in {@link BluetoothProfile}. + */ + public void onConnectionStateChanged(BluetoothDevice device, int state) { + Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); + } - private volatile IBluetoothHidDevice mService; + /** + * Callback called when GET_REPORT is received from remote host. Should be replied by + * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, + * byte[])}. + * + * @param type Requested Report Type. + * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. + * @param bufferSize Requested buffer size, application shall respond with at least given + * number of bytes. + */ + public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { + Log.d( + TAG, + "onGetReport: device=" + + device + + " type=" + + type + + " id=" + + id + + " bufferSize=" + + bufferSize); + } + + /** + * Callback called when SET_REPORT is received from remote host. In case received data are + * invalid, application shall respond with {@link + * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. + * + * @param type Report Type. + * @param id Report Id. + * @param data Report data. + */ + public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); + } + + /** + * Callback called when SET_PROTOCOL is received from remote host. Application shall use + * this information to send only reports valid for given protocol mode. By default, {@link + * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * + * @param protocol Protocol Mode. + */ + public void onSetProtocol(BluetoothDevice device, byte protocol) { + Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); + } + /** + * Callback called when report data is received over interrupt channel. Report Type is + * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * + * @param reportId Report Id. + * @param data Report data. + */ + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); + } + + /** + * Callback called when Virtual Cable is removed. After this callback is received connection + * will be disconnected automatically. + */ + public void onVirtualCableUnplug(BluetoothDevice device) { + Log.d(TAG, "onVirtualCableUnplug: device=" + device); + } + } + + private Context mContext; + private ServiceListener mServiceListener; + private volatile IBluetoothHidDevice mService; private BluetoothAdapter mAdapter; - private static class BluetoothHidDeviceCallbackWrapper - extends IBluetoothHidDeviceCallback.Stub { + private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { - private BluetoothHidDeviceCallback mCallback; + private final Executor mExecutor; + private final Callback mCallback; - public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) { + CallbackWrapper(Executor executor, Callback callback) { + mExecutor = executor; mCallback = callback; } @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - mCallback.onAppStatusChanged(pluggedDevice, registered); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); } @Override public void onConnectionStateChanged(BluetoothDevice device, int state) { - mCallback.onConnectionStateChanged(device, state); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); } @Override public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - mCallback.onGetReport(device, type, id, bufferSize); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); } @Override public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - mCallback.onSetReport(device, type, id, data); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); } @Override public void onSetProtocol(BluetoothDevice device, byte protocol) { - mCallback.onSetProtocol(device, protocol); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); } @Override public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - mCallback.onInterruptData(device, reportId, data); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); } @Override public void onVirtualCableUnplug(BluetoothDevice device) { - mCallback.onVirtualCableUnplug(device); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); } } @@ -213,8 +433,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { }; BluetoothHidDevice(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothHidDevice"); - mContext = context; mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -245,7 +463,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } void doUnbind() { - Log.d(TAG, "Unbinding HidDevService"); if (mService != null) { mService = null; try { @@ -257,8 +474,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } void close() { - Log.v(TAG, "close()"); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -277,8 +492,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List getConnectedDevices() { - Log.v(TAG, "getConnectedDevices()"); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -290,14 +503,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); } - return new ArrayList(); + return new ArrayList<>(); } /** {@inheritDoc} */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -309,14 +520,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); } - return new ArrayList(); + return new ArrayList<>(); } /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - Log.v(TAG, "getConnectionState(): device=" + device); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -336,9 +545,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { * when application is registered. Only one application can be registered at one time. When an * application is registered, the HID Host service will be disabled until it is unregistered. * When no longer used, application should be unregistered using {@link #unregisterApp()}. The - * registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to - * the return value of this method. + * app will be automatically unregistered if it is not foreground. The registration status + * should be tracked by the application by handling callback from Callback#onAppStatusChanged. + * The app registration status is not related to the return value of this method. * * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID * Device SDP record is required. @@ -348,27 +557,36 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The * Outgoing QoS Settings is not required. Use null or default * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be - * sent. The BluetoothHidDeviceCallback object is required. + * @param executor {@link Executor} object on which callback will be executed. The Executor + * object is required. + * @param callback {@link Callback} object to which callback messages will be sent. The Callback + * object is required. * @return true if the command is successfully sent; otherwise false. */ - public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, - BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, - BluetoothHidDeviceCallback callback) { - Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos - + " callback=" + callback); - + public boolean registerApp( + BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, + BluetoothHidDeviceAppQosSettings outQos, + Executor executor, + Callback callback) { boolean result = false; - if (sdp == null || callback == null) { - return false; + if (sdp == null) { + throw new IllegalArgumentException("sdp parameter cannot be null"); + } + + if (executor == null) { + throw new IllegalArgumentException("executor parameter cannot be null"); + } + + if (callback == null) { + throw new IllegalArgumentException("callback parameter cannot be null"); } final IBluetoothHidDevice service = mService; if (service != null) { try { - BluetoothHidDeviceCallbackWrapper cbw = - new BluetoothHidDeviceCallbackWrapper(callback); + CallbackWrapper cbw = new CallbackWrapper(executor, callback); result = service.registerApp(sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); @@ -384,16 +602,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Unregisters application. Active connection will be disconnected and no new connections will * be allowed until registered again using {@link #registerApp * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should - * be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to - * the return value of this method. + * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be + * tracked by the application by handling callback from Callback#onAppStatusChanged. The app + * registration status is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean unregisterApp() { - Log.v(TAG, "unregisterApp()"); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -437,7 +652,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Sends report to remote host as reply for GET_REPORT request from {@link - * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. + * Callback#onGetReport(BluetoothDevice, byte, byte, int)}. * * @param type Report Type, as in request. * @param id Report Id, as in request. @@ -445,8 +660,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @return true if the command is successfully sent; otherwise false. */ public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -465,14 +678,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Sends error handshake message as reply for invalid SET_REPORT request from {@link - * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. + * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. */ public boolean reportError(BluetoothDevice device, byte error) { - Log.v(TAG, "reportError(): device=" + device + " error=" + error); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -496,8 +707,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public boolean unplug(BluetoothDevice device) { - Log.v(TAG, "unplug(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -517,15 +726,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be - * tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to - * the return value of this method. + * tracked by the application by handling callback from Callback#onConnectionStateChanged. The + * connection state is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean connect(BluetoothDevice device) { - Log.v(TAG, "connect(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -544,14 +750,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Disconnects from currently connected host. The connection state should be tracked by the - * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged. - * The connection state is not related to the return value of this method. + * application by handling callback from Callback#onConnectionStateChanged. The connection state + * is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean disconnect(BluetoothDevice device) { - Log.v(TAG, "disconnect(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index c05df2d23e4..a485b898c2d 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -29,12 +29,12 @@ import android.os.Parcelable; */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { - public final int serviceType; - public final int tokenRate; - public final int tokenBucketSize; - public final int peakBandwidth; - public final int latency; - public final int delayVariation; + private final int mServiceType; + private final int mTokenRate; + private final int mTokenBucketSize; + private final int mPeakBandwidth; + private final int mLatency; + private final int mDelayVariation; public static final int SERVICE_NO_TRAFFIC = 0x00; public static final int SERVICE_BEST_EFFORT = 0x01; @@ -44,38 +44,53 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. - * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. + * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and + * Appendix D for parameters. * - * @param serviceType L2CAP service type - * @param tokenRate L2CAP token rate - * @param tokenBucketSize L2CAP token bucket size - * @param peakBandwidth L2CAP peak bandwidth - * @param latency L2CAP latency - * @param delayVariation L2CAP delay variation + * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT + * @param tokenRate L2CAP token rate, default = 0 + * @param tokenBucketSize L2CAP token bucket size, default = 0 + * @param peakBandwidth L2CAP peak bandwidth, default = 0 + * @param latency L2CAP latency, default = MAX + * @param delayVariation L2CAP delay variation, default = MAX */ - public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, - int peakBandwidth, int latency, int delayVariation) { - this.serviceType = serviceType; - this.tokenRate = tokenRate; - this.tokenBucketSize = tokenBucketSize; - this.peakBandwidth = peakBandwidth; - this.latency = latency; - this.delayVariation = delayVariation; + public BluetoothHidDeviceAppQosSettings( + int serviceType, + int tokenRate, + int tokenBucketSize, + int peakBandwidth, + int latency, + int delayVariation) { + mServiceType = serviceType; + mTokenRate = tokenRate; + mTokenBucketSize = tokenBucketSize; + mPeakBandwidth = peakBandwidth; + mLatency = latency; + mDelayVariation = delayVariation; } - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppQosSettings) { - BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; - return this.serviceType == qos.serviceType - && this.tokenRate == qos.tokenRate - && this.tokenBucketSize == qos.tokenBucketSize - && this.peakBandwidth == qos.peakBandwidth - && this.latency == qos.latency - && this.delayVariation == qos.delayVariation; - } - return false; + public int getServiceType() { + return mServiceType; + } + + public int getTokenRate() { + return mTokenRate; + } + + public int getTokenBucketSize() { + return mTokenBucketSize; + } + + public int getPeakBandwidth() { + return mPeakBandwidth; + } + + public int getLatency() { + return mLatency; + } + + public int getDelayVariation() { + return mDelayVariation; } @Override @@ -106,104 +121,11 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeInt(serviceType); - out.writeInt(tokenRate); - out.writeInt(tokenBucketSize); - out.writeInt(peakBandwidth); - out.writeInt(latency); - out.writeInt(delayVariation); - } - - /** @return an int array representation of this instance */ - public int[] toArray() { - return new int[] { - serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation - }; - } - - /** A helper to build the BluetoothHidDeviceAppQosSettings object. */ - public static class Builder { - // Optional parameters - initialized to default values - private int mServiceType = SERVICE_BEST_EFFORT; - private int mTokenRate = 0; - private int mTokenBucketSize = 0; - private int mPeakBandwidth = 0; - private int mLatency = MAX; - private int mDelayVariation = MAX; - - /** - * Set the service type. - * - * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT, - * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. - * @return BluetoothHidDeviceAppQosSettings Builder with specified service type. - */ - public Builder serviceType(int val) { - mServiceType = val; - return this; - } - /** - * Set the token rate. - * - * @param val token rate - * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate. - */ - public Builder tokenRate(int val) { - mTokenRate = val; - return this; - } - - /** - * Set the bucket size. - * - * @param val bucket size - * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size. - */ - public Builder tokenBucketSize(int val) { - mTokenBucketSize = val; - return this; - } - - /** - * Set the peak bandwidth. - * - * @param val peak bandwidth - * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth. - */ - public Builder peakBandwidth(int val) { - mPeakBandwidth = val; - return this; - } - /** - * Set the latency. - * - * @param val latency - * @return BluetoothHidDeviceAppQosSettings Builder with specified latency. - */ - public Builder latency(int val) { - mLatency = val; - return this; - } - - /** - * Set the delay variation. - * - * @param val delay variation - * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation. - */ - public Builder delayVariation(int val) { - mDelayVariation = val; - return this; - } - - /** - * Build the BluetoothHidDeviceAppQosSettings object. - * - * @return BluetoothHidDeviceAppQosSettings object with current settings. - */ - public BluetoothHidDeviceAppQosSettings build() { - return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize, - mPeakBandwidth, mLatency, mDelayVariation); - } + out.writeInt(mServiceType); + out.writeInt(mTokenRate); + out.writeInt(mTokenBucketSize); + out.writeInt(mPeakBandwidth); + out.writeInt(mLatency); + out.writeInt(mDelayVariation); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 562c559eddc..237082e4cb6 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; /** * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application. @@ -31,11 +30,11 @@ import java.util.Arrays; */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { - public final String name; - public final String description; - public final String provider; - public final byte subclass; - public final byte[] descriptors; + private final String mName; + private final String mDescription; + private final String mProvider; + private final byte mSubclass; + private final byte[] mDescriptors; /** * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. @@ -52,24 +51,31 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { */ public BluetoothHidDeviceAppSdpSettings( String name, String description, String provider, byte subclass, byte[] descriptors) { - this.name = name; - this.description = description; - this.provider = provider; - this.subclass = subclass; - this.descriptors = descriptors.clone(); + mName = name; + mDescription = description; + mProvider = provider; + mSubclass = subclass; + mDescriptors = descriptors.clone(); } - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppSdpSettings) { - BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; - return this.name.equals(sdp.name) - && this.description.equals(sdp.description) - && this.provider.equals(sdp.provider) - && this.subclass == sdp.subclass - && Arrays.equals(this.descriptors, sdp.descriptors); - } - return false; + public String getName() { + return mName; + } + + public String getDescription() { + return mDescription; + } + + public String getProvider() { + return mProvider; + } + + public byte getSubclass() { + return mSubclass; + } + + public byte[] getDescriptors() { + return mDescriptors; } @Override @@ -99,10 +105,10 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(name); - out.writeString(description); - out.writeString(provider); - out.writeByte(subclass); - out.writeByteArray(descriptors); + out.writeString(mName); + out.writeString(mDescription); + out.writeString(mProvider); + out.writeByte(mSubclass); + out.writeByteArray(mDescriptors); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java deleted file mode 100644 index e71b00f2349..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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.util.Log; - -/** - * The template class that applications use to call callback functions on events from the HID host. - * Callback functions are wrapped in this class and registered to the Android system during app - * registration. - * - *

        {@see BluetoothHidDevice} - */ -public abstract class BluetoothHidDeviceCallback { - - private static final String TAG = "BluetoothHidDevCallback"; - - /** - * Callback called when application registration state changes. Usually it's called due to - * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], - * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also - * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered - * automatically. - * - * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has - * Virtual Cable established with device. Only valid when application is registered, can be - * null. - * @param registered true if application is registered, false - * otherwise. - */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Log.d(TAG, - "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); - } - - /** - * Callback called when connection state with remote host was changed. Application can assume - * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED} - * state. - * - * @param device {@link BluetoothDevice} object representing host device which connection state - * was changed. - * @param state Connection state as defined in {@link BluetoothProfile}. - */ - public void onConnectionStateChanged(BluetoothDevice device, int state) { - Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); - } - - /** - * Callback called when GET_REPORT is received from remote host. Should be replied by - * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, - * byte[])}. - * - * @param type Requested Report Type. - * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. - * @param bufferSize Requested buffer size, application shall respond with at least given number - * of bytes. - */ - public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" - + bufferSize); - } - - /** - * Callback called when SET_REPORT is received from remote host. In case received data are - * invalid, application shall respond with {@link - * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. - * - * @param type Report Type. - * @param id Report Id. - * @param data Report data. - */ - public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); - } - - /** - * Callback called when SET_PROTOCOL is received from remote host. Application shall use this - * information to send only reports valid for given protocol mode. By default, {@link - * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. - * - * @param protocol Protocol Mode. - */ - public void onSetProtocol(BluetoothDevice device, byte protocol) { - Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); - } - - /** - * Callback called when report data is received over interrupt channel. Report Type is assumed - * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. - * - * @param reportId Report Id. - * @param data Report data. - */ - public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); - } - - /** - * Callback called when Virtual Cable is removed. After this callback is - * received connection will be disconnected automatically. - */ - public void onVirtualCableUnplug(BluetoothDevice device) { - Log.d(TAG, "onVirtualCableUnplug: device=" + device); - } -} -- GitLab From 5c01a03a2224bea2fc4311883da3c7c6ebceb60c Mon Sep 17 00:00:00 2001 From: Yueming Wang Date: Wed, 21 Mar 2018 18:20:34 +0000 Subject: [PATCH 0916/1408] Revert "Logging: Bluetooth Enabled" This reverts commit 882bbae29f30c4e82308d2e1058191413cf4e686. Reason for revert: Reverting this CL as it causes several build breakages in git_stage-aosp-master Change-Id: I4a29f7313857a561452c01fb331accbf59c64a72 --- .../android/server/bluetooth/BluetoothManagerService.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 9d62667c167..029dc7b86d5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,7 +60,6 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; -import android.util.StatsLog; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -2155,11 +2154,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mActiveLogs.add( new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } - - int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : - StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; - StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, - Binder.getCallingUid(), null, state, reason, packageName); } private void addCrashLog() { -- GitLab From 2ac1380d96ba9eab1805d83035c51bfb0c36727e Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Thu, 22 Mar 2018 18:30:31 +0000 Subject: [PATCH 0917/1408] Revert "Revert "Logging: Bluetooth Enabled"" This reverts commit 5c01a03a2224bea2fc4311883da3c7c6ebceb60c. Reason for revert: The dummy StatsLog class was merged into stage-aosp-master and oc-mr1-dev-plus-aosp, which were the branches that broke initially. Change-Id: Iab9bac487d845db0ba70b2622679523604b4944e --- .../android/server/bluetooth/BluetoothManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 029dc7b86d5..9d62667c167 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import android.util.StatsLog; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -2154,6 +2155,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mActiveLogs.add( new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } + + int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : + StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; + StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, + Binder.getCallingUid(), null, state, reason, packageName); } private void addCrashLog() { -- GitLab From c0e13903ac135023d9b8d25c2c78e1a2d421c99f Mon Sep 17 00:00:00 2001 From: Tej Singh Date: Thu, 22 Mar 2018 18:30:31 +0000 Subject: [PATCH 0918/1408] Logging: Bluetooth Enabled Logs bluetooth enabled state changed atom. Note: this is cherry picked from aosp: go/aog/647319. Bug: b/72320489 Bug: b/74457175 Test: verified logs appear in adb logcat -b stats Merged-In: Iab9bac487d845db0ba70b2622679523604b4944e Change-Id: Iab9bac487d845db0ba70b2622679523604b4944e --- .../android/server/bluetooth/BluetoothManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 02b13807154..7f2e0b81239 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -60,6 +60,7 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import android.util.StatsLog; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -2178,6 +2179,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mActiveLogs.add( new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } + + int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : + StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; + StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, + Binder.getCallingUid(), null, state, reason, packageName); } private void addCrashLog() { -- GitLab From 690633fc66ed83b20c75b7e116123ff3e078099f Mon Sep 17 00:00:00 2001 From: Ivan Podogov Date: Tue, 27 Feb 2018 17:58:16 +0000 Subject: [PATCH 0919/1408] HIDD: Address API Review concerns * Replace bare field usage with getter methods; * Remove Builder; * Move BluetoothHidDeviceCallback to inner class; * Remove toArray() and equals(); * Throw IllegalArgumentException where applicable; * Add an Executor parameter before Callback; Bug: 72168436, 72168126 Test: make update-api, make, make sl4a.Common Change-Id: I13095458bf3ded7a376e8d20fd13df12ef426693 (cherry picked from commit f2f5dc355fa9a962ded0d29368535796aa4116d8) --- .../android/bluetooth/BluetoothHidDevice.java | 358 ++++++++++++++---- .../BluetoothHidDeviceAppQosSettings.java | 188 +++------ .../BluetoothHidDeviceAppSdpSettings.java | 60 +-- .../bluetooth/BluetoothHidDeviceCallback.java | 120 ------ 4 files changed, 369 insertions(+), 357 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothHidDeviceCallback.java diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 77ab7319046..cb1d1062681 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -27,8 +27,8 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.concurrent.Executor; /** * Provides the public APIs to control the Bluetooth HID Device profile. @@ -37,7 +37,6 @@ import java.util.List; * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. */ public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothHidDevice.class.getSimpleName(); /** @@ -62,106 +61,327 @@ public final class BluetoothHidDevice implements BluetoothProfile { "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; /** - * Constants representing device subclass. + * Constant representing unspecified HID device subclass. * * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback) + * BluetoothHidDeviceAppQosSettings, Executor, Callback) */ public static final byte SUBCLASS1_NONE = (byte) 0x00; + /** + * Constant representing keyboard subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; + /** + * Constant representing mouse subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_MOUSE = (byte) 0x80; + /** + * Constant representing combo keyboard and mouse subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS1_COMBO = (byte) 0xC0; + /** + * Constant representing uncategorized HID device subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; + /** + * Constant representing joystick subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; + /** + * Constant representing gamepad subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; + /** + * Constant representing remote control subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; + /** + * Constant representing sensing device subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; + /** + * Constant representing digitizer tablet subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; + /** + * Constant representing card reader subclass. + * + * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, + * BluetoothHidDeviceAppQosSettings, Executor, Callback) + */ public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** - * Constants representing report types. + * Constant representing HID Input Report type. * - * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int) - * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[]) + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) */ public static final byte REPORT_TYPE_INPUT = (byte) 1; + /** + * Constant representing HID Output Report type. + * + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) + */ public static final byte REPORT_TYPE_OUTPUT = (byte) 2; + /** + * Constant representing HID Feature Report type. + * + * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) + */ public static final byte REPORT_TYPE_FEATURE = (byte) 3; /** - * Constants representing error response for Set Report. + * Constant representing success response for Set Report. * - * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[]) + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) */ public static final byte ERROR_RSP_SUCCESS = (byte) 0; + /** + * Constant representing error response for Set Report due to "not ready". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_NOT_READY = (byte) 1; + /** + * Constant representing error response for Set Report due to "invalid report ID". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; + /** + * Constant representing error response for Set Report due to "unsupported request". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; + /** + * Constant representing error response for Set Report due to "invalid parameter". + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; + /** + * Constant representing error response for Set Report with unknown reason. + * + * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) + */ public static final byte ERROR_RSP_UNKNOWN = (byte) 14; /** - * Constants representing protocol mode used set by host. Default is always {@link + * Constant representing boot protocol mode used set by host. Default is always {@link * #PROTOCOL_REPORT_MODE} unless notified otherwise. * - * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte) + * @see Callback#onSetProtocol(BluetoothDevice, byte) */ public static final byte PROTOCOL_BOOT_MODE = (byte) 0; + /** + * Constant representing report protocol mode used set by host. Default is always {@link + * #PROTOCOL_REPORT_MODE} unless notified otherwise. + * + * @see Callback#onSetProtocol(BluetoothDevice, byte) + */ public static final byte PROTOCOL_REPORT_MODE = (byte) 1; - private Context mContext; + /** + * The template class that applications use to call callback functions on events from the HID + * host. Callback functions are wrapped in this class and registered to the Android system + * during app registration. + */ + public abstract static class Callback { + + private static final String TAG = "BluetoothHidDevCallback"; + + /** + * Callback called when application registration state changes. Usually it's called due to + * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], + * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also + * unsolicited in case e.g. Bluetooth was turned off in which case application is + * unregistered automatically. + * + * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently + * has Virtual Cable established with device. Only valid when application is registered, + * can be null. + * @param registered true if application is registered, false + * otherwise. + */ + public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + Log.d( + TAG, + "onAppStatusChanged: pluggedDevice=" + + pluggedDevice + + " registered=" + + registered); + } - private ServiceListener mServiceListener; + /** + * Callback called when connection state with remote host was changed. Application can + * assume than Virtual Cable is established when called with {@link + * BluetoothProfile#STATE_CONNECTED} state. + * + * @param device {@link BluetoothDevice} object representing host device which connection + * state was changed. + * @param state Connection state as defined in {@link BluetoothProfile}. + */ + public void onConnectionStateChanged(BluetoothDevice device, int state) { + Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); + } - private volatile IBluetoothHidDevice mService; + /** + * Callback called when GET_REPORT is received from remote host. Should be replied by + * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, + * byte[])}. + * + * @param type Requested Report Type. + * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. + * @param bufferSize Requested buffer size, application shall respond with at least given + * number of bytes. + */ + public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { + Log.d( + TAG, + "onGetReport: device=" + + device + + " type=" + + type + + " id=" + + id + + " bufferSize=" + + bufferSize); + } + + /** + * Callback called when SET_REPORT is received from remote host. In case received data are + * invalid, application shall respond with {@link + * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. + * + * @param type Report Type. + * @param id Report Id. + * @param data Report data. + */ + public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); + } + + /** + * Callback called when SET_PROTOCOL is received from remote host. Application shall use + * this information to send only reports valid for given protocol mode. By default, {@link + * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * + * @param protocol Protocol Mode. + */ + public void onSetProtocol(BluetoothDevice device, byte protocol) { + Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); + } + /** + * Callback called when report data is received over interrupt channel. Report Type is + * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * + * @param reportId Report Id. + * @param data Report data. + */ + public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); + } + + /** + * Callback called when Virtual Cable is removed. After this callback is received connection + * will be disconnected automatically. + */ + public void onVirtualCableUnplug(BluetoothDevice device) { + Log.d(TAG, "onVirtualCableUnplug: device=" + device); + } + } + + private Context mContext; + private ServiceListener mServiceListener; + private volatile IBluetoothHidDevice mService; private BluetoothAdapter mAdapter; - private static class BluetoothHidDeviceCallbackWrapper - extends IBluetoothHidDeviceCallback.Stub { + private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { - private BluetoothHidDeviceCallback mCallback; + private final Executor mExecutor; + private final Callback mCallback; - public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) { + CallbackWrapper(Executor executor, Callback callback) { + mExecutor = executor; mCallback = callback; } @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - mCallback.onAppStatusChanged(pluggedDevice, registered); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); } @Override public void onConnectionStateChanged(BluetoothDevice device, int state) { - mCallback.onConnectionStateChanged(device, state); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); } @Override public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - mCallback.onGetReport(device, type, id, bufferSize); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); } @Override public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - mCallback.onSetReport(device, type, id, data); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); } @Override public void onSetProtocol(BluetoothDevice device, byte protocol) { - mCallback.onSetProtocol(device, protocol); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); } @Override public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - mCallback.onInterruptData(device, reportId, data); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); } @Override public void onVirtualCableUnplug(BluetoothDevice device) { - mCallback.onVirtualCableUnplug(device); + clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); } } @@ -213,8 +433,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { }; BluetoothHidDevice(Context context, ServiceListener listener) { - Log.v(TAG, "BluetoothHidDevice"); - mContext = context; mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -245,7 +463,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } void doUnbind() { - Log.d(TAG, "Unbinding HidDevService"); if (mService != null) { mService = null; try { @@ -257,8 +474,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } void close() { - Log.v(TAG, "close()"); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -277,8 +492,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List getConnectedDevices() { - Log.v(TAG, "getConnectedDevices()"); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -290,14 +503,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); } - return new ArrayList(); + return new ArrayList<>(); } /** {@inheritDoc} */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -309,14 +520,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { Log.w(TAG, "Proxy not attached to service"); } - return new ArrayList(); + return new ArrayList<>(); } /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - Log.v(TAG, "getConnectionState(): device=" + device); - final IBluetoothHidDevice service = mService; if (service != null) { try { @@ -336,9 +545,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { * when application is registered. Only one application can be registered at one time. When an * application is registered, the HID Host service will be disabled until it is unregistered. * When no longer used, application should be unregistered using {@link #unregisterApp()}. The - * registration status should be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to - * the return value of this method. + * app will be automatically unregistered if it is not foreground. The registration status + * should be tracked by the application by handling callback from Callback#onAppStatusChanged. + * The app registration status is not related to the return value of this method. * * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID * Device SDP record is required. @@ -348,27 +557,36 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The * Outgoing QoS Settings is not required. Use null or default * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be - * sent. The BluetoothHidDeviceCallback object is required. + * @param executor {@link Executor} object on which callback will be executed. The Executor + * object is required. + * @param callback {@link Callback} object to which callback messages will be sent. The Callback + * object is required. * @return true if the command is successfully sent; otherwise false. */ - public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, - BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, - BluetoothHidDeviceCallback callback) { - Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos - + " callback=" + callback); - + public boolean registerApp( + BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, + BluetoothHidDeviceAppQosSettings outQos, + Executor executor, + Callback callback) { boolean result = false; - if (sdp == null || callback == null) { - return false; + if (sdp == null) { + throw new IllegalArgumentException("sdp parameter cannot be null"); + } + + if (executor == null) { + throw new IllegalArgumentException("executor parameter cannot be null"); + } + + if (callback == null) { + throw new IllegalArgumentException("callback parameter cannot be null"); } final IBluetoothHidDevice service = mService; if (service != null) { try { - BluetoothHidDeviceCallbackWrapper cbw = - new BluetoothHidDeviceCallbackWrapper(callback); + CallbackWrapper cbw = new CallbackWrapper(executor, callback); result = service.registerApp(sdp, inQos, outQos, cbw); } catch (RemoteException e) { Log.e(TAG, e.toString()); @@ -384,16 +602,13 @@ public final class BluetoothHidDevice implements BluetoothProfile { * Unregisters application. Active connection will be disconnected and no new connections will * be allowed until registered again using {@link #registerApp * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should - * be tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to - * the return value of this method. + * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be + * tracked by the application by handling callback from Callback#onAppStatusChanged. The app + * registration status is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean unregisterApp() { - Log.v(TAG, "unregisterApp()"); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -437,7 +652,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Sends report to remote host as reply for GET_REPORT request from {@link - * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}. + * Callback#onGetReport(BluetoothDevice, byte, byte, int)}. * * @param type Report Type, as in request. * @param id Report Id, as in request. @@ -445,8 +660,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @return true if the command is successfully sent; otherwise false. */ public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -465,14 +678,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Sends error handshake message as reply for invalid SET_REPORT request from {@link - * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}. + * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}. * * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. */ public boolean reportError(BluetoothDevice device, byte error) { - Log.v(TAG, "reportError(): device=" + device + " error=" + error); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -496,8 +707,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public boolean unplug(BluetoothDevice device) { - Log.v(TAG, "unplug(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -517,15 +726,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be - * tracked by the application by handling callback from - * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to - * the return value of this method. + * tracked by the application by handling callback from Callback#onConnectionStateChanged. The + * connection state is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean connect(BluetoothDevice device) { - Log.v(TAG, "connect(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; @@ -544,14 +750,12 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** * Disconnects from currently connected host. The connection state should be tracked by the - * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged. - * The connection state is not related to the return value of this method. + * application by handling callback from Callback#onConnectionStateChanged. The connection state + * is not related to the return value of this method. * * @return true if the command is successfully sent; otherwise false. */ public boolean disconnect(BluetoothDevice device) { - Log.v(TAG, "disconnect(): device=" + device); - boolean result = false; final IBluetoothHidDevice service = mService; diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index c05df2d23e4..a485b898c2d 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -29,12 +29,12 @@ import android.os.Parcelable; */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { - public final int serviceType; - public final int tokenRate; - public final int tokenBucketSize; - public final int peakBandwidth; - public final int latency; - public final int delayVariation; + private final int mServiceType; + private final int mTokenRate; + private final int mTokenBucketSize; + private final int mPeakBandwidth; + private final int mLatency; + private final int mDelayVariation; public static final int SERVICE_NO_TRAFFIC = 0x00; public static final int SERVICE_BEST_EFFORT = 0x01; @@ -44,38 +44,53 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. - * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. + * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and + * Appendix D for parameters. * - * @param serviceType L2CAP service type - * @param tokenRate L2CAP token rate - * @param tokenBucketSize L2CAP token bucket size - * @param peakBandwidth L2CAP peak bandwidth - * @param latency L2CAP latency - * @param delayVariation L2CAP delay variation + * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT + * @param tokenRate L2CAP token rate, default = 0 + * @param tokenBucketSize L2CAP token bucket size, default = 0 + * @param peakBandwidth L2CAP peak bandwidth, default = 0 + * @param latency L2CAP latency, default = MAX + * @param delayVariation L2CAP delay variation, default = MAX */ - public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, - int peakBandwidth, int latency, int delayVariation) { - this.serviceType = serviceType; - this.tokenRate = tokenRate; - this.tokenBucketSize = tokenBucketSize; - this.peakBandwidth = peakBandwidth; - this.latency = latency; - this.delayVariation = delayVariation; + public BluetoothHidDeviceAppQosSettings( + int serviceType, + int tokenRate, + int tokenBucketSize, + int peakBandwidth, + int latency, + int delayVariation) { + mServiceType = serviceType; + mTokenRate = tokenRate; + mTokenBucketSize = tokenBucketSize; + mPeakBandwidth = peakBandwidth; + mLatency = latency; + mDelayVariation = delayVariation; } - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppQosSettings) { - BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; - return this.serviceType == qos.serviceType - && this.tokenRate == qos.tokenRate - && this.tokenBucketSize == qos.tokenBucketSize - && this.peakBandwidth == qos.peakBandwidth - && this.latency == qos.latency - && this.delayVariation == qos.delayVariation; - } - return false; + public int getServiceType() { + return mServiceType; + } + + public int getTokenRate() { + return mTokenRate; + } + + public int getTokenBucketSize() { + return mTokenBucketSize; + } + + public int getPeakBandwidth() { + return mPeakBandwidth; + } + + public int getLatency() { + return mLatency; + } + + public int getDelayVariation() { + return mDelayVariation; } @Override @@ -106,104 +121,11 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeInt(serviceType); - out.writeInt(tokenRate); - out.writeInt(tokenBucketSize); - out.writeInt(peakBandwidth); - out.writeInt(latency); - out.writeInt(delayVariation); - } - - /** @return an int array representation of this instance */ - public int[] toArray() { - return new int[] { - serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation - }; - } - - /** A helper to build the BluetoothHidDeviceAppQosSettings object. */ - public static class Builder { - // Optional parameters - initialized to default values - private int mServiceType = SERVICE_BEST_EFFORT; - private int mTokenRate = 0; - private int mTokenBucketSize = 0; - private int mPeakBandwidth = 0; - private int mLatency = MAX; - private int mDelayVariation = MAX; - - /** - * Set the service type. - * - * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT, - * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one. - * @return BluetoothHidDeviceAppQosSettings Builder with specified service type. - */ - public Builder serviceType(int val) { - mServiceType = val; - return this; - } - /** - * Set the token rate. - * - * @param val token rate - * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate. - */ - public Builder tokenRate(int val) { - mTokenRate = val; - return this; - } - - /** - * Set the bucket size. - * - * @param val bucket size - * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size. - */ - public Builder tokenBucketSize(int val) { - mTokenBucketSize = val; - return this; - } - - /** - * Set the peak bandwidth. - * - * @param val peak bandwidth - * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth. - */ - public Builder peakBandwidth(int val) { - mPeakBandwidth = val; - return this; - } - /** - * Set the latency. - * - * @param val latency - * @return BluetoothHidDeviceAppQosSettings Builder with specified latency. - */ - public Builder latency(int val) { - mLatency = val; - return this; - } - - /** - * Set the delay variation. - * - * @param val delay variation - * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation. - */ - public Builder delayVariation(int val) { - mDelayVariation = val; - return this; - } - - /** - * Build the BluetoothHidDeviceAppQosSettings object. - * - * @return BluetoothHidDeviceAppQosSettings object with current settings. - */ - public BluetoothHidDeviceAppQosSettings build() { - return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize, - mPeakBandwidth, mLatency, mDelayVariation); - } + out.writeInt(mServiceType); + out.writeInt(mTokenRate); + out.writeInt(mTokenBucketSize); + out.writeInt(mPeakBandwidth); + out.writeInt(mLatency); + out.writeInt(mDelayVariation); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 562c559eddc..237082e4cb6 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; /** * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application. @@ -31,11 +30,11 @@ import java.util.Arrays; */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { - public final String name; - public final String description; - public final String provider; - public final byte subclass; - public final byte[] descriptors; + private final String mName; + private final String mDescription; + private final String mProvider; + private final byte mSubclass; + private final byte[] mDescriptors; /** * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. @@ -52,24 +51,31 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { */ public BluetoothHidDeviceAppSdpSettings( String name, String description, String provider, byte subclass, byte[] descriptors) { - this.name = name; - this.description = description; - this.provider = provider; - this.subclass = subclass; - this.descriptors = descriptors.clone(); + mName = name; + mDescription = description; + mProvider = provider; + mSubclass = subclass; + mDescriptors = descriptors.clone(); } - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHidDeviceAppSdpSettings) { - BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; - return this.name.equals(sdp.name) - && this.description.equals(sdp.description) - && this.provider.equals(sdp.provider) - && this.subclass == sdp.subclass - && Arrays.equals(this.descriptors, sdp.descriptors); - } - return false; + public String getName() { + return mName; + } + + public String getDescription() { + return mDescription; + } + + public String getProvider() { + return mProvider; + } + + public byte getSubclass() { + return mSubclass; + } + + public byte[] getDescriptors() { + return mDescriptors; } @Override @@ -99,10 +105,10 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(name); - out.writeString(description); - out.writeString(provider); - out.writeByte(subclass); - out.writeByteArray(descriptors); + out.writeString(mName); + out.writeString(mDescription); + out.writeString(mProvider); + out.writeByte(mSubclass); + out.writeByteArray(mDescriptors); } } diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java b/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java deleted file mode 100644 index e71b00f2349..00000000000 --- a/framework/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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.util.Log; - -/** - * The template class that applications use to call callback functions on events from the HID host. - * Callback functions are wrapped in this class and registered to the Android system during app - * registration. - * - *

        {@see BluetoothHidDevice} - */ -public abstract class BluetoothHidDeviceCallback { - - private static final String TAG = "BluetoothHidDevCallback"; - - /** - * Callback called when application registration state changes. Usually it's called due to - * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], - * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also - * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered - * automatically. - * - * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has - * Virtual Cable established with device. Only valid when application is registered, can be - * null. - * @param registered true if application is registered, false - * otherwise. - */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Log.d(TAG, - "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered); - } - - /** - * Callback called when connection state with remote host was changed. Application can assume - * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED} - * state. - * - * @param device {@link BluetoothDevice} object representing host device which connection state - * was changed. - * @param state Connection state as defined in {@link BluetoothProfile}. - */ - public void onConnectionStateChanged(BluetoothDevice device, int state) { - Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); - } - - /** - * Callback called when GET_REPORT is received from remote host. Should be replied by - * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, - * byte[])}. - * - * @param type Requested Report Type. - * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. - * @param bufferSize Requested buffer size, application shall respond with at least given number - * of bytes. - */ - public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize=" - + bufferSize); - } - - /** - * Callback called when SET_REPORT is received from remote host. In case received data are - * invalid, application shall respond with {@link - * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. - * - * @param type Report Type. - * @param id Report Id. - * @param data Report data. - */ - public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); - } - - /** - * Callback called when SET_PROTOCOL is received from remote host. Application shall use this - * information to send only reports valid for given protocol mode. By default, {@link - * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. - * - * @param protocol Protocol Mode. - */ - public void onSetProtocol(BluetoothDevice device, byte protocol) { - Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); - } - - /** - * Callback called when report data is received over interrupt channel. Report Type is assumed - * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. - * - * @param reportId Report Id. - * @param data Report data. - */ - public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); - } - - /** - * Callback called when Virtual Cable is removed. After this callback is - * received connection will be disconnected automatically. - */ - public void onVirtualCableUnplug(BluetoothDevice device) { - Log.d(TAG, "onVirtualCableUnplug: device=" + device); - } -} -- GitLab From af5e99336b3a3e8573b648311db0a00141a83ff9 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Fri, 23 Mar 2018 17:26:29 +0000 Subject: [PATCH 0920/1408] Revert "Bluetooth: preserve one advertisement slot for GMS core" This reverts commit a70a16b3e9d2433b7517e679f630d9dab33eea8a. Reason for revert: This breaks platforms that only support a single advertising slot. Bug: 76135219 Test: advertising does not work, revert change, advertising works Change-Id: Ib8c823eb9990cc06bad95c8c3ad0129afb245e00 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b9e80e40679..ee667c22077 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -680,10 +680,6 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return null; } - if (!isMultipleAdvertisementSupported()) { - Log.e(TAG, "Bluetooth LE advertising not supported"); - return null; - } synchronized (mLock) { if (sBluetoothLeAdvertiser == null) { sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); -- GitLab From e98595e5217cf5ed2576be51ba2c97ddc3078cc3 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 16 Mar 2018 09:15:48 -0700 Subject: [PATCH 0921/1408] Hearing Aid Profile: set and get activeDevice Add setActiveDevice() for Hearing Aid Profile in SettingsLib Bug: 69623109 Test: robolectric test and manual test Change-Id: I70eafe030747053876e2ab8a125d5dd01c5e0eb9 Merged-In: I70eafe030747053876e2ab8a125d5dd01c5e0eb9 (cherry picked from commit befadbc4a5450e3140293c37ecd5533581659687) --- .../bluetooth/BluetoothHearingAid.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 647e0d033fb..8f8083ed73e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -378,6 +379,76 @@ public final class BluetoothHearingAid implements BluetoothProfile { } } + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, Hearing Aid audio + * streaming is to the active Hearing Aid device. If a remote device + * is not connected, it cannot be selected as active. + * + *

        This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + mService.setActiveDevice(device); + return true; + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Check whether the device is active. + * + *

        Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isActiveDevice(@Nullable BluetoothDevice device) { + if (VDBG) log("isActiveDevice()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + return mService.isActiveDevice(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + /** * Set priority of the profile * -- GitLab From 451a6825f5b217e2504d39d9a3281b7b17f01b59 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 19 Mar 2018 13:06:45 -0700 Subject: [PATCH 0922/1408] Remove BLE App entry from mBleApps list when App died When a registered BLE App unexpectedly dies, its entry in mBleApps list needs to be cleanup/removed. Test: Manual test by repeatedly killing the gms core process. Bug: 74076974 Change-Id: I2dc86b782dd6b07017a360a0b709504f0a375969 (cherry picked from commit 73a1651446c400ecd1b07f2f7f448d68406517f7) --- .../server/bluetooth/BluetoothManagerService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 02b13807154..ae56cb92686 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -632,6 +632,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Binder is dead - unregister " + mPackageName); } + + for (Map.Entry entry : mBleApps.entrySet()) { + IBinder token = entry.getKey(); + ClientDeathRecipient deathRec = entry.getValue(); + if (deathRec.equals(this)) { + mBleApps.remove(token); + break; + } + } + if (isBleAppPresent()) { // Nothing to do, another app is here. return; -- GitLab From 4f16d04b2bf85488a5d8df94d222882d3911af7f Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 20 Mar 2018 16:54:27 -0700 Subject: [PATCH 0923/1408] Calls unlinkToDeath in binderDied to deregister When a registered BLE App unexpectedly dies and the binderDied callback is called, the unlinkToDeath is called to remove linkage. Also, refactor code to use an existing function. Test: Manual test by repeatedly killing the gms core process. Bug: 74076974 Change-Id: If47a534ecafe7fceae14f8cf8526987cabd279cd (cherry picked from commit 699f42e24658b74f4b869b10633e9f31a7a9d7a8) --- .../bluetooth/BluetoothManagerService.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ae56cb92686..216794139c4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -637,29 +637,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { IBinder token = entry.getKey(); ClientDeathRecipient deathRec = entry.getValue(); if (deathRec.equals(this)) { - mBleApps.remove(token); + updateBleAppCount(token, false, mPackageName); break; } } - - if (isBleAppPresent()) { - // Nothing to do, another app is here. - return; - } - if (DBG) { - Slog.d(TAG, "Disabling LE only mode after application crash"); - } - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) { - mEnable = false; - mBluetooth.onBrEdrDown(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } } public String getPackageName() { -- GitLab From d11e081d35a4533e29b3c3dd6b69801e8ca66542 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Fri, 16 Mar 2018 04:00:27 -0700 Subject: [PATCH 0924/1408] Add the AVRCP Target Service (2/2) Add the AVRCP Target constant to Bluetooth Profile. Also remove redundant public static final modifier on the constants. Bug: 68854188 Test: Compiles and local test with service enabled Change-Id: If2ec607fc704c225f8903d438fe970dfafac25f1 (cherry picked from commit 38c3073ba2532db7b5adf5f7f96cef847771c41b) Merged-In: If2ec607fc704c225f8903d438fe970dfafac25f1 --- .../android/bluetooth/BluetoothProfile.java | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 656188fbdfb..6aeb94da117 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -38,7 +38,7 @@ public interface BluetoothProfile { * This extra represents the current connection state of the profile of the * Bluetooth device. */ - public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; + String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; /** * Extra for the connection state intents of the individual profiles. @@ -46,123 +46,130 @@ public interface BluetoothProfile { * This extra represents the previous connection state of the profile of the * Bluetooth device. */ - public static final String EXTRA_PREVIOUS_STATE = + String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = 0; + int STATE_DISCONNECTED = 0; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = 1; + int STATE_CONNECTING = 1; /** The profile is in connected state */ - public static final int STATE_CONNECTED = 2; + int STATE_CONNECTED = 2; /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = 3; + int STATE_DISCONNECTING = 3; /** * Headset and Handsfree profile */ - public static final int HEADSET = 1; + int HEADSET = 1; /** * A2DP profile. */ - public static final int A2DP = 2; + int A2DP = 2; /** * Health Profile */ - public static final int HEALTH = 3; + int HEALTH = 3; /** * HID Host * * @hide */ - public static final int HID_HOST = 4; + int HID_HOST = 4; /** * PAN Profile * * @hide */ - public static final int PAN = 5; + int PAN = 5; /** * PBAP * * @hide */ - public static final int PBAP = 6; + int PBAP = 6; /** * GATT */ - public static final int GATT = 7; + int GATT = 7; /** * GATT_SERVER */ - public static final int GATT_SERVER = 8; + int GATT_SERVER = 8; /** * MAP Profile * * @hide */ - public static final int MAP = 9; + int MAP = 9; /* * SAP Profile * @hide */ - public static final int SAP = 10; + int SAP = 10; /** * A2DP Sink Profile * * @hide */ - public static final int A2DP_SINK = 11; + int A2DP_SINK = 11; /** * AVRCP Controller Profile * * @hide */ - public static final int AVRCP_CONTROLLER = 12; + int AVRCP_CONTROLLER = 12; + + /** + * AVRCP Target Profile + * + * @hide + */ + int AVRCP = 13; /** * Headset Client - HFP HF Role * * @hide */ - public static final int HEADSET_CLIENT = 16; + int HEADSET_CLIENT = 16; /** * PBAP Client * * @hide */ - public static final int PBAP_CLIENT = 17; + int PBAP_CLIENT = 17; /** * MAP Messaging Client Equipment (MCE) * * @hide */ - public static final int MAP_CLIENT = 18; + int MAP_CLIENT = 18; /** * HID Device */ - public static final int HID_DEVICE = 19; + int HID_DEVICE = 19; /** * Object Push Profile (OPP) * * @hide */ - public static final int OPP = 20; + int OPP = 20; /** * Hearing Aid Device @@ -185,7 +192,7 @@ public interface BluetoothProfile { * * @hide **/ - public static final int PRIORITY_AUTO_CONNECT = 1000; + int PRIORITY_AUTO_CONNECT = 1000; /** * Default priority for devices that allow incoming @@ -194,7 +201,7 @@ public interface BluetoothProfile { * @hide **/ @SystemApi - public static final int PRIORITY_ON = 100; + int PRIORITY_ON = 100; /** * Default priority for devices that does not allow incoming @@ -203,14 +210,14 @@ public interface BluetoothProfile { * @hide **/ @SystemApi - public static final int PRIORITY_OFF = 0; + int PRIORITY_OFF = 0; /** * Default priority when not set or when the device is unpaired * * @hide */ - public static final int PRIORITY_UNDEFINED = -1; + int PRIORITY_UNDEFINED = -1; /** * Get connected devices for this specific profile. -- GitLab From 5d7b3bc09d72e7b24dfffd2d9908dbb154fb0894 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 28 Mar 2018 12:56:05 -0700 Subject: [PATCH 0925/1408] Bluetooth HIDD: Remove unplug() Remove the hidden unplug() API because it is not used and it can cause the remote device to be unpaired. System apps should use unbond() and user apps should not use reflection to invoke it. Bug: 67866553 Test: SL4A Change-Id: I1bdc06dbb5460c9fd52230b78cbf9434a4349d24 --- .../android/bluetooth/BluetoothHidDevice.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index a3d6968e4c7..e017c42af98 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -700,29 +700,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } - /** - * Sends Virtual Cable Unplug to currently connected host. - * - * @return - * {@hide} - */ - public boolean unplug(BluetoothDevice device) { - boolean result = false; - - final IBluetoothHidDevice service = mService; - if (service != null) { - try { - result = service.unplug(device); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - } - - return result; - } - /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be -- GitLab From 109ab00fd0aabd4ee2e911ad644cfb20af955093 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 28 Mar 2018 12:56:05 -0700 Subject: [PATCH 0926/1408] Bluetooth HIDD: Remove unplug() Remove the hidden unplug() API because it is not used and it can cause the remote device to be unpaired. System apps should use unbond() and user apps should not use reflection to invoke it. Bug: 67866553 Test: SL4A Change-Id: I1bdc06dbb5460c9fd52230b78cbf9434a4349d24 (cherry picked from commit 5d7b3bc09d72e7b24dfffd2d9908dbb154fb0894) --- .../android/bluetooth/BluetoothHidDevice.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index cb1d1062681..af99bf7dfd8 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -700,29 +700,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } - /** - * Sends Virtual Cable Unplug to currently connected host. - * - * @return - * {@hide} - */ - public boolean unplug(BluetoothDevice device) { - boolean result = false; - - final IBluetoothHidDevice service = mService; - if (service != null) { - try { - result = service.unplug(device); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Log.w(TAG, "Proxy not attached to service"); - } - - return result; - } - /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be -- GitLab From ba0b0ec0203e44cd38d186327f0e0d3286608d19 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 28 Mar 2018 16:53:10 -0700 Subject: [PATCH 0927/1408] Hearing Aid: change get/set active device (3/3) * setActiveDevice() returns false in error case, e.g. when the device is not connected * add getActiveDevices() instead of isActiveDevice(), which returns a list that must have two elements: left and right, or empty list on error Test: manual Bug: 69623109 Change-Id: I48f3388c56434d0c21e09bd8b794e58848cd8794 --- .../bluetooth/BluetoothHearingAid.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 8f8083ed73e..159e165d594 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -421,29 +421,29 @@ public final class BluetoothHearingAid implements BluetoothProfile { } /** - * Check whether the device is active. + * Get the connected physical Hearing Aid devices that are active * *

        Requires {@link android.Manifest.permission#BLUETOOTH} * permission. * - * @return the connected device that is active or null if no device - * is active + * @return the list of active devices. The first element is the left active + * device; the second element is the right active device. If either or both side + * is not active, it will be null on that position. Returns empty list on error. * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public boolean isActiveDevice(@Nullable BluetoothDevice device) { - if (VDBG) log("isActiveDevice()"); + public List getActiveDevices() { + if (VDBG) log("getActiveDevices()"); try { mServiceLock.readLock().lock(); - if (mService != null && isEnabled() - && ((device == null) || isValidDevice(device))) { - return mService.isActiveDevice(device); + if (mService != null && isEnabled()) { + return mService.getActiveDevices(); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return new ArrayList<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + return new ArrayList<>(); } finally { mServiceLock.readLock().unlock(); } -- GitLab From b566e1751c37c106f18e1a385bfc40bfa8938092 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 19 Mar 2018 12:28:56 -0700 Subject: [PATCH 0928/1408] Add min_ce/max_ce parameters to requestLeConnectionUpdate() Add new test parameters, min_ce and max_ce, to the SL4A tests for LE Connection-oriented Channel (CoC) feature. This CL passes these 2 parameters to native stack. Test: Run cmd: act.py -c $MY_SL4A_CONFIG -tc BleCoc2ConnTest Bug: 77528723 Change-Id: I9d3d74f671772014209f8114c2d1b8ba606c54d5 --- .../java/android/bluetooth/BluetoothGatt.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 3df433643b0..71edc8a32c9 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1514,22 +1514,24 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the request is send to the Bluetooth stack. * @hide */ - public boolean requestLeConnectionUpdate(int minConnectionInterval, - int maxConnectionInterval, - int slaveLatency, int supervisionTimeout) { + public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, + int slaveLatency, int supervisionTimeout, + int minConnectionEventLen, int maxConnectionEventLen) { if (DBG) { Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval - + ")" + (1.25 * minConnectionInterval) - + "msec, max=(" + maxConnectionInterval + ")" + + ")" + (1.25 * minConnectionInterval) + + "msec, max=(" + maxConnectionInterval + ")" + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency - + ", timeout=" + supervisionTimeout + "msec"); + + ", timeout=" + supervisionTimeout + "msec" + ", min_ce=" + + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen); } if (mService == null || mClientIf == 0) return false; try { mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), - minConnectionInterval, maxConnectionInterval, - slaveLatency, supervisionTimeout); + minConnectionInterval, maxConnectionInterval, + slaveLatency, supervisionTimeout, + minConnectionEventLen, maxConnectionEventLen); } catch (RemoteException e) { Log.e(TAG, "", e); return false; -- GitLab From 41b86c3de4b1ad308cca1da1d1b7637921b2bb3d Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 3 Apr 2018 12:12:53 -0700 Subject: [PATCH 0929/1408] Bluetooth: Add hidden API to get current user of HID Device (1/3) Bug: 69136526 Test: test with apps using HID Device profile Change-Id: If0e49840257c877c975c2da176a08e613668cbc3 --- .../android/bluetooth/BluetoothHidDevice.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index e017c42af98..4a466c6686c 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -700,6 +700,28 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } + /** + * Gets the application name of the current HidDeviceService user. + * + * @return the current user name, or empty string if cannot get the name + * {@hide} + */ + public String getUserAppName() { + final IBluetoothHidDevice service = mService; + + if (service != null) { + try { + return service.getUserAppName(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return ""; + } + /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be -- GitLab From 7a892c292e47ded6257de80ca60fb6ae4e04075f Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 3 Apr 2018 12:12:53 -0700 Subject: [PATCH 0930/1408] Bluetooth: Add hidden API to get current user of HID Device (1/3) Bug: 69136526 Test: test with apps using HID Device profile Change-Id: If0e49840257c877c975c2da176a08e613668cbc3 (cherry picked from commit 347ef4099e0d2b1334efd22e6ca7d5cc5464b4eb) --- .../android/bluetooth/BluetoothHidDevice.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index af99bf7dfd8..3bc8544ebf8 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -700,6 +700,28 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } + /** + * Gets the application name of the current HidDeviceService user. + * + * @return the current user name, or empty string if cannot get the name + * {@hide} + */ + public String getUserAppName() { + final IBluetoothHidDevice service = mService; + + if (service != null) { + try { + return service.getUserAppName(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return ""; + } + /** * Initiates connection to host which is currently paired with this device. If the application * is not registered, #connect(BluetoothDevice) will fail. The connection state should be -- GitLab From 31cb77c6975d8999d5cd12cb85e94cb526e3a770 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 28 Mar 2018 16:53:10 -0700 Subject: [PATCH 0931/1408] Hearing Aid: change get/set active device (3/3) * setActiveDevice() returns false in error case, e.g. when the device is not connected * add getActiveDevices() instead of isActiveDevice(), which returns a list that must have two elements: left and right, or empty list on error Test: manual Bug: 69623109 Change-Id: I48f3388c56434d0c21e09bd8b794e58848cd8794 (cherry picked from commit ba0b0ec0203e44cd38d186327f0e0d3286608d19) --- .../bluetooth/BluetoothHearingAid.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 8f8083ed73e..159e165d594 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -421,29 +421,29 @@ public final class BluetoothHearingAid implements BluetoothProfile { } /** - * Check whether the device is active. + * Get the connected physical Hearing Aid devices that are active * *

        Requires {@link android.Manifest.permission#BLUETOOTH} * permission. * - * @return the connected device that is active or null if no device - * is active + * @return the list of active devices. The first element is the left active + * device; the second element is the right active device. If either or both side + * is not active, it will be null on that position. Returns empty list on error. * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public boolean isActiveDevice(@Nullable BluetoothDevice device) { - if (VDBG) log("isActiveDevice()"); + public List getActiveDevices() { + if (VDBG) log("getActiveDevices()"); try { mServiceLock.readLock().lock(); - if (mService != null && isEnabled() - && ((device == null) || isValidDevice(device))) { - return mService.isActiveDevice(device); + if (mService != null && isEnabled()) { + return mService.getActiveDevices(); } if (mService == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return new ArrayList<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + return new ArrayList<>(); } finally { mServiceLock.readLock().unlock(); } -- GitLab From 6505a0c522f2bed7a7185609c010e0d1d482eb34 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 19 Mar 2018 12:28:56 -0700 Subject: [PATCH 0932/1408] Add min_ce/max_ce parameters to requestLeConnectionUpdate() Add new test parameters, min_ce and max_ce, to the SL4A tests for LE Connection-oriented Channel (CoC) feature. This CL passes these 2 parameters to native stack. Test: Run cmd: act.py -c $MY_SL4A_CONFIG -tc BleCoc2ConnTest Bug: 77528723 Change-Id: I9d3d74f671772014209f8114c2d1b8ba606c54d5 (cherry picked from commit 148dd5bf861ad2caac0c53a53ee5154bfa413405) --- .../java/android/bluetooth/BluetoothGatt.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 3df433643b0..71edc8a32c9 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1514,22 +1514,24 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the request is send to the Bluetooth stack. * @hide */ - public boolean requestLeConnectionUpdate(int minConnectionInterval, - int maxConnectionInterval, - int slaveLatency, int supervisionTimeout) { + public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, + int slaveLatency, int supervisionTimeout, + int minConnectionEventLen, int maxConnectionEventLen) { if (DBG) { Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval - + ")" + (1.25 * minConnectionInterval) - + "msec, max=(" + maxConnectionInterval + ")" + + ")" + (1.25 * minConnectionInterval) + + "msec, max=(" + maxConnectionInterval + ")" + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency - + ", timeout=" + supervisionTimeout + "msec"); + + ", timeout=" + supervisionTimeout + "msec" + ", min_ce=" + + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen); } if (mService == null || mClientIf == 0) return false; try { mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), - minConnectionInterval, maxConnectionInterval, - slaveLatency, supervisionTimeout); + minConnectionInterval, maxConnectionInterval, + slaveLatency, supervisionTimeout, + minConnectionEventLen, maxConnectionEventLen); } catch (RemoteException e) { Log.e(TAG, "", e); return false; -- GitLab From 13c2ebbd1351358e8ccd4ce0dfa304ceea312b1d Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 13 Apr 2018 14:54:10 -0700 Subject: [PATCH 0933/1408] Clarify API doc for BluetoothGattServer::addService Clarify that the caller has to wait for onServiceAdded callback before calling BluetoothGattServer::addService again. Bug: 72717069 Test: Compile Change-Id: I20b031c724ba64bfd71cf10e58e587f69e4a2555 --- framework/java/android/bluetooth/BluetoothGattServer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 4ed250043ae..ef1b0bd7188 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -701,10 +701,14 @@ public final class BluetoothGattServer implements BluetoothProfile { *

        If the local device has already exposed services when this function * is called, a service update notification will be sent to all clients. * + *

        The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate + * whether this service has been added successfully. Do not add another service + * before this callback. + * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param service Service to be added to the list of services provided by this device. - * @return true, if the service has been added successfully + * @return true, if the request to add service has been initiated */ public boolean addService(BluetoothGattService service) { if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); -- GitLab From 11dc909599e9154f555db327000ec76db6910cd0 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Wed, 4 Apr 2018 18:33:46 -0700 Subject: [PATCH 0934/1408] Cleanup documentation for LE CoC in BluetoothAdapter Test: Compile Bug: 77631591 Change-Id: Ic3c7f13e560534a048bf5c8b274fe62190c214c7 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6aabe18ba8f..3d95669f4c9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -80,8 +80,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to * listen for incoming RFComm connection requests with {@link - * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented - * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for + * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

        *

        This class is thread safe.

        -- GitLab From 9b60fc70d9b79cfe22bba24758450b9af8f5aa9f Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 13 Apr 2018 14:54:10 -0700 Subject: [PATCH 0935/1408] Clarify API doc for BluetoothGattServer::addService Clarify that the caller has to wait for onServiceAdded callback before calling BluetoothGattServer::addService again. Bug: 72717069 Test: Compile Change-Id: I20b031c724ba64bfd71cf10e58e587f69e4a2555 (cherry picked from commit 4b5cf4856064efbc018c0f2b4d6d3ff199403ee0) --- framework/java/android/bluetooth/BluetoothGattServer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 4ed250043ae..ef1b0bd7188 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -701,10 +701,14 @@ public final class BluetoothGattServer implements BluetoothProfile { *

        If the local device has already exposed services when this function * is called, a service update notification will be sent to all clients. * + *

        The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate + * whether this service has been added successfully. Do not add another service + * before this callback. + * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param service Service to be added to the list of services provided by this device. - * @return true, if the service has been added successfully + * @return true, if the request to add service has been initiated */ public boolean addService(BluetoothGattService service) { if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); -- GitLab From 93e6d423f62546155b9316a399ab786f8d057dc5 Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Thu, 19 Apr 2018 14:16:15 -0700 Subject: [PATCH 0936/1408] Fix the implementation of BluetoothCodecStatus.equals() Previously, the BluetoothCodecStatus.equals() implementation was incorrect when comparing arrays of capabilities. In the new implementation, the arrays are compared correctly, and also the ordering of the capabilities in each array is ignored. Also, added unit tests for class BluetoothCodecConfig and class BluetoothCodecStatus. Bug: 73404858 Bug: 73379307 Test: Unit tests (in frameworks/base) runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java Change-Id: If22087465397b7c4175c33f7d1909a15d957fb24 --- .../bluetooth/BluetoothCodecStatus.java | 26 +- .../bluetooth/BluetoothCodecConfigTest.java | 327 ++++++++++++ .../bluetooth/BluetoothCodecStatusTest.java | 468 ++++++++++++++++++ 3 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7ae4cb70623..3a05e7093d4 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -57,13 +57,35 @@ public final class BluetoothCodecStatus implements Parcelable { if (o instanceof BluetoothCodecStatus) { BluetoothCodecStatus other = (BluetoothCodecStatus) o; return (Objects.equals(other.mCodecConfig, mCodecConfig) - && Objects.equals(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) - && Objects.equals(other.mCodecsSelectableCapabilities, + && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) + && sameCapabilities(other.mCodecsSelectableCapabilities, mCodecsSelectableCapabilities)); } return false; } + /** + * Checks whether two arrays of capabilities contain same capabilities. + * The order of the capabilities in each array is ignored. + * + * @param c1 the first array of capabilities to compare + * @param c2 the second array of capabilities to compare + * @return true if both arrays contain same capabilities + */ + private static boolean sameCapabilities(BluetoothCodecConfig[] c1, + BluetoothCodecConfig[] c2) { + if (c1 == null) { + return (c2 == null); + } + if (c2 == null) { + return false; + } + if (c1.length != c2.length) { + return false; + } + return Arrays.asList(c1).containsAll(Arrays.asList(c2)); + } + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java new file mode 100644 index 00000000000..59b46656dd5 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java @@ -0,0 +1,327 @@ +/* + * Copyright 2018 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.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothCodecConfig}. + *

        + * To run this test, use: + * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java + */ +public class BluetoothCodecConfigTest extends TestCase { + private static final int[] kCodecTypeArray = new int[] { + BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID, + }; + private static final int[] kCodecPriorityArray = new int[] { + BluetoothCodecConfig.CODEC_PRIORITY_DISABLED, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, + }; + private static final int[] kSampleRateArray = new int[] { + BluetoothCodecConfig.SAMPLE_RATE_NONE, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.SAMPLE_RATE_88200, + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.SAMPLE_RATE_176400, + BluetoothCodecConfig.SAMPLE_RATE_192000, + }; + private static final int[] kBitsPerSampleArray = new int[] { + BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + }; + private static final int[] kChannelModeArray = new int[] { + BluetoothCodecConfig.CHANNEL_MODE_NONE, + BluetoothCodecConfig.CHANNEL_MODE_MONO, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + }; + private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, }; + private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, }; + private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, }; + private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, }; + + private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length * + kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length * + kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length * + kCodecSpecific4Array.length; + + private int selectCodecType(int configId) { + int left = kCodecTypeArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecTypeArray.length; + return kCodecTypeArray[index]; + } + + private int selectCodecPriority(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecPriorityArray.length; + return kCodecPriorityArray[index]; + } + + private int selectSampleRate(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kSampleRateArray.length; + return kSampleRateArray[index]; + } + + private int selectBitsPerSample(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kBitsPerSampleArray.length; + return kBitsPerSampleArray[index]; + } + + private int selectChannelMode(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kChannelModeArray.length; + return kChannelModeArray[index]; + } + + private long selectCodecSpecific1(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific1Array.length; + return kCodecSpecific1Array[index]; + } + + private long selectCodecSpecific2(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific2Array.length; + return kCodecSpecific2Array[index]; + } + + private long selectCodecSpecific3(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length * kCodecSpecific3Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific3Array.length; + return kCodecSpecific3Array[index]; + } + + private long selectCodecSpecific4(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length * kCodecSpecific3Array.length * + kCodecSpecific4Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific4Array.length; + return kCodecSpecific4Array[index]; + } + + @SmallTest + public void testBluetoothCodecConfig_valid_get_methods() { + + for (int config_id = 0; config_id < kTotalConfigs; config_id++) { + int codec_type = selectCodecType(config_id); + int codec_priority = selectCodecPriority(config_id); + int sample_rate = selectSampleRate(config_id); + int bits_per_sample = selectBitsPerSample(config_id); + int channel_mode = selectChannelMode(config_id); + long codec_specific1 = selectCodecSpecific1(config_id); + long codec_specific2 = selectCodecSpecific2(config_id); + long codec_specific3 = selectCodecSpecific3(config_id); + long codec_specific4 = selectCodecSpecific4(config_id); + + BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority, + sample_rate, bits_per_sample, + channel_mode, codec_specific1, + codec_specific2, codec_specific3, + codec_specific4); + if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + assertFalse(bcc.isValid()); + } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + assertFalse(bcc.isValid()); + } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + assertFalse(bcc.isValid()); + } else { + assertTrue(bcc.isValid()); + } + + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { + assertTrue(bcc.isMandatoryCodec()); + } else { + assertFalse(bcc.isMandatoryCodec()); + } + + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { + assertEquals("SBC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) { + assertEquals("AAC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) { + assertEquals("aptX", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) { + assertEquals("aptX HD", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) { + assertEquals("LDAC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) { + assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")", + bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) { + assertEquals("INVALID CODEC", bcc.getCodecName()); + } + + assertEquals(codec_type, bcc.getCodecType()); + assertEquals(codec_priority, bcc.getCodecPriority()); + assertEquals(sample_rate, bcc.getSampleRate()); + assertEquals(bits_per_sample, bcc.getBitsPerSample()); + assertEquals(channel_mode, bcc.getChannelMode()); + assertEquals(codec_specific1, bcc.getCodecSpecific1()); + assertEquals(codec_specific2, bcc.getCodecSpecific2()); + assertEquals(codec_specific3, bcc.getCodecSpecific3()); + assertEquals(codec_specific4, bcc.getCodecSpecific4()); + } + } + + @SmallTest + public void testBluetoothCodecConfig_equals() { + BluetoothCodecConfig bcc1 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + BluetoothCodecConfig bcc2_same = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertTrue(bcc1.equals(bcc2_same)); + + BluetoothCodecConfig bcc3_codec_type = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc3_codec_type)); + + BluetoothCodecConfig bcc4_codec_priority = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc4_codec_priority)); + + BluetoothCodecConfig bcc5_sample_rate = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc5_sample_rate)); + + BluetoothCodecConfig bcc6_bits_per_sample = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc6_bits_per_sample)); + + BluetoothCodecConfig bcc7_channel_mode = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc7_channel_mode)); + + BluetoothCodecConfig bcc8_codec_specific1 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1001, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc8_codec_specific1)); + + BluetoothCodecConfig bcc9_codec_specific2 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2002, 3000, 4000); + assertFalse(bcc1.equals(bcc9_codec_specific2)); + + BluetoothCodecConfig bcc10_codec_specific3 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3003, 4000); + assertFalse(bcc1.equals(bcc10_codec_specific3)); + + BluetoothCodecConfig bcc11_codec_specific4 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4004); + assertFalse(bcc1.equals(bcc11_codec_specific4)); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java new file mode 100644 index 00000000000..83bf2ed1938 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java @@ -0,0 +1,468 @@ +/* + * Copyright 2018 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.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.util.Arrays; +import java.util.Objects; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothCodecStatus}. + *

        + * To run this test, use: + * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java + */ +public class BluetoothCodecStatusTest extends TestCase { + + // Codec configs: A and B are same; C is different + private static final BluetoothCodecConfig config_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig config_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig config_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + // Local capabilities: A and B are same; C is different + private static final BluetoothCodecConfig local_capability1_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability1_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability1_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + + private static final BluetoothCodecConfig local_capability2_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability2_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability2_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + + // Selectable capabilities: A and B are same; C is different + private static final BluetoothCodecConfig selectable_capability1_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability1_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability1_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig[] local_capability_A = { + local_capability1_A, + local_capability2_A, + local_capability3_A, + local_capability4_A, + local_capability5_A, + }; + + private static final BluetoothCodecConfig[] local_capability_B = { + local_capability1_B, + local_capability2_B, + local_capability3_B, + local_capability4_B, + local_capability5_B, + }; + + private static final BluetoothCodecConfig[] local_capability_B_reordered = { + local_capability5_B, + local_capability4_B, + local_capability2_B, + local_capability3_B, + local_capability1_B, + }; + + private static final BluetoothCodecConfig[] local_capability_C = { + local_capability1_C, + local_capability2_C, + local_capability3_C, + local_capability4_C, + local_capability5_C, + }; + + private static final BluetoothCodecConfig[] selectable_capability_A = { + selectable_capability1_A, + selectable_capability2_A, + selectable_capability3_A, + selectable_capability4_A, + selectable_capability5_A, + }; + + private static final BluetoothCodecConfig[] selectable_capability_B = { + selectable_capability1_B, + selectable_capability2_B, + selectable_capability3_B, + selectable_capability4_B, + selectable_capability5_B, + }; + + private static final BluetoothCodecConfig[] selectable_capability_B_reordered = { + selectable_capability5_B, + selectable_capability4_B, + selectable_capability2_B, + selectable_capability3_B, + selectable_capability1_B, + }; + + private static final BluetoothCodecConfig[] selectable_capability_C = { + selectable_capability1_C, + selectable_capability2_C, + selectable_capability3_C, + selectable_capability4_C, + selectable_capability5_C, + }; + + private static final BluetoothCodecStatus bcs_A = + new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A); + private static final BluetoothCodecStatus bcs_B = + new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B); + private static final BluetoothCodecStatus bcs_B_reordered = + new BluetoothCodecStatus(config_B, local_capability_B_reordered, + selectable_capability_B_reordered); + private static final BluetoothCodecStatus bcs_C = + new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C); + + @SmallTest + public void testBluetoothCodecStatus_get_methods() { + + assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A)); + assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B)); + assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C)); + + assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A)); + assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B)); + assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C)); + + assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_A)); + assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_B)); + assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_C)); + } + + @SmallTest + public void testBluetoothCodecStatus_equals() { + assertTrue(bcs_A.equals(bcs_B)); + assertTrue(bcs_B.equals(bcs_A)); + assertTrue(bcs_A.equals(bcs_B_reordered)); + assertTrue(bcs_B_reordered.equals(bcs_A)); + assertFalse(bcs_A.equals(bcs_C)); + assertFalse(bcs_C.equals(bcs_A)); + } +} -- GitLab From af53c8f68036f1067f9f9fb251a0b5194c2bd840 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Wed, 4 Apr 2018 18:33:46 -0700 Subject: [PATCH 0937/1408] Cleanup documentation for LE CoC in BluetoothAdapter Test: Compile Bug: 77631591 Change-Id: Ic3c7f13e560534a048bf5c8b274fe62190c214c7 (cherry picked from commit dac8e140184f3ac52210ff3638af3f53edca1ba0) --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ee667c22077..1b6b5a01ec7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -80,8 +80,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to * listen for incoming RFComm connection requests with {@link - * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented - * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for + * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

        *

        This class is thread safe.

        -- GitLab From 09fa6f29eed32ec4117625881521dda0fb60f28a Mon Sep 17 00:00:00 2001 From: Pavlin Radoslavov Date: Thu, 19 Apr 2018 14:16:15 -0700 Subject: [PATCH 0938/1408] Fix the implementation of BluetoothCodecStatus.equals() Previously, the BluetoothCodecStatus.equals() implementation was incorrect when comparing arrays of capabilities. In the new implementation, the arrays are compared correctly, and also the ordering of the capabilities in each array is ignored. Also, added unit tests for class BluetoothCodecConfig and class BluetoothCodecStatus. Bug: 73404858 Bug: 73379307 Test: Unit tests (in frameworks/base) runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java Change-Id: If22087465397b7c4175c33f7d1909a15d957fb24 Merged-In: If22087465397b7c4175c33f7d1909a15d957fb24 (cherry picked from commit 9d36e6babc38cf7017726a587b656bdd6f8b0051) --- .../bluetooth/BluetoothCodecStatus.java | 26 +- .../bluetooth/BluetoothCodecConfigTest.java | 327 ++++++++++++ .../bluetooth/BluetoothCodecStatusTest.java | 468 ++++++++++++++++++ 3 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7ae4cb70623..3a05e7093d4 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -57,13 +57,35 @@ public final class BluetoothCodecStatus implements Parcelable { if (o instanceof BluetoothCodecStatus) { BluetoothCodecStatus other = (BluetoothCodecStatus) o; return (Objects.equals(other.mCodecConfig, mCodecConfig) - && Objects.equals(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) - && Objects.equals(other.mCodecsSelectableCapabilities, + && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) + && sameCapabilities(other.mCodecsSelectableCapabilities, mCodecsSelectableCapabilities)); } return false; } + /** + * Checks whether two arrays of capabilities contain same capabilities. + * The order of the capabilities in each array is ignored. + * + * @param c1 the first array of capabilities to compare + * @param c2 the second array of capabilities to compare + * @return true if both arrays contain same capabilities + */ + private static boolean sameCapabilities(BluetoothCodecConfig[] c1, + BluetoothCodecConfig[] c2) { + if (c1 == null) { + return (c2 == null); + } + if (c2 == null) { + return false; + } + if (c1.length != c2.length) { + return false; + } + return Arrays.asList(c1).containsAll(Arrays.asList(c2)); + } + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java new file mode 100644 index 00000000000..59b46656dd5 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java @@ -0,0 +1,327 @@ +/* + * Copyright 2018 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.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothCodecConfig}. + *

        + * To run this test, use: + * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java + */ +public class BluetoothCodecConfigTest extends TestCase { + private static final int[] kCodecTypeArray = new int[] { + BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX, + BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID, + }; + private static final int[] kCodecPriorityArray = new int[] { + BluetoothCodecConfig.CODEC_PRIORITY_DISABLED, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, + }; + private static final int[] kSampleRateArray = new int[] { + BluetoothCodecConfig.SAMPLE_RATE_NONE, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.SAMPLE_RATE_88200, + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.SAMPLE_RATE_176400, + BluetoothCodecConfig.SAMPLE_RATE_192000, + }; + private static final int[] kBitsPerSampleArray = new int[] { + BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + }; + private static final int[] kChannelModeArray = new int[] { + BluetoothCodecConfig.CHANNEL_MODE_NONE, + BluetoothCodecConfig.CHANNEL_MODE_MONO, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + }; + private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, }; + private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, }; + private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, }; + private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, }; + + private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length * + kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length * + kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length * + kCodecSpecific4Array.length; + + private int selectCodecType(int configId) { + int left = kCodecTypeArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecTypeArray.length; + return kCodecTypeArray[index]; + } + + private int selectCodecPriority(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecPriorityArray.length; + return kCodecPriorityArray[index]; + } + + private int selectSampleRate(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kSampleRateArray.length; + return kSampleRateArray[index]; + } + + private int selectBitsPerSample(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kBitsPerSampleArray.length; + return kBitsPerSampleArray[index]; + } + + private int selectChannelMode(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kChannelModeArray.length; + return kChannelModeArray[index]; + } + + private long selectCodecSpecific1(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific1Array.length; + return kCodecSpecific1Array[index]; + } + + private long selectCodecSpecific2(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific2Array.length; + return kCodecSpecific2Array[index]; + } + + private long selectCodecSpecific3(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length * kCodecSpecific3Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific3Array.length; + return kCodecSpecific3Array[index]; + } + + private long selectCodecSpecific4(int configId) { + int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * + kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * + kCodecSpecific2Array.length * kCodecSpecific3Array.length * + kCodecSpecific4Array.length; + int right = kTotalConfigs / left; + int index = configId / right; + index = index % kCodecSpecific4Array.length; + return kCodecSpecific4Array[index]; + } + + @SmallTest + public void testBluetoothCodecConfig_valid_get_methods() { + + for (int config_id = 0; config_id < kTotalConfigs; config_id++) { + int codec_type = selectCodecType(config_id); + int codec_priority = selectCodecPriority(config_id); + int sample_rate = selectSampleRate(config_id); + int bits_per_sample = selectBitsPerSample(config_id); + int channel_mode = selectChannelMode(config_id); + long codec_specific1 = selectCodecSpecific1(config_id); + long codec_specific2 = selectCodecSpecific2(config_id); + long codec_specific3 = selectCodecSpecific3(config_id); + long codec_specific4 = selectCodecSpecific4(config_id); + + BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority, + sample_rate, bits_per_sample, + channel_mode, codec_specific1, + codec_specific2, codec_specific3, + codec_specific4); + if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + assertFalse(bcc.isValid()); + } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + assertFalse(bcc.isValid()); + } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + assertFalse(bcc.isValid()); + } else { + assertTrue(bcc.isValid()); + } + + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { + assertTrue(bcc.isMandatoryCodec()); + } else { + assertFalse(bcc.isMandatoryCodec()); + } + + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { + assertEquals("SBC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) { + assertEquals("AAC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) { + assertEquals("aptX", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) { + assertEquals("aptX HD", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) { + assertEquals("LDAC", bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) { + assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")", + bcc.getCodecName()); + } + if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) { + assertEquals("INVALID CODEC", bcc.getCodecName()); + } + + assertEquals(codec_type, bcc.getCodecType()); + assertEquals(codec_priority, bcc.getCodecPriority()); + assertEquals(sample_rate, bcc.getSampleRate()); + assertEquals(bits_per_sample, bcc.getBitsPerSample()); + assertEquals(channel_mode, bcc.getChannelMode()); + assertEquals(codec_specific1, bcc.getCodecSpecific1()); + assertEquals(codec_specific2, bcc.getCodecSpecific2()); + assertEquals(codec_specific3, bcc.getCodecSpecific3()); + assertEquals(codec_specific4, bcc.getCodecSpecific4()); + } + } + + @SmallTest + public void testBluetoothCodecConfig_equals() { + BluetoothCodecConfig bcc1 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + BluetoothCodecConfig bcc2_same = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertTrue(bcc1.equals(bcc2_same)); + + BluetoothCodecConfig bcc3_codec_type = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc3_codec_type)); + + BluetoothCodecConfig bcc4_codec_priority = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc4_codec_priority)); + + BluetoothCodecConfig bcc5_sample_rate = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc5_sample_rate)); + + BluetoothCodecConfig bcc6_bits_per_sample = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc6_bits_per_sample)); + + BluetoothCodecConfig bcc7_channel_mode = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc7_channel_mode)); + + BluetoothCodecConfig bcc8_codec_specific1 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1001, 2000, 3000, 4000); + assertFalse(bcc1.equals(bcc8_codec_specific1)); + + BluetoothCodecConfig bcc9_codec_specific2 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2002, 3000, 4000); + assertFalse(bcc1.equals(bcc9_codec_specific2)); + + BluetoothCodecConfig bcc10_codec_specific3 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3003, 4000); + assertFalse(bcc1.equals(bcc10_codec_specific3)); + + BluetoothCodecConfig bcc11_codec_specific4 = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4004); + assertFalse(bcc1.equals(bcc11_codec_specific4)); + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java new file mode 100644 index 00000000000..83bf2ed1938 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java @@ -0,0 +1,468 @@ +/* + * Copyright 2018 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.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.util.Arrays; +import java.util.Objects; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothCodecStatus}. + *

        + * To run this test, use: + * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java + */ +public class BluetoothCodecStatusTest extends TestCase { + + // Codec configs: A and B are same; C is different + private static final BluetoothCodecConfig config_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig config_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig config_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + // Local capabilities: A and B are same; C is different + private static final BluetoothCodecConfig local_capability1_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability1_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability1_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + + private static final BluetoothCodecConfig local_capability2_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability2_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability2_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability3_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability4_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig local_capability5_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + + // Selectable capabilities: A and B are same; C is different + private static final BluetoothCodecConfig selectable_capability1_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability1_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability1_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability2_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability3_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_16, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability4_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100, + BluetoothCodecConfig.BITS_PER_SAMPLE_24, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_A = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_B = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO | + BluetoothCodecConfig.CHANNEL_MODE_MONO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig selectable_capability5_C = + new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_44100 | + BluetoothCodecConfig.SAMPLE_RATE_48000 | + BluetoothCodecConfig.SAMPLE_RATE_88200 | + BluetoothCodecConfig.SAMPLE_RATE_96000, + BluetoothCodecConfig.BITS_PER_SAMPLE_16 | + BluetoothCodecConfig.BITS_PER_SAMPLE_24 | + BluetoothCodecConfig.BITS_PER_SAMPLE_32, + BluetoothCodecConfig.CHANNEL_MODE_STEREO, + 1000, 2000, 3000, 4000); + + private static final BluetoothCodecConfig[] local_capability_A = { + local_capability1_A, + local_capability2_A, + local_capability3_A, + local_capability4_A, + local_capability5_A, + }; + + private static final BluetoothCodecConfig[] local_capability_B = { + local_capability1_B, + local_capability2_B, + local_capability3_B, + local_capability4_B, + local_capability5_B, + }; + + private static final BluetoothCodecConfig[] local_capability_B_reordered = { + local_capability5_B, + local_capability4_B, + local_capability2_B, + local_capability3_B, + local_capability1_B, + }; + + private static final BluetoothCodecConfig[] local_capability_C = { + local_capability1_C, + local_capability2_C, + local_capability3_C, + local_capability4_C, + local_capability5_C, + }; + + private static final BluetoothCodecConfig[] selectable_capability_A = { + selectable_capability1_A, + selectable_capability2_A, + selectable_capability3_A, + selectable_capability4_A, + selectable_capability5_A, + }; + + private static final BluetoothCodecConfig[] selectable_capability_B = { + selectable_capability1_B, + selectable_capability2_B, + selectable_capability3_B, + selectable_capability4_B, + selectable_capability5_B, + }; + + private static final BluetoothCodecConfig[] selectable_capability_B_reordered = { + selectable_capability5_B, + selectable_capability4_B, + selectable_capability2_B, + selectable_capability3_B, + selectable_capability1_B, + }; + + private static final BluetoothCodecConfig[] selectable_capability_C = { + selectable_capability1_C, + selectable_capability2_C, + selectable_capability3_C, + selectable_capability4_C, + selectable_capability5_C, + }; + + private static final BluetoothCodecStatus bcs_A = + new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A); + private static final BluetoothCodecStatus bcs_B = + new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B); + private static final BluetoothCodecStatus bcs_B_reordered = + new BluetoothCodecStatus(config_B, local_capability_B_reordered, + selectable_capability_B_reordered); + private static final BluetoothCodecStatus bcs_C = + new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C); + + @SmallTest + public void testBluetoothCodecStatus_get_methods() { + + assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A)); + assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B)); + assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C)); + + assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A)); + assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B)); + assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C)); + + assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_A)); + assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_B)); + assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), + selectable_capability_C)); + } + + @SmallTest + public void testBluetoothCodecStatus_equals() { + assertTrue(bcs_A.equals(bcs_B)); + assertTrue(bcs_B.equals(bcs_A)); + assertTrue(bcs_A.equals(bcs_B_reordered)); + assertTrue(bcs_B_reordered.equals(bcs_A)); + assertFalse(bcs_A.equals(bcs_C)); + assertFalse(bcs_C.equals(bcs_A)); + } +} -- GitLab From 6f59ed59311f85b6cd05797f4f1535f691015ca0 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Apr 2018 11:17:17 -0700 Subject: [PATCH 0939/1408] BluetoothManagerService: Add dumpsys logs for BLE_ON -> OFF Add dumpsys logs for state transition from BLE_ON to OFF. This can be either from location settings off or airplane mode enabled. Test: manual Bug: 74090881 Change-Id: I1f6cfe691b359591af60c3be2e015bec5d3859e8 --- .../android/server/bluetooth/BluetoothManagerService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 6494a81d9dd..be3b5de602e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -285,6 +285,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName(), false); mBluetooth.onBrEdrDown(); mEnable = false; mEnableExternal = false; @@ -677,6 +680,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + mContext.getPackageName(), false); mBluetooth.onBrEdrDown(); } } catch (RemoteException e) { -- GitLab From 450cadc996bbf7d9fee6eba48b24cdd7e68b1f0b Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Apr 2018 11:17:17 -0700 Subject: [PATCH 0940/1408] BluetoothManagerService: Add dumpsys logs for BLE_ON -> OFF Add dumpsys logs for state transition from BLE_ON to OFF. This can be either from location settings off or airplane mode enabled. Test: manual Bug: 74090881 Change-Id: I1f6cfe691b359591af60c3be2e015bec5d3859e8 (cherry picked from commit 917f4b332e2c21f074e5f50bf598ff36d77f2945) --- .../android/server/bluetooth/BluetoothManagerService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8d10cfaaf9c..2c0e97eae53 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -285,6 +285,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName(), false); mBluetooth.onBrEdrDown(); mEnable = false; mEnableExternal = false; @@ -677,6 +680,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + mContext.getPackageName(), false); mBluetooth.onBrEdrDown(); } } catch (RemoteException e) { -- GitLab From fd9c48deb56172e1d4fe2194f192b8ed02c6083b Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:13:45 -0700 Subject: [PATCH 0941/1408] DO NOT MERGE Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I3c25af233742e63351a68e8c5a279b51a94e49e2 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f7e25045155..3f2b93c6547 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -723,7 +723,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return sService.getRemoteName(this); + String name = sService.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } -- GitLab From 32aa64764cb4a47d597cca91537a5fccb571ae3c Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:13:45 -0700 Subject: [PATCH 0942/1408] DO NOT MERGE Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I3c25af233742e63351a68e8c5a279b51a94e49e2 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6d6dfebced2..a061e19a40a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -727,7 +727,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return sService.getRemoteName(this); + String name = sService.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } -- GitLab From 5fed7d1d6a8b79e7fd9be69bd6a40322f2486ce9 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:22:39 -0700 Subject: [PATCH 0943/1408] Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I7f2201cab36adf7f01d1a794d783cb78a536811f --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ac21395c2fa..7a6b72e980f 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -848,7 +848,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteName(this); + String name = service.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From dfc7ab6fbd229ef45e1a15a2b2cad583740d4112 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:13:45 -0700 Subject: [PATCH 0944/1408] DO NOT MERGE Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I3c25af233742e63351a68e8c5a279b51a94e49e2 Merged-In: I7f2201cab36adf7f01d1a794d783cb78a536811f --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7ff37d29323..d84cdf4ea8b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -780,7 +780,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return sService.getRemoteName(this); + String name = sService.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } -- GitLab From f0bdbddfe1111519be33a31e9063ca78164ff176 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:22:39 -0700 Subject: [PATCH 0945/1408] DO NOT MERGE Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I7f2201cab36adf7f01d1a794d783cb78a536811f --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 98cd319a399..9498f125c2a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -808,7 +808,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteName(this); + String name = service.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 4143ed3ac8c059ea32f6f33b6ecadfd133428beb Mon Sep 17 00:00:00 2001 From: Pulkit Bhuwalka Date: Wed, 28 Mar 2018 13:51:43 -0700 Subject: [PATCH 0946/1408] Get/Set IO capability of Bluetooth device Creates the hidden BluetoothAdapter APIs which can be used to control IO capability settings on the local Bluetooth device. Bug: 36015413 Test: Used a test activity to control getting/setting the value and attempting pairing. Change-Id: Ibbfdc5ae5a1d56c6e3d003ab3bf5d095dcb583e4 (cherry picked from commit 6043b7b90a27a836241bfc05e71eb46ce04c66cf) --- .../android/bluetooth/BluetoothAdapter.java | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3d95669f4c9..df75b0d0b7d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -389,6 +389,58 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; + /** + * Device only has a display. + * + * @hide + */ + public static final int IO_CAPABILITY_OUT = 0; + + /** + * Device has a display and the ability to input Yes/No. + * + * @hide + */ + public static final int IO_CAPABILITY_IO = 1; + + /** + * Device only has a keyboard for entry but no display. + * + * @hide + */ + public static final int IO_CAPABILITY_IN = 2; + + /** + * Device has no Input or Output capability. + * + * @hide + */ + public static final int IO_CAPABILITY_NONE = 3; + + /** + * Device has a display and a full keyboard. + * + * @hide + */ + public static final int IO_CAPABILITY_KBDISP = 4; + + /** + * Maximum range value for Input/Output capabilities. + * + *

        This should be updated when adding a new Input/Output capability. Other code + * like validation depends on this being accurate. + * + * @hide + */ + public static final int IO_CAPABILITY_MAX = 5; + + /** + * The Input/Output capability of the device is unknown. + * + * @hide + */ + public static final int IO_CAPABILITY_UNKNOWN = 255; + /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -1224,6 +1276,106 @@ public final class BluetoothAdapter { return false; } + /** + * Returns the Input/Output capability of the device for classic Bluetooth. + * + * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, + * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE}, + * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public int getIoCapability() { + if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getIoCapability(); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; + } + + /** + * Sets the Input/Output capability of the device for classic Bluetooth. + * + *

        Changing the Input/Output capability of a device only takes effect on restarting the + * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()} + * and {@link BluetoothAdapter#enable()} to see the changes. + * + * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, + * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, + * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setIoCapability(int capability) { + if (getState() != STATE_ON) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.setIoCapability(capability); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Returns the Input/Output capability of the device for BLE operations. + * + * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, + * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE}, + * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public int getLeIoCapability() { + if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getLeIoCapability(); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; + } + + /** + * Sets the Input/Output capability of the device for BLE operations. + * + *

        Changing the Input/Output capability of a device only takes effect on restarting the + * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()} + * and {@link BluetoothAdapter#enable()} to see the changes. + * + * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, + * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, + * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setLeIoCapability(int capability) { + if (getState() != STATE_ON) return false; + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.setLeIoCapability(capability); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + /** * Get the current Bluetooth scan mode of the local Bluetooth adapter. *

        The Bluetooth scan mode determines if the local adapter is -- GitLab From 201c4a40b17d1375c8f8dc8a1a4976346e17e698 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 3 May 2018 08:50:49 -0700 Subject: [PATCH 0947/1408] Fix spelling errors in BluetoothGatt documentation Correct "paramter" to "parameter". Correct "connectoin" to connection. Bug: 79198797 Test: Compile Change-Id: I98646eec66da2aaa6f74ae2db35ea914c6a105a1 --- .../java/android/bluetooth/BluetoothGatt.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 71edc8a32c9..457119dc5bf 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -98,22 +98,22 @@ public final class BluetoothGatt implements BluetoothProfile { public static final int GATT_FAILURE = 0x101; /** - * Connection paramter update - Use the connection paramters recommended by the + * Connection parameter update - Use the connection parameters recommended by the * Bluetooth SIG. This is the default value if no connection parameter update * is requested. */ public static final int CONNECTION_PRIORITY_BALANCED = 0; /** - * Connection paramter update - Request a high priority, low latency connection. - * An application should only request high priority connection paramters to transfer - * large amounts of data over LE quickly. Once the transfer is complete, the application - * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters - * to reduce energy use. + * Connection parameter update - Request a high priority, low latency connection. + * An application should only request high priority connection parameters to transfer large + * amounts of data over LE quickly. Once the transfer is complete, the application should + * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce + * energy use. */ public static final int CONNECTION_PRIORITY_HIGH = 1; - /** Connection paramter update - Request low power, reduced data rate connection parameters. */ + /** Connection parameter update - Request low power, reduced data rate connection parameters. */ public static final int CONNECTION_PRIORITY_LOW_POWER = 2; /** -- GitLab From 7cffaa9a37e485c6d204060cc647470a70f055d7 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 3 May 2018 08:50:49 -0700 Subject: [PATCH 0948/1408] Fix spelling errors in BluetoothGatt documentation Correct "paramter" to "parameter". Correct "connectoin" to connection. Bug: 79198797 Test: Compile Change-Id: I98646eec66da2aaa6f74ae2db35ea914c6a105a1 (cherry picked from commit 201c4a40b17d1375c8f8dc8a1a4976346e17e698) --- .../java/android/bluetooth/BluetoothGatt.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 71edc8a32c9..457119dc5bf 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -98,22 +98,22 @@ public final class BluetoothGatt implements BluetoothProfile { public static final int GATT_FAILURE = 0x101; /** - * Connection paramter update - Use the connection paramters recommended by the + * Connection parameter update - Use the connection parameters recommended by the * Bluetooth SIG. This is the default value if no connection parameter update * is requested. */ public static final int CONNECTION_PRIORITY_BALANCED = 0; /** - * Connection paramter update - Request a high priority, low latency connection. - * An application should only request high priority connection paramters to transfer - * large amounts of data over LE quickly. Once the transfer is complete, the application - * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters - * to reduce energy use. + * Connection parameter update - Request a high priority, low latency connection. + * An application should only request high priority connection parameters to transfer large + * amounts of data over LE quickly. Once the transfer is complete, the application should + * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce + * energy use. */ public static final int CONNECTION_PRIORITY_HIGH = 1; - /** Connection paramter update - Request low power, reduced data rate connection parameters. */ + /** Connection parameter update - Request low power, reduced data rate connection parameters. */ public static final int CONNECTION_PRIORITY_LOW_POWER = 2; /** -- GitLab From 09ca4a4c07d360218ab9ca40e6c2ae7fbf485aec Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 2 May 2018 19:10:56 -0700 Subject: [PATCH 0949/1408] Bluetooth: Fix HFP SCO logic and documentation AudioService: * Call setBtScoActiveDevice and setBluetoothScoOn both in AudioService's broadcast receiver so that these two methods must be triggerred in the same sequence as ACTIVE_DEVICE_CHANGED and AUDIO_STATE_CHANGED intents are sent and we no longer need to handle race condition by synchronously checking active device in setBluetoothScoOn * Default sco audio mode when no headset is active should be virtual voice call, as many HFP devices do not accept SCO audio without an ongoing call * Synchronize checkScoAudioState() method with mScoClients * Add helper functions connectBluetoothScoHelper and disconnectBluetoothScoHelper to call various SCO setup and tear down methods based on sco audio mode * Try raw, virtual call, and voice recognition mode when disconnecting externally started SCO * Add new sco state SCO_STATE_DEACTIVATING to allow back to back calling of startBluetoothSco and stopBluetoothSco Audio Manager: * Modified AudioManager logic so that removed devices callback is called before newly added devices BluetoothHeadset: * Modified BluetoothHeadset so that start and stop SCO using virtual voice call no longer need a parameter and will use active device by default * Modified documentation around various sco mangement APIs to match their expected behaviors Bug: 76114959 Test: VoIP calls sanity test cases Change-Id: Id50db88f4ff36069b0f392c81dd9d90c24cd2206 --- .../android/bluetooth/BluetoothHeadset.java | 97 +++++++++++++------ 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index a68f485f437..0c91a2054b8 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.Manifest; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -633,8 +634,9 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected of if the connected headset doesn't support - * voice recognition or on error, true otherwise + * @return false if there is no headset connected, or the connected headset doesn't support + * voice recognition, or voice recognition is already started, or audio channel is occupied, + * or on error, true otherwise */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); @@ -654,10 +656,15 @@ public final class BluetoothHeadset implements BluetoothProfile { * Stop Bluetooth Voice Recognition mode, and shut down the * Bluetooth audio path. * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected or on error, true otherwise + * @return false if there is no headset connected, or voice recognition has not started, + * or voice recognition has ended on this headset, or on error, true otherwise */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); @@ -798,11 +805,12 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Check if Bluetooth SCO audio is connected. + * Check if at least one headset's SCO audio is connected or connecting * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @return true if SCO is connected, false otherwise or on error + * @return true if at least one device's SCO audio is connected or connecting, false otherwise + * or on error * @hide */ public boolean isAudioOn() { @@ -821,11 +829,21 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a connection of headset audio. - * It setup SCO channel with remote connected headset device. + * Initiates a connection of headset audio to the current active device + * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. * - * @return true if successful false if there was some error such as there is no connected - * headset + *

        {@link #EXTRA_STATE} will transition from + * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when + * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} + * in case of failure to establish the audio connection. + * + * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true + * before calling this method + * + * @return false if there was some error such as there is no active headset * @hide */ public boolean connectAudio() { @@ -844,11 +862,14 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a disconnection of headset audio. - * It tears down the SCO channel from remote headset device. + * Initiates a disconnection of HFP SCO audio. + * Tear down voice recognition or virtual voice call if any. + * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. * - * @return true if successful false if there was some error such as there is no connected SCO - * channel + * @return false if audio is not connected, or on error, true otherwise * @hide */ public boolean disconnectAudio() { @@ -867,22 +888,33 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a SCO channel connection with the headset (if connected). - * Also initiates a virtual voice call for Handsfree devices as many devices - * do not accept SCO audio without a call. - * This API allows the handsfree device to be used for routing non-cellular - * call audio. + * Initiates a SCO channel connection as a virtual voice call to the current active device + * Active handsfree device will be notified of incoming call and connected call. * - * @param device Remote Bluetooth Device - * @return true if successful, false if there was some error. + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. + * + *

        {@link #EXTRA_STATE} will transition from + * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when + * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} + * in case of failure to establish the audio connection. + * + * @return true if successful, false if one of the following case applies + * - SCO audio is not idle (connecting or connected) + * - virtual call has already started + * - there is no active device + * - a Telecom managed call is going on + * - binder is dead or Bluetooth is disabled or other error * @hide */ - public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + if (service != null && isEnabled()) { try { - return service.startScoUsingVirtualVoiceCall(device); + return service.startScoUsingVirtualVoiceCall(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -894,19 +926,24 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Terminates an ongoing SCO connection and the associated virtual - * call. + * Terminates an ongoing SCO connection and the associated virtual call. * - * @param device Remote Bluetooth Device - * @return true if successful, false if there was some error. + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * + * @return true if successful, false if one of the following case applies + * - virtual voice call is not started or has ended + * - binder is dead or Bluetooth is disabled or other error * @hide */ - public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + if (service != null && isEnabled()) { try { - return service.stopScoUsingVirtualVoiceCall(device); + return service.stopScoUsingVirtualVoiceCall(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } -- GitLab From c46a01e89a0aaf40196e773e2112b06894d278f5 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 2 May 2018 19:10:56 -0700 Subject: [PATCH 0950/1408] Bluetooth: Fix HFP SCO logic and documentation AudioService: * Call setBtScoActiveDevice and setBluetoothScoOn both in AudioService's broadcast receiver so that these two methods must be triggerred in the same sequence as ACTIVE_DEVICE_CHANGED and AUDIO_STATE_CHANGED intents are sent and we no longer need to handle race condition by synchronously checking active device in setBluetoothScoOn * Default sco audio mode when no headset is active should be virtual voice call, as many HFP devices do not accept SCO audio without an ongoing call * Synchronize checkScoAudioState() method with mScoClients * Add helper functions connectBluetoothScoHelper and disconnectBluetoothScoHelper to call various SCO setup and tear down methods based on sco audio mode * Try raw, virtual call, and voice recognition mode when disconnecting externally started SCO * Add new sco state SCO_STATE_DEACTIVATING to allow back to back calling of startBluetoothSco and stopBluetoothSco Audio Manager: * Modified AudioManager logic so that removed devices callback is called before newly added devices BluetoothHeadset: * Modified BluetoothHeadset so that start and stop SCO using virtual voice call no longer need a parameter and will use active device by default * Modified documentation around various sco mangement APIs to match their expected behaviors Bug: 76114959 Test: VoIP calls sanity test cases Change-Id: Id50db88f4ff36069b0f392c81dd9d90c24cd2206 (cherry picked from commit 09ca4a4c07d360218ab9ca40e6c2ae7fbf485aec) --- .../android/bluetooth/BluetoothHeadset.java | 97 +++++++++++++------ 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index a68f485f437..0c91a2054b8 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.Manifest; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -633,8 +634,9 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected of if the connected headset doesn't support - * voice recognition or on error, true otherwise + * @return false if there is no headset connected, or the connected headset doesn't support + * voice recognition, or voice recognition is already started, or audio channel is occupied, + * or on error, true otherwise */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); @@ -654,10 +656,15 @@ public final class BluetoothHeadset implements BluetoothProfile { * Stop Bluetooth Voice Recognition mode, and shut down the * Bluetooth audio path. * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param device Bluetooth headset - * @return false if there is no headset connected or on error, true otherwise + * @return false if there is no headset connected, or voice recognition has not started, + * or voice recognition has ended on this headset, or on error, true otherwise */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); @@ -798,11 +805,12 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Check if Bluetooth SCO audio is connected. + * Check if at least one headset's SCO audio is connected or connecting * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @return true if SCO is connected, false otherwise or on error + * @return true if at least one device's SCO audio is connected or connecting, false otherwise + * or on error * @hide */ public boolean isAudioOn() { @@ -821,11 +829,21 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a connection of headset audio. - * It setup SCO channel with remote connected headset device. + * Initiates a connection of headset audio to the current active device + * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. * - * @return true if successful false if there was some error such as there is no connected - * headset + *

        {@link #EXTRA_STATE} will transition from + * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when + * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} + * in case of failure to establish the audio connection. + * + * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true + * before calling this method + * + * @return false if there was some error such as there is no active headset * @hide */ public boolean connectAudio() { @@ -844,11 +862,14 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a disconnection of headset audio. - * It tears down the SCO channel from remote headset device. + * Initiates a disconnection of HFP SCO audio. + * Tear down voice recognition or virtual voice call if any. + * + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. * - * @return true if successful false if there was some error such as there is no connected SCO - * channel + * @return false if audio is not connected, or on error, true otherwise * @hide */ public boolean disconnectAudio() { @@ -867,22 +888,33 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Initiates a SCO channel connection with the headset (if connected). - * Also initiates a virtual voice call for Handsfree devices as many devices - * do not accept SCO audio without a call. - * This API allows the handsfree device to be used for routing non-cellular - * call audio. + * Initiates a SCO channel connection as a virtual voice call to the current active device + * Active handsfree device will be notified of incoming call and connected call. * - * @param device Remote Bluetooth Device - * @return true if successful, false if there was some error. + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. + * + *

        {@link #EXTRA_STATE} will transition from + * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when + * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} + * in case of failure to establish the audio connection. + * + * @return true if successful, false if one of the following case applies + * - SCO audio is not idle (connecting or connected) + * - virtual call has already started + * - there is no active device + * - a Telecom managed call is going on + * - binder is dead or Bluetooth is disabled or other error * @hide */ - public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + if (service != null && isEnabled()) { try { - return service.startScoUsingVirtualVoiceCall(device); + return service.startScoUsingVirtualVoiceCall(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -894,19 +926,24 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * Terminates an ongoing SCO connection and the associated virtual - * call. + * Terminates an ongoing SCO connection and the associated virtual call. * - * @param device Remote Bluetooth Device - * @return true if successful, false if there was some error. + *

        Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. + * If this function returns true, this intent will be broadcasted with + * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * + * @return true if successful, false if one of the following case applies + * - virtual voice call is not started or has ended + * - binder is dead or Bluetooth is disabled or other error * @hide */ - public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + if (service != null && isEnabled()) { try { - return service.stopScoUsingVirtualVoiceCall(device); + return service.stopScoUsingVirtualVoiceCall(); } catch (RemoteException e) { Log.e(TAG, e.toString()); } -- GitLab From c89a9862ad53312de78f1b695feb6351bf56868f Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 11 May 2018 08:47:24 -0700 Subject: [PATCH 0951/1408] BluetoothManager: Continue when BLE not supported Bluetooth should turn on completely (without GATT) when the chip does not support Bluetooth Low Energy. Bug: 77495521 Test: Remove vendor/etc/permissions/android.hardware.bluetooth_le.xml Reboot Toggle Bluetooth Change-Id: I9314ee473b4a5a158196bab3037b1452247ba466 (cherry picked from commit 5123ebd75647d3e67487de69cd0381f325a0adc9) --- .../bluetooth/BluetoothManagerService.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8d10cfaaf9c..2a573b7f9d1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -757,26 +757,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Action taken when GattService is turned on + * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on. */ - private void onBluetoothGattServiceUp() { + private void continueFromBleOnState() { if (DBG) { - Slog.d(TAG, "BluetoothGatt Service is Up"); + Slog.d(TAG, "continueFromBleOnState()"); } try { mBluetoothLock.readLock().lock(); if (mBluetooth == null) { - if (DBG) { - Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); - } - return; - } - int st = mBluetooth.getState(); - if (st != BluetoothAdapter.STATE_BLE_ON) { - if (DBG) { - Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " - + BluetoothAdapter.nameForState(st)); - } + Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { @@ -1632,7 +1622,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service)); - onBluetoothGattServiceUp(); + continueFromBleOnState(); break; } // else must be SERVICE_IBLUETOOTH @@ -2049,21 +2039,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Bluetooth is in LE only mode"); } - if (mBluetoothGatt != null) { - if (DBG) { - Slog.d(TAG, "Calling BluetoothGattServiceUp"); - } - onBluetoothGattServiceUp(); + if (mBluetoothGatt != null || !mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + continueFromBleOnState(); } else { if (DBG) { Slog.d(TAG, "Binding Bluetooth GATT service"); } - if (mContext.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT); - } + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT); } sendBleStateChanged(prevState, newState); //Don't broadcase this as std intent -- GitLab From 9031d83d882d3c0b88187d05a2d45d9b249baaeb Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 11 May 2018 08:47:24 -0700 Subject: [PATCH 0952/1408] BluetoothManager: Continue when BLE not supported Bluetooth should turn on completely (without GATT) when the chip does not support Bluetooth Low Energy. Bug: 77495521 Test: Remove vendor/etc/permissions/android.hardware.bluetooth_le.xml Reboot Toggle Bluetooth Change-Id: I9314ee473b4a5a158196bab3037b1452247ba466 --- .../bluetooth/BluetoothManagerService.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index be3b5de602e..378357e2b15 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -762,26 +762,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Action taken when GattService is turned on + * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on. */ - private void onBluetoothGattServiceUp() { + private void continueFromBleOnState() { if (DBG) { - Slog.d(TAG, "BluetoothGatt Service is Up"); + Slog.d(TAG, "continueFromBleOnState()"); } try { mBluetoothLock.readLock().lock(); if (mBluetooth == null) { - if (DBG) { - Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!"); - } - return; - } - int st = mBluetooth.getState(); - if (st != BluetoothAdapter.STATE_BLE_ON) { - if (DBG) { - Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " - + BluetoothAdapter.nameForState(st)); - } + Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { @@ -1613,7 +1603,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { mBluetoothGatt = IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service)); - onBluetoothGattServiceUp(); + continueFromBleOnState(); break; } // else must be SERVICE_IBLUETOOTH @@ -2030,21 +2020,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Bluetooth is in LE only mode"); } - if (mBluetoothGatt != null) { - if (DBG) { - Slog.d(TAG, "Calling BluetoothGattServiceUp"); - } - onBluetoothGattServiceUp(); + if (mBluetoothGatt != null || !mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + continueFromBleOnState(); } else { if (DBG) { Slog.d(TAG, "Binding Bluetooth GATT service"); } - if (mContext.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT); - } + Intent i = new Intent(IBluetoothGatt.class.getName()); + doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + UserHandle.CURRENT); } sendBleStateChanged(prevState, newState); //Don't broadcase this as std intent -- GitLab From dc16007bfecc4cdd240015ce9835b8848a0d1df4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 7 Dec 2017 22:56:03 -0800 Subject: [PATCH 0953/1408] Fix bad type for txPower in PeriodicAdvertisingReport serialization Bug: 69634768 Test: compilation Change-Id: Icedfbaf1ba933637e935ada0fd98aea42c73f2b2 Merged-In: Icedfbaf1ba933637e935ada0fd98aea42c73f2b2 --- .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 51b93cbd64d..6fc8d553946 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -71,7 +71,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(syncHandle); - dest.writeLong(txPower); + dest.writeInt(txPower); dest.writeInt(rssi); dest.writeInt(dataStatus); if (data != null) { -- GitLab From bcb396fab5a3d6d316c2a6633a27de9cb77c548b Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 4 Jun 2018 10:32:06 -0700 Subject: [PATCH 0954/1408] ScanRecord.getServiceData NPE fix Bug: 109662587 Change-Id: I0e9653d9cd7e4b748870bacb6fbcecae535ebb2a --- framework/java/android/bluetooth/le/ScanRecord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index f8aaba910b7..04dd060cae5 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -117,7 +117,7 @@ public final class ScanRecord { */ @Nullable public byte[] getServiceData(ParcelUuid serviceDataUuid) { - if (serviceDataUuid == null) { + if (serviceDataUuid == null || mServiceData == null) { return null; } return mServiceData.get(serviceDataUuid); -- GitLab From dea97b7e14979a37cf247b9204a7681b41a1f819 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 19 Jun 2018 08:48:10 -0700 Subject: [PATCH 0955/1408] Add Feature Flag for Hearing Aid Profile Using the Settings App-Developer Options-Feature Flag, allow the user to enable or disable the Hearing Aid Profile. Test: Manual testing using Settings App Change-Id: I58a9d339941e235242c443c85b6f4194b5a296c9 --- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aa426d3cd31..78b738500a9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -53,12 +53,16 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; import android.util.Slog; import android.util.StatsLog; @@ -386,6 +390,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); + // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils + boolean isHearingAidEnabled; + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); + if (!TextUtils.isEmpty(value)) { + isHearingAidEnabled = Boolean.parseBoolean(value); + Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); + FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); + } + IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); -- GitLab From 15e33418240bb57100f17d345719183756323478 Mon Sep 17 00:00:00 2001 From: baisheng Date: Wed, 11 Jul 2018 14:40:50 +0800 Subject: [PATCH 0956/1408] Remove not-required assertion in BluetoothTestUtils.disable/enable Those assertions will cause LE builds failed as BluetoothAdapter behaves a bit different on LE. And this removal should not have impacts on exiting tests because Semaphore will guarantee BT is disabled/enabled within timeout. Bug: 111329335 Test: Do Bluetooth Enable Stress test on LE and RoW builds. Change-Id: Ieedec90d21f4fd1aa3cdb4a6cb5849a9fa4fd864 --- .../src/android/bluetooth/BluetoothTestUtils.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index ada03666b7b..b906d84adf5 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -446,7 +446,10 @@ public class BluetoothTestUtils extends Assert { final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(receiver, filter); - assertTrue(adapter.enable()); + // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to + // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable. + // So no assertion applied here. + adapter.enable(); boolean success = false; try { success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); @@ -489,7 +492,10 @@ public class BluetoothTestUtils extends Assert { final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(receiver, filter); - assertTrue(adapter.disable()); + // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to + // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable. + // So no assertion applied here. + adapter.disable(); boolean success = false; try { success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); -- GitLab From 46f6c56ff30e00a62d144a84df8dd592c53e6b37 Mon Sep 17 00:00:00 2001 From: Pulkit Bhuwalka Date: Fri, 20 Apr 2018 16:56:53 -0700 Subject: [PATCH 0957/1408] Add IntDef to BluetoothAdapter IO_CAP constants Bug: None Test: Builds Change-Id: I04f8f6cdcc1a552ae2f2a358308b9a5c862989d3 --- .../java/android/bluetooth/BluetoothAdapter.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index df75b0d0b7d..bd65639605d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -441,6 +441,12 @@ public final class BluetoothAdapter { */ public static final int IO_CAPABILITY_UNKNOWN = 255; + /** @hide */ + @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE, + IO_CAPABILITY_KBDISP}) + @Retention(RetentionPolicy.SOURCE) + public @interface IoCapability {} + /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -1286,6 +1292,7 @@ public final class BluetoothAdapter { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @IoCapability public int getIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { @@ -1313,7 +1320,7 @@ public final class BluetoothAdapter { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setIoCapability(int capability) { + public boolean setIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); @@ -1336,6 +1343,7 @@ public final class BluetoothAdapter { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @IoCapability public int getLeIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { @@ -1363,7 +1371,7 @@ public final class BluetoothAdapter { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setLeIoCapability(int capability) { + public boolean setLeIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); -- GitLab From c2342ac2d02119ac502e9cf427958409bc2b61a6 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Wed, 18 Jul 2018 15:41:24 -0700 Subject: [PATCH 0958/1408] Always review permissions before mProfileServices = new HashMap<>(); - private final boolean mPermissionReviewRequired; + private final boolean mWirelessConsentRequired; private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override @@ -368,8 +368,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext = context; - mPermissionReviewRequired = context.getResources() - .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired); + mWirelessConsentRequired = context.getResources() + .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired); mCrashes = 0; mBluetooth = null; @@ -885,7 +885,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName, + if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { return false; } @@ -922,7 +922,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName, + if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { return false; } @@ -945,7 +945,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean startConsentUiIfNeeded(String packageName, int callingUid, String intentAction) throws RemoteException { - if (checkBluetoothPermissionWhenPermissionReviewRequired()) { + if (checkBluetoothPermissionWhenWirelessConsentRequired()) { return false; } try { @@ -978,21 +978,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** * Check if the caller must still pass permission check or if the caller is exempted - * from the consent UI via the MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED check. + * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check. * * Commands from some callers may be exempted from triggering the consent UI when * enabling bluetooth. This exemption is checked via the - * MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED and allows calls to skip + * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip * the consent UI where it may otherwise be required. * * @hide */ - private boolean checkBluetoothPermissionWhenPermissionReviewRequired() { - if (!mPermissionReviewRequired) { - return false; - } + private boolean checkBluetoothPermissionWhenWirelessConsentRequired() { int result = mContext.checkCallingPermission( - android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED); + android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED); return result == PackageManager.PERMISSION_GRANTED; } -- GitLab From 2b61bf65937b7879d6944f26cc7fba342d7d109c Mon Sep 17 00:00:00 2001 From: Claire Treyz Date: Thu, 26 Jul 2018 10:17:53 -0700 Subject: [PATCH 0959/1408] Fix unregisterScanner call in BluetoothLeScanner. LE scanners were originally part of the GATT client, but were seperated out and have their own registration API, so the BluetoothLeScanner call to unregisterClient should be updated to call unregisterScanner. Bug:75984723 Change-Id: I4c0543beb6d7b706cf75e78fcfb58480a6efd1d8 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 347fc4df4eb..f5e2a09ee3e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -441,8 +441,8 @@ public final class BluetoothLeScanner { if (status == BluetoothGatt.GATT_SUCCESS) { try { if (mScannerId == -1) { - // Registration succeeds after timeout, unregister client. - mBluetoothGatt.unregisterClient(scannerId); + // Registration succeeds after timeout, unregister scanner. + mBluetoothGatt.unregisterScanner(scannerId); } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, -- GitLab From 7d543894e0497651fc160728d659543483500f87 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 1 Aug 2018 15:07:20 +0100 Subject: [PATCH 0960/1408] 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 --- .../java/android/bluetooth/BluetoothA2dp.java | 23 ++++++++++++ .../android/bluetooth/BluetoothA2dpSink.java | 2 ++ .../android/bluetooth/BluetoothAdapter.java | 15 ++++++++ .../android/bluetooth/BluetoothClass.java | 5 +++ .../bluetooth/BluetoothCodecConfig.java | 36 +++++++++++++++++++ .../bluetooth/BluetoothCodecStatus.java | 5 +++ .../android/bluetooth/BluetoothDevice.java | 34 ++++++++++++++++++ .../java/android/bluetooth/BluetoothGatt.java | 11 ++++++ .../BluetoothGattCharacteristic.java | 5 +++ .../bluetooth/BluetoothGattDescriptor.java | 4 +++ .../bluetooth/BluetoothGattService.java | 4 +++ .../android/bluetooth/BluetoothHeadset.java | 13 +++++++ .../bluetooth/BluetoothHeadsetClient.java | 6 ++++ .../bluetooth/BluetoothHeadsetClientCall.java | 6 ++++ .../bluetooth/BluetoothHearingAid.java | 4 +++ .../java/android/bluetooth/BluetoothMap.java | 2 ++ .../android/bluetooth/BluetoothMapClient.java | 2 ++ .../java/android/bluetooth/BluetoothPan.java | 11 ++++++ .../java/android/bluetooth/BluetoothPbap.java | 2 ++ .../android/bluetooth/BluetoothProfile.java | 6 ++++ .../java/android/bluetooth/BluetoothSap.java | 2 ++ .../bluetooth/BluetoothServerSocket.java | 2 ++ .../android/bluetooth/BluetoothSocket.java | 6 ++++ .../java/android/bluetooth/BluetoothUuid.java | 17 +++++++++ .../java/android/bluetooth/le/ScanRecord.java | 2 ++ 25 files changed, 225 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 419eda3a85e..798e00d789c 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -117,6 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -137,6 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -160,6 +163,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; /** @@ -167,6 +171,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; /** @@ -174,6 +179,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_SUPPORTED = 1; /** @@ -182,6 +188,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; /** @@ -189,6 +196,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; /** @@ -196,6 +204,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; private Context mContext; @@ -268,6 +277,7 @@ public final class BluetoothA2dp implements BluetoothProfile { return true; } + @UnsupportedAppUsage /*package*/ void close() { mServiceListener = null; IBluetoothManager mgr = mAdapter.getBluetoothManager(); @@ -315,6 +325,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { @@ -357,6 +368,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { @@ -460,6 +472,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -490,6 +503,7 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @Nullable + @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { @@ -556,6 +570,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { @@ -671,6 +686,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ + @UnsupportedAppUsage public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { @@ -698,6 +714,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ + @UnsupportedAppUsage public void setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); @@ -723,6 +740,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ + @UnsupportedAppUsage public void enableOptionalCodecs(BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, true); @@ -735,6 +753,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ + @UnsupportedAppUsage public void disableOptionalCodecs(BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, false); @@ -775,6 +794,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ + @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -799,6 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ + @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -824,6 +845,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ + @UnsupportedAppUsage public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN @@ -854,6 +876,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static String stateToString(int state) { switch (state) { case STATE_DISCONNECTED: diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index faab000a899..e92e55a4974 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -278,6 +279,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothA2dpSink service = mService; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index df75b0d0b7d..2bf444c23b3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; @@ -629,6 +630,7 @@ public final class BluetoothAdapter { private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; + @UnsupportedAppUsage private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -988,6 +990,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState + @UnsupportedAppUsage public int getLeState() { int state = BluetoothAdapter.STATE_OFF; @@ -1098,6 +1101,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ + @UnsupportedAppUsage public boolean disable(boolean persist) { try { @@ -1149,6 +1153,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ + @UnsupportedAppUsage public boolean factoryReset() { try { mServiceLock.readLock().lock(); @@ -1172,6 +1177,7 @@ public final class BluetoothAdapter { * @return the UUIDs supported by the local Bluetooth Adapter. * @hide */ + @UnsupportedAppUsage public ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; @@ -1438,6 +1444,7 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ + @UnsupportedAppUsage public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) { return false; @@ -1456,6 +1463,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public boolean setScanMode(int mode) { if (getState() != STATE_ON) { return false; @@ -1465,6 +1473,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public int getDiscoverableTimeout() { if (getState() != STATE_ON) { return -1; @@ -1483,6 +1492,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) { return; @@ -2007,6 +2017,7 @@ public final class BluetoothAdapter { * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} * @hide */ + @UnsupportedAppUsage public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2094,6 +2105,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @UnsupportedAppUsage public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2206,6 +2218,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @UnsupportedAppUsage public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); @@ -2749,6 +2762,7 @@ public final class BluetoothAdapter { return true; } + @UnsupportedAppUsage /*package*/ IBluetoothManager getBluetoothManager() { return mManagerService; } @@ -2756,6 +2770,7 @@ public final class BluetoothAdapter { private final ArrayList mProxyServiceStateCallbacks = new ArrayList(); + @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { synchronized (mProxyServiceStateCallbacks) { if (cb == null) { diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index f22ea6e88e0..8557f389d91 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -63,6 +64,7 @@ public final class BluetoothClass implements Parcelable { private final int mClass; /** @hide */ + @UnsupportedAppUsage public BluetoothClass(int classInt) { mClass = classInt; } @@ -322,8 +324,10 @@ public final class BluetoothClass implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public static final int PROFILE_HEADSET = 0; /** @hide */ + @UnsupportedAppUsage public static final int PROFILE_A2DP = 1; /** @hide */ public static final int PROFILE_OPP = 2; @@ -346,6 +350,7 @@ public final class BluetoothClass implements Parcelable { * @return True if this device might support specified profile. * @hide */ + @UnsupportedAppUsage public boolean doesClassMatch(int profile) { if (profile == PROFILE_A2DP) { if (hasService(Service.RENDER)) { diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index e3a6e064725..79c0a3a207c 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -32,34 +33,58 @@ public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final int mCodecType; @@ -72,6 +97,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(int codecType, int codecPriority, int sampleRate, int bitsPerSample, int channelMode, long codecSpecific1, @@ -276,6 +302,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public int getCodecType() { return mCodecType; } @@ -296,6 +323,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public int getCodecPriority() { return mCodecPriority; } @@ -307,6 +335,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param codecPriority the codec priority */ + @UnsupportedAppUsage public void setCodecPriority(int codecPriority) { mCodecPriority = codecPriority; } @@ -324,6 +353,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public int getSampleRate() { return mSampleRate; } @@ -338,6 +368,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public int getBitsPerSample() { return mBitsPerSample; } @@ -351,6 +382,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec channel mode */ + @UnsupportedAppUsage public int getChannelMode() { return mChannelMode; } @@ -360,6 +392,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } @@ -369,6 +402,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value2 */ + @UnsupportedAppUsage public long getCodecSpecific2() { return mCodecSpecific2; } @@ -378,6 +412,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value3 */ + @UnsupportedAppUsage public long getCodecSpecific3() { return mCodecSpecific3; } @@ -387,6 +422,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value4 */ + @UnsupportedAppUsage public long getCodecSpecific4() { return mCodecSpecific4; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 3a05e7093d4..78560d2de42 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -37,6 +38,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.codec.extra.CODEC_STATUS"; @@ -137,6 +139,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -146,6 +149,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -155,6 +159,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ac21395c2fa..a429e701f7b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Parcel; @@ -115,6 +116,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_DISAPPEARED = "android.bluetooth.device.action.DISAPPEARED"; @@ -186,6 +188,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED"; @@ -306,6 +309,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; /** @@ -346,6 +350,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -390,6 +395,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.action.PAIRING_REQUEST"; /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; @@ -481,6 +487,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -489,6 +496,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -503,6 +511,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -510,6 +519,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -517,6 +527,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -524,6 +535,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -532,6 +544,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -610,6 +623,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ + @UnsupportedAppUsage public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; /** @@ -720,6 +734,7 @@ public final class BluetoothDevice implements Parcelable { private final String mAddress; /*package*/ + @UnsupportedAppUsage static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { @@ -763,6 +778,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException address is invalid * @hide */ + @UnsupportedAppUsage /*package*/ BluetoothDevice(String address) { getService(); // ensures sService is initialized if (!BluetoothAdapter.checkBluetoothAddress(address)) { @@ -883,6 +899,7 @@ public final class BluetoothDevice implements Parcelable { * @return the Bluetooth alias, or null if no alias or there was a problem * @hide */ + @UnsupportedAppUsage public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -907,6 +924,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @UnsupportedAppUsage public boolean setAlias(String alias) { final IBluetooth service = sService; if (service == null) { @@ -930,6 +948,7 @@ public final class BluetoothDevice implements Parcelable { * @see #getAlias() * @see #getName() */ + @UnsupportedAppUsage public String getAliasName() { String name = getAlias(); if (name == null) { @@ -948,6 +967,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1006,6 +1026,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException if an invalid transport was specified * @hide */ + @UnsupportedAppUsage public boolean createBond(int transport) { final IBluetooth service = sService; if (service == null) { @@ -1059,6 +1080,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1351,6 +1373,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean setPasskey(int passkey) { //TODO(BT) /* @@ -1391,6 +1414,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean cancelPairingUserInput() { final IBluetooth service = sService; if (service == null) { @@ -1406,6 +1430,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean isBluetoothDock() { // TODO(BT) /* @@ -1431,6 +1456,7 @@ public final class BluetoothDevice implements Parcelable { * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ + @UnsupportedAppUsage public int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1475,6 +1501,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ + @UnsupportedAppUsage public int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1497,6 +1524,7 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @UnsupportedAppUsage public boolean setMessageAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1539,6 +1567,7 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @UnsupportedAppUsage public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1577,6 +1606,7 @@ public final class BluetoothDevice implements Parcelable { * permissions * @hide */ + @UnsupportedAppUsage public BluetoothSocket createRfcommSocket(int channel) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1729,6 +1759,7 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ + @UnsupportedAppUsage public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1748,6 +1779,7 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ + @UnsupportedAppUsage public BluetoothSocket createScoSocket() throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1765,6 +1797,7 @@ public final class BluetoothDevice implements Parcelable { * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ + @UnsupportedAppUsage public static byte[] convertPinToBytes(String pin) { if (pin == null) { return null; @@ -1896,6 +1929,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ + @UnsupportedAppUsage public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 457119dc5bf..78248efdd04 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -41,16 +42,23 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; + @UnsupportedAppUsage private IBluetoothGatt mService; + @UnsupportedAppUsage private volatile BluetoothGattCallback mCallback; private Handler mHandler; + @UnsupportedAppUsage private int mClientIf; private BluetoothDevice mDevice; + @UnsupportedAppUsage private boolean mAutoConnect; + @UnsupportedAppUsage private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); + @UnsupportedAppUsage private Boolean mDeviceBusy = false; + @UnsupportedAppUsage private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -810,6 +818,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Unregister the current application and callbacks. */ + @UnsupportedAppUsage private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); if (mService == null || mClientIf == 0) return; @@ -845,6 +854,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ + @UnsupportedAppUsage /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { @@ -1407,6 +1417,7 @@ public final class BluetoothGatt implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public boolean refresh() { if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 2c124034b78..22bb956b04f 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,6 +15,7 @@ */ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -182,6 +183,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected int mInstance; /** @@ -218,6 +220,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothGattService mService; /** @@ -381,6 +384,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage /*package*/ void setService(BluetoothGattService service) { mService = service; } @@ -464,6 +468,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setKeySize(int keySize) { mKeySize = keySize; } diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 217a5abf1fe..3ffbb9e0c05 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -100,6 +101,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected int mInstance; /** @@ -114,6 +116,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothGattCharacteristic mCharacteristic; /** @@ -205,6 +208,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { mCharacteristic = characteristic; } diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index ce1dc1ce631..8e740ee387d 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,6 +15,7 @@ */ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -48,6 +49,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothDevice mDevice; /** @@ -265,6 +267,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setInstanceId(int instanceId) { mInstanceId = instanceId; } @@ -382,6 +385,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0c91a2054b8..636b1b9b13c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -110,6 +111,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -401,6 +403,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * results once close() has been called. Multiple invocations of close() * are ok. */ + @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); @@ -602,6 +605,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ + @UnsupportedAppUsage public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); final IBluetoothHeadset service = mService; @@ -719,6 +723,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -846,6 +851,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false if there was some error such as there is no active headset * @hide */ + @UnsupportedAppUsage public boolean connectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -872,6 +878,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false if audio is not connected, or on error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -909,6 +916,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -938,6 +946,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -962,6 +971,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type) { final IBluetoothHeadset service = mService; @@ -1060,6 +1070,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1089,6 +1100,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); @@ -1163,6 +1175,7 @@ public final class BluetoothHeadset implements BluetoothProfile { } }; + @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 031287f5ee1..a0a3c347718 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -476,6 +477,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = mService; @@ -498,6 +500,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = mService; @@ -720,6 +723,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ + @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = mService; @@ -766,6 +770,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

        */ + @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = mService; @@ -943,6 +948,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = mService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d46b2e37467..e02a2f4ae5d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -143,6 +144,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ + @UnsupportedAppUsage public int getId() { return mId; } @@ -162,6 +164,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ + @UnsupportedAppUsage public int getState() { return mState; } @@ -171,6 +174,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ + @UnsupportedAppUsage public String getNumber() { return mNumber; } @@ -189,6 +193,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ + @UnsupportedAppUsage public boolean isMultiParty() { return mMultiParty; } @@ -198,6 +203,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ + @UnsupportedAppUsage public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 159e165d594..606f00a8239 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -108,6 +109,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -401,6 +403,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -432,6 +435,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 5b55b23680c..b82680c1cae 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -233,6 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { * @param device Remote Bluetooth Device * @return false on error, true otherwise */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index af3b662d6a4..bff67f2980c 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 866b0630835..b5b7b1fe759 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -129,6 +130,7 @@ public final class BluetoothPan implements BluetoothProfile { * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ + @UnsupportedAppUsage /*package*/ BluetoothPan(Context context, ServiceListener l) { mContext = context; mServiceListener = l; @@ -142,6 +144,7 @@ public final class BluetoothPan implements BluetoothProfile { doBind(); } + @UnsupportedAppUsage boolean doBind() { Intent intent = new Intent(IBluetoothPan.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); @@ -154,6 +157,7 @@ public final class BluetoothPan implements BluetoothProfile { return true; } + @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); @@ -236,6 +240,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = mPanService; @@ -276,6 +281,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothPan service = mPanService; @@ -348,6 +354,7 @@ public final class BluetoothPan implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } + @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); final IBluetoothPan service = mPanService; @@ -360,6 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { } } + @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = mPanService; @@ -392,14 +400,17 @@ public final class BluetoothPan implements BluetoothProfile { } }; + @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } + @UnsupportedAppUsage private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } + @UnsupportedAppUsage private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 794435457f2..6e92a6ef71a 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.SdkConstant; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -281,6 +282,7 @@ public class BluetoothPbap implements BluetoothProfile { */ // TODO: This is currently being used by SettingsLib and will be used in the future. // TODO: Must specify target device. Implement this in the service. + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 6aeb94da117..9777b5cc6cd 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -20,6 +20,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import java.util.List; @@ -85,6 +86,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int PAN = 5; /** @@ -122,6 +124,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int A2DP_SINK = 11; /** @@ -129,6 +132,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int AVRCP_CONTROLLER = 12; /** @@ -192,6 +196,7 @@ public interface BluetoothProfile { * * @hide **/ + @UnsupportedAppUsage int PRIORITY_AUTO_CONNECT = 1000; /** @@ -217,6 +222,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int PRIORITY_UNDEFINED = -1; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48481620c97..4311255a07f 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -280,6 +281,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index ebb7f187aea..ba4b5a56679 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; @@ -69,6 +70,7 @@ public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; private static final boolean DBG = false; + @UnsupportedAppUsage /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 09a5b593e52..780f896139f 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; @@ -110,6 +111,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; + @UnsupportedAppUsage /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; @@ -129,10 +131,13 @@ public final class BluetoothSocket implements Closeable { private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ + @UnsupportedAppUsage private ParcelFileDescriptor mPfd; + @UnsupportedAppUsage private LocalSocket mSocket; private InputStream mSocketIS; private OutputStream mSocketOS; + @UnsupportedAppUsage private int mPort; /* RFCOMM channel or L2CAP psm */ private int mFd; private String mServiceName; @@ -517,6 +522,7 @@ public final class BluetoothSocket implements Closeable { * * @throws IOException if an i/o error occurs. */ + @UnsupportedAppUsage /*package*/ void flush() throws IOException { if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); if (VDBG) Log.d(TAG, "flush: " + mSocketOS); diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 605dbd21993..fdbfec00e62 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -37,16 +38,20 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ + @UnsupportedAppUsage public static final ParcelUuid AudioSink = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AudioSource = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid AdvAudioDist = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid Handsfree = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid Handsfree_AG = @@ -55,20 +60,24 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AvrcpTarget = ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid Hid = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); + @UnsupportedAppUsage public static final ParcelUuid Hogp = ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid PBAP_PCE = ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAP = @@ -92,10 +101,12 @@ public final class BluetoothUuid { /** Length of bytes for 128 bit UUID */ public static final int UUID_BYTES_128_BIT = 16; + @UnsupportedAppUsage public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; + @UnsupportedAppUsage public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } @@ -104,6 +115,7 @@ public final class BluetoothUuid { return uuid.equals(AudioSink); } + @UnsupportedAppUsage public static boolean isAdvAudioDist(ParcelUuid uuid) { return uuid.equals(AdvAudioDist); } @@ -120,6 +132,7 @@ public final class BluetoothUuid { return uuid.equals(AvrcpController); } + @UnsupportedAppUsage public static boolean isAvrcpTarget(ParcelUuid uuid) { return uuid.equals(AvrcpTarget); } @@ -162,6 +175,7 @@ public final class BluetoothUuid { * @param uuidArray - Array of ParcelUuids * @param uuid */ + @UnsupportedAppUsage public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { if ((uuidArray == null || uuidArray.length == 0) && uuid == null) { return true; @@ -183,6 +197,7 @@ public final class BluetoothUuid { * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids */ + @UnsupportedAppUsage public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; @@ -330,6 +345,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ + @UnsupportedAppUsage public static boolean is16BitUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { @@ -345,6 +361,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. */ + @UnsupportedAppUsage public static boolean is32BitUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 04dd060cae5..07ed18d90ee 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.Nullable; +import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; import android.util.ArrayMap; @@ -174,6 +175,7 @@ public final class ScanRecord { * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. * @hide */ + @UnsupportedAppUsage public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { return null; -- GitLab From de2b201ddbf64e0a14475155ce5635d829ea5692 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 1 Aug 2018 15:00:35 +0100 Subject: [PATCH 0961/1408] 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: I88a1311e27c5f9a5f9d1035db76034f86f650efc --- .../java/android/bluetooth/BluetoothA2dp.java | 23 ++++++++++++ .../android/bluetooth/BluetoothA2dpSink.java | 2 ++ .../android/bluetooth/BluetoothAdapter.java | 15 ++++++++ .../android/bluetooth/BluetoothClass.java | 5 +++ .../bluetooth/BluetoothCodecConfig.java | 36 +++++++++++++++++++ .../bluetooth/BluetoothCodecStatus.java | 5 +++ .../android/bluetooth/BluetoothDevice.java | 34 ++++++++++++++++++ .../java/android/bluetooth/BluetoothGatt.java | 11 ++++++ .../BluetoothGattCharacteristic.java | 5 +++ .../bluetooth/BluetoothGattDescriptor.java | 4 +++ .../bluetooth/BluetoothGattService.java | 4 +++ .../android/bluetooth/BluetoothHeadset.java | 13 +++++++ .../bluetooth/BluetoothHeadsetClient.java | 6 ++++ .../bluetooth/BluetoothHeadsetClientCall.java | 6 ++++ .../bluetooth/BluetoothHearingAid.java | 4 +++ .../java/android/bluetooth/BluetoothMap.java | 2 ++ .../android/bluetooth/BluetoothMapClient.java | 2 ++ .../java/android/bluetooth/BluetoothPan.java | 11 ++++++ .../java/android/bluetooth/BluetoothPbap.java | 2 ++ .../android/bluetooth/BluetoothProfile.java | 6 ++++ .../java/android/bluetooth/BluetoothSap.java | 2 ++ .../bluetooth/BluetoothServerSocket.java | 2 ++ .../android/bluetooth/BluetoothSocket.java | 6 ++++ .../java/android/bluetooth/BluetoothUuid.java | 17 +++++++++ .../java/android/bluetooth/le/ScanRecord.java | 2 ++ 25 files changed, 225 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 94fd138ca6e..966f9025e8a 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -117,6 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -137,6 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -160,6 +163,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; /** @@ -167,6 +171,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; /** @@ -174,6 +179,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_SUPPORTED = 1; /** @@ -182,6 +188,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; /** @@ -189,6 +196,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; /** @@ -196,6 +204,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; private Context mContext; @@ -268,6 +277,7 @@ public final class BluetoothA2dp implements BluetoothProfile { return true; } + @UnsupportedAppUsage /*package*/ void close() { mServiceListener = null; IBluetoothManager mgr = mAdapter.getBluetoothManager(); @@ -315,6 +325,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { @@ -357,6 +368,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { @@ -460,6 +472,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -490,6 +503,7 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @Nullable + @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { @@ -556,6 +570,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { @@ -671,6 +686,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ + @UnsupportedAppUsage public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { @@ -698,6 +714,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ + @UnsupportedAppUsage public void setCodecConfigPreference(BluetoothDevice device, BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); @@ -723,6 +740,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ + @UnsupportedAppUsage public void enableOptionalCodecs(BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, true); @@ -735,6 +753,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ + @UnsupportedAppUsage public void disableOptionalCodecs(BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, false); @@ -775,6 +794,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ + @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -799,6 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ + @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -824,6 +845,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ + @UnsupportedAppUsage public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN @@ -854,6 +876,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public static String stateToString(int state) { switch (state) { case STATE_DISCONNECTED: diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 13f0aaf47f0..fda2f892753 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -278,6 +279,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothA2dpSink service = mService; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1b9e27ca707..3c22905550c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; @@ -633,6 +634,7 @@ public final class BluetoothAdapter { private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; + @UnsupportedAppUsage private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -988,6 +990,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState + @UnsupportedAppUsage public int getLeState() { int state = BluetoothAdapter.STATE_OFF; @@ -1098,6 +1101,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ + @UnsupportedAppUsage public boolean disable(boolean persist) { try { @@ -1149,6 +1153,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ + @UnsupportedAppUsage public boolean factoryReset() { try { mServiceLock.readLock().lock(); @@ -1172,6 +1177,7 @@ public final class BluetoothAdapter { * @return the UUIDs supported by the local Bluetooth Adapter. * @hide */ + @UnsupportedAppUsage public ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; @@ -1438,6 +1444,7 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ + @UnsupportedAppUsage public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) { return false; @@ -1456,6 +1463,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public boolean setScanMode(int mode) { if (getState() != STATE_ON) { return false; @@ -1465,6 +1473,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public int getDiscoverableTimeout() { if (getState() != STATE_ON) { return -1; @@ -1483,6 +1492,7 @@ public final class BluetoothAdapter { } /** @hide */ + @UnsupportedAppUsage public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) { return; @@ -2007,6 +2017,7 @@ public final class BluetoothAdapter { * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} * @hide */ + @UnsupportedAppUsage public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2094,6 +2105,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @UnsupportedAppUsage public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2206,6 +2218,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @UnsupportedAppUsage public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); @@ -2749,6 +2762,7 @@ public final class BluetoothAdapter { return true; } + @UnsupportedAppUsage /*package*/ IBluetoothManager getBluetoothManager() { return mManagerService; } @@ -2756,6 +2770,7 @@ public final class BluetoothAdapter { private final ArrayList mProxyServiceStateCallbacks = new ArrayList(); + @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { synchronized (mProxyServiceStateCallbacks) { if (cb == null) { diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index f22ea6e88e0..8557f389d91 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -63,6 +64,7 @@ public final class BluetoothClass implements Parcelable { private final int mClass; /** @hide */ + @UnsupportedAppUsage public BluetoothClass(int classInt) { mClass = classInt; } @@ -322,8 +324,10 @@ public final class BluetoothClass implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public static final int PROFILE_HEADSET = 0; /** @hide */ + @UnsupportedAppUsage public static final int PROFILE_A2DP = 1; /** @hide */ public static final int PROFILE_OPP = 2; @@ -346,6 +350,7 @@ public final class BluetoothClass implements Parcelable { * @return True if this device might support specified profile. * @hide */ + @UnsupportedAppUsage public boolean doesClassMatch(int profile) { if (profile == PROFILE_A2DP) { if (hasService(Service.RENDER)) { diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index e3a6e064725..79c0a3a207c 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -32,34 +33,58 @@ public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final int mCodecType; @@ -72,6 +97,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(int codecType, int codecPriority, int sampleRate, int bitsPerSample, int channelMode, long codecSpecific1, @@ -276,6 +302,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public int getCodecType() { return mCodecType; } @@ -296,6 +323,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public int getCodecPriority() { return mCodecPriority; } @@ -307,6 +335,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param codecPriority the codec priority */ + @UnsupportedAppUsage public void setCodecPriority(int codecPriority) { mCodecPriority = codecPriority; } @@ -324,6 +353,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public int getSampleRate() { return mSampleRate; } @@ -338,6 +368,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public int getBitsPerSample() { return mBitsPerSample; } @@ -351,6 +382,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec channel mode */ + @UnsupportedAppUsage public int getChannelMode() { return mChannelMode; } @@ -360,6 +392,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } @@ -369,6 +402,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value2 */ + @UnsupportedAppUsage public long getCodecSpecific2() { return mCodecSpecific2; } @@ -378,6 +412,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value3 */ + @UnsupportedAppUsage public long getCodecSpecific3() { return mCodecSpecific3; } @@ -387,6 +422,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value4 */ + @UnsupportedAppUsage public long getCodecSpecific4() { return mCodecSpecific4; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 3a05e7093d4..78560d2de42 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -37,6 +38,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.codec.extra.CODEC_STATUS"; @@ -137,6 +139,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -146,6 +149,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -155,6 +159,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7a6b72e980f..818a749842f 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Parcel; @@ -115,6 +116,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_DISAPPEARED = "android.bluetooth.device.action.DISAPPEARED"; @@ -186,6 +188,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED"; @@ -306,6 +309,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; /** @@ -346,6 +350,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -390,6 +395,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.action.PAIRING_REQUEST"; /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; @@ -481,6 +487,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -489,6 +496,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -503,6 +511,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -510,6 +519,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -517,6 +527,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -524,6 +535,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -532,6 +544,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -610,6 +623,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ + @UnsupportedAppUsage public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; /** @@ -720,6 +734,7 @@ public final class BluetoothDevice implements Parcelable { private final String mAddress; /*package*/ + @UnsupportedAppUsage static IBluetooth getService() { synchronized (BluetoothDevice.class) { if (sService == null) { @@ -763,6 +778,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException address is invalid * @hide */ + @UnsupportedAppUsage /*package*/ BluetoothDevice(String address) { getService(); // ensures sService is initialized if (!BluetoothAdapter.checkBluetoothAddress(address)) { @@ -887,6 +903,7 @@ public final class BluetoothDevice implements Parcelable { * @return the Bluetooth alias, or null if no alias or there was a problem * @hide */ + @UnsupportedAppUsage public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -911,6 +928,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ + @UnsupportedAppUsage public boolean setAlias(String alias) { final IBluetooth service = sService; if (service == null) { @@ -934,6 +952,7 @@ public final class BluetoothDevice implements Parcelable { * @see #getAlias() * @see #getName() */ + @UnsupportedAppUsage public String getAliasName() { String name = getAlias(); if (name == null) { @@ -952,6 +971,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1010,6 +1030,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException if an invalid transport was specified * @hide */ + @UnsupportedAppUsage public boolean createBond(int transport) { final IBluetooth service = sService; if (service == null) { @@ -1063,6 +1084,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1355,6 +1377,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean setPasskey(int passkey) { //TODO(BT) /* @@ -1395,6 +1418,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean cancelPairingUserInput() { final IBluetooth service = sService; if (service == null) { @@ -1410,6 +1434,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ + @UnsupportedAppUsage public boolean isBluetoothDock() { // TODO(BT) /* @@ -1435,6 +1460,7 @@ public final class BluetoothDevice implements Parcelable { * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ + @UnsupportedAppUsage public int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1479,6 +1505,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ + @UnsupportedAppUsage public int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1501,6 +1528,7 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @UnsupportedAppUsage public boolean setMessageAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1543,6 +1571,7 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the value has been successfully set. * @hide */ + @UnsupportedAppUsage public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1581,6 +1610,7 @@ public final class BluetoothDevice implements Parcelable { * permissions * @hide */ + @UnsupportedAppUsage public BluetoothSocket createRfcommSocket(int channel) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1733,6 +1763,7 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ + @UnsupportedAppUsage public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1752,6 +1783,7 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ + @UnsupportedAppUsage public BluetoothSocket createScoSocket() throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1769,6 +1801,7 @@ public final class BluetoothDevice implements Parcelable { * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ + @UnsupportedAppUsage public static byte[] convertPinToBytes(String pin) { if (pin == null) { return null; @@ -1900,6 +1933,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ + @UnsupportedAppUsage public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 457119dc5bf..78248efdd04 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -41,16 +42,23 @@ public final class BluetoothGatt implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; + @UnsupportedAppUsage private IBluetoothGatt mService; + @UnsupportedAppUsage private volatile BluetoothGattCallback mCallback; private Handler mHandler; + @UnsupportedAppUsage private int mClientIf; private BluetoothDevice mDevice; + @UnsupportedAppUsage private boolean mAutoConnect; + @UnsupportedAppUsage private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); + @UnsupportedAppUsage private Boolean mDeviceBusy = false; + @UnsupportedAppUsage private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -810,6 +818,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Unregister the current application and callbacks. */ + @UnsupportedAppUsage private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); if (mService == null || mClientIf == 0) return; @@ -845,6 +854,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ + @UnsupportedAppUsage /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { @@ -1407,6 +1417,7 @@ public final class BluetoothGatt implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public boolean refresh() { if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 243ad359a48..6d46b3a4186 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,6 +15,7 @@ */ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -182,6 +183,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected int mInstance; /** @@ -218,6 +220,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothGattService mService; /** @@ -381,6 +384,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage /*package*/ void setService(BluetoothGattService service) { mService = service; } @@ -464,6 +468,7 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setKeySize(int keySize) { mKeySize = keySize; } diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 217a5abf1fe..3ffbb9e0c05 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -100,6 +101,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected int mInstance; /** @@ -114,6 +116,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothGattCharacteristic mCharacteristic; /** @@ -205,6 +208,7 @@ public class BluetoothGattDescriptor implements Parcelable { * * @hide */ + @UnsupportedAppUsage /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { mCharacteristic = characteristic; } diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index ce1dc1ce631..8e740ee387d 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,6 +15,7 @@ */ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -48,6 +49,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage protected BluetoothDevice mDevice; /** @@ -265,6 +267,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setInstanceId(int instanceId) { mInstanceId = instanceId; } @@ -382,6 +385,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ + @UnsupportedAppUsage public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0c91a2054b8..636b1b9b13c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -110,6 +111,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -401,6 +403,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * results once close() has been called. Multiple invocations of close() * are ok. */ + @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); @@ -602,6 +605,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ + @UnsupportedAppUsage public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); final IBluetoothHeadset service = mService; @@ -719,6 +723,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -846,6 +851,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false if there was some error such as there is no active headset * @hide */ + @UnsupportedAppUsage public boolean connectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -872,6 +878,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return false if audio is not connected, or on error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -909,6 +916,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -938,6 +946,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -962,6 +971,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type) { final IBluetoothHeadset service = mService; @@ -1060,6 +1070,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1089,6 +1100,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); @@ -1163,6 +1175,7 @@ public final class BluetoothHeadset implements BluetoothProfile { } }; + @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 397b90656f3..ec18d42698c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -476,6 +477,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = mService; @@ -498,6 +500,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = mService; @@ -720,6 +723,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ + @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = mService; @@ -766,6 +770,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

        */ + @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = mService; @@ -943,6 +948,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = mService; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d46b2e37467..e02a2f4ae5d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -143,6 +144,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ + @UnsupportedAppUsage public int getId() { return mId; } @@ -162,6 +164,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ + @UnsupportedAppUsage public int getState() { return mState; } @@ -171,6 +174,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ + @UnsupportedAppUsage public String getNumber() { return mNumber; } @@ -189,6 +193,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ + @UnsupportedAppUsage public boolean isMultiParty() { return mMultiParty; } @@ -198,6 +203,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ + @UnsupportedAppUsage public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 159e165d594..606f00a8239 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -108,6 +109,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -401,6 +403,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -432,6 +435,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) + @UnsupportedAppUsage public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 0fa1d5d6295..98c23c600f1 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -233,6 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { * @param device Remote Bluetooth Device * @return false on error, true otherwise */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 4f21d936e56..183be5f38bd 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 9f401eb3cef..58be7329602 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -129,6 +130,7 @@ public final class BluetoothPan implements BluetoothProfile { * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ + @UnsupportedAppUsage /*package*/ BluetoothPan(Context context, ServiceListener l) { mContext = context; mServiceListener = l; @@ -142,6 +144,7 @@ public final class BluetoothPan implements BluetoothProfile { doBind(); } + @UnsupportedAppUsage boolean doBind() { Intent intent = new Intent(IBluetoothPan.class.getName()); ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); @@ -154,6 +157,7 @@ public final class BluetoothPan implements BluetoothProfile { return true; } + @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); @@ -236,6 +240,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = mPanService; @@ -276,6 +281,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothPan service = mPanService; @@ -348,6 +354,7 @@ public final class BluetoothPan implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } + @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); final IBluetoothPan service = mPanService; @@ -360,6 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { } } + @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = mPanService; @@ -392,14 +400,17 @@ public final class BluetoothPan implements BluetoothProfile { } }; + @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } + @UnsupportedAppUsage private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } + @UnsupportedAppUsage private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index c60e9e075c8..ae264e19bb7 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.SdkConstant; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -281,6 +282,7 @@ public class BluetoothPbap implements BluetoothProfile { */ // TODO: This is currently being used by SettingsLib and will be used in the future. // TODO: Must specify target device. Implement this in the service. + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 6aeb94da117..9777b5cc6cd 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -20,6 +20,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.UnsupportedAppUsage; import java.util.List; @@ -85,6 +86,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int PAN = 5; /** @@ -122,6 +124,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int A2DP_SINK = 11; /** @@ -129,6 +132,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int AVRCP_CONTROLLER = 12; /** @@ -192,6 +196,7 @@ public interface BluetoothProfile { * * @hide **/ + @UnsupportedAppUsage int PRIORITY_AUTO_CONNECT = 1000; /** @@ -217,6 +222,7 @@ public interface BluetoothProfile { * * @hide */ + @UnsupportedAppUsage int PRIORITY_UNDEFINED = -1; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index c51e39a7418..1b732062f61 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -280,6 +281,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index ebb7f187aea..ba4b5a56679 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; @@ -69,6 +70,7 @@ public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; private static final boolean DBG = false; + @UnsupportedAppUsage /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 09a5b593e52..780f896139f 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; @@ -110,6 +111,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; + @UnsupportedAppUsage /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; @@ -129,10 +131,13 @@ public final class BluetoothSocket implements Closeable { private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ + @UnsupportedAppUsage private ParcelFileDescriptor mPfd; + @UnsupportedAppUsage private LocalSocket mSocket; private InputStream mSocketIS; private OutputStream mSocketOS; + @UnsupportedAppUsage private int mPort; /* RFCOMM channel or L2CAP psm */ private int mFd; private String mServiceName; @@ -517,6 +522,7 @@ public final class BluetoothSocket implements Closeable { * * @throws IOException if an i/o error occurs. */ + @UnsupportedAppUsage /*package*/ void flush() throws IOException { if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); if (VDBG) Log.d(TAG, "flush: " + mSocketOS); diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 605dbd21993..fdbfec00e62 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -37,16 +38,20 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ + @UnsupportedAppUsage public static final ParcelUuid AudioSink = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AudioSource = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid AdvAudioDist = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid Handsfree = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid Handsfree_AG = @@ -55,20 +60,24 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AvrcpTarget = ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid Hid = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); + @UnsupportedAppUsage public static final ParcelUuid Hogp = ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid PBAP_PCE = ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); + @UnsupportedAppUsage public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid MAP = @@ -92,10 +101,12 @@ public final class BluetoothUuid { /** Length of bytes for 128 bit UUID */ public static final int UUID_BYTES_128_BIT = 16; + @UnsupportedAppUsage public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; + @UnsupportedAppUsage public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } @@ -104,6 +115,7 @@ public final class BluetoothUuid { return uuid.equals(AudioSink); } + @UnsupportedAppUsage public static boolean isAdvAudioDist(ParcelUuid uuid) { return uuid.equals(AdvAudioDist); } @@ -120,6 +132,7 @@ public final class BluetoothUuid { return uuid.equals(AvrcpController); } + @UnsupportedAppUsage public static boolean isAvrcpTarget(ParcelUuid uuid) { return uuid.equals(AvrcpTarget); } @@ -162,6 +175,7 @@ public final class BluetoothUuid { * @param uuidArray - Array of ParcelUuids * @param uuid */ + @UnsupportedAppUsage public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { if ((uuidArray == null || uuidArray.length == 0) && uuid == null) { return true; @@ -183,6 +197,7 @@ public final class BluetoothUuid { * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids */ + @UnsupportedAppUsage public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; @@ -330,6 +345,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ + @UnsupportedAppUsage public static boolean is16BitUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { @@ -345,6 +361,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. */ + @UnsupportedAppUsage public static boolean is32BitUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 04dd060cae5..07ed18d90ee 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.Nullable; +import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; import android.os.ParcelUuid; import android.util.ArrayMap; @@ -174,6 +175,7 @@ public final class ScanRecord { * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. * @hide */ + @UnsupportedAppUsage public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { return null; -- GitLab From 97f92616b0c91891e717a7d5fd053ea146b9878a Mon Sep 17 00:00:00 2001 From: Ralph Nathan Date: Mon, 23 Jul 2018 09:35:18 -0700 Subject: [PATCH 0962/1408] Mark BluetoothClass#getClassOfDevice as TestApi This method is being used the Android Things instrumentation test apk. Bug: 111654175 Test: tests pass Change-Id: Id3fcd2d89789868e50048542fd1dfe25d9986103 --- framework/java/android/bluetooth/BluetoothClass.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 8557f389d91..3a78cbdd4d0 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -293,6 +294,7 @@ public final class BluetoothClass implements Parcelable { * * @hide */ + @TestApi public int getClassOfDevice() { return mClass; } -- GitLab From 2450589220dfebc32fc702f6dd27c2692658d9c7 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Mon, 6 Aug 2018 17:45:38 +0200 Subject: [PATCH 0963/1408] Fix NPE when starting advertising with null GATT Bug: 112164168 Change-Id: I82be0e06e990cc116d3ab48de80b5bb170d69892 --- .../java/android/bluetooth/le/BluetoothLeAdvertiser.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 0fb4ba1a876..13c5ff69097 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -411,7 +411,14 @@ public final class BluetoothLeAdvertiser { try { gatt = mBluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + Log.e(TAG, "Failed to get Bluetooth GATT - ", e); + postStartSetFailure(handler, callback, + AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); + return; + } + + if (gatt == null) { + Log.e(TAG, "Bluetooth GATT is null"); postStartSetFailure(handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); return; -- GitLab From 2d006b8a24d6ac043ece40c4dcff0cf3ab61323c Mon Sep 17 00:00:00 2001 From: jonerlin Date: Thu, 9 Aug 2018 16:39:43 +0800 Subject: [PATCH 0964/1408] AdapterService: Only bind HeadsetService in ON state. * there will be an endless loop to call doBind and cause many connectionRecords of HeadsetService while binding HeadsetService in BLE_ON state. * allow to bind HeadsetService only when BT state is ON Bug: 111730478 Test: Keep device in BLE_ON state -> kill com.android.phone process -> As time goes on, adb shell dumpsys activity services to check the connectionRecords of HeadsetService will not become more and more Change-Id: I5f66d015b8aa3c94c7e887ef033213af74235b4a --- .../bluetooth/BluetoothManagerService.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aa426d3cd31..99e0056254f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1161,6 +1161,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean bindService() { + int state = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + state = mBluetooth.getState(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return false; + } finally { + mBluetoothLock.readLock().unlock(); + } + + if (!mEnable || state != BluetoothAdapter.STATE_ON) { + if (DBG) { + Slog.d(TAG, "Unable to bindService while Bluetooth is disabled"); + } + return false; + } + if (mIntent != null && mService == null && doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); -- GitLab From 4ddfc18023ea4ec2dc0f190efda0387fe310ffdf Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 20 Aug 2018 13:04:15 -0700 Subject: [PATCH 0965/1408] Add note for FLAG_CANCEL_CURRENT when stopping scan Added a note in the Android API documentation for stopScan to make sure that the flag FLAG_CANCEL_CURRENT is not used when creating the PendingIntent parameter. Bug: 77658091 Test: Just compile since not logic is changed Change-Id: I55f33ae68679310ba4899708a2072a40fea0b3b4 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index a189e271379..a337a40094d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -275,7 +275,9 @@ public final class BluetoothLeScanner { } /** - * Stops an ongoing Bluetooth LE scan started using a PendingIntent. + * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the + * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop + * scan may have no effect. * * @param callbackIntent The PendingIntent that was used to start the scan. * @see #startScan(List, ScanSettings, PendingIntent) -- GitLab From 167334ff4b73ddefdb9eb671758cca7a0f388f2a Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Tue, 21 Aug 2018 12:17:42 +0800 Subject: [PATCH 0966/1408] Be the current user to get A2DP proxy After user switched, Bluetooth process will be restarted to run as secondary user and some system process like AudioService keeps as primary user. When BluetoothA2dp did rebind, it still used primary UID for those system process and caused they could not invoke A2DP APIs. This changes to use current user ID when rebinding to A2dpService and system process can get correct A2DP proxy after user switched. Bug: 112469503 Test: manual Change-Id: If5ee945b0dcf2044ef2baedd762751fa6ef3d09a --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 966f9025e8a..d21f76d435e 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -30,6 +30,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -270,7 +271,7 @@ public final class BluetoothA2dp implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); return false; } -- GitLab From cb5aa1266854dcdb1d54f3c3089f2ae4d5e1a7f6 Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Tue, 21 Aug 2018 12:17:42 +0800 Subject: [PATCH 0967/1408] Be the current user to get A2DP proxy After user switched, Bluetooth process will be restarted to run as secondary user and some system process like AudioService keeps as primary user. When BluetoothA2dp did rebind, it still used primary UID for those system process and caused they could not invoke A2DP APIs. This changes to use current user ID when rebinding to A2dpService and system process can get correct A2DP proxy after user switched. Bug: 112469503 Test: manual Change-Id: If5ee945b0dcf2044ef2baedd762751fa6ef3d09a (cherry picked from commit 167334ff4b73ddefdb9eb671758cca7a0f388f2a) --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 94fd138ca6e..864e6903be9 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -261,7 +262,7 @@ public final class BluetoothA2dp implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); return false; } -- GitLab From a5b13dd720c08bc2bcf80712ef213aa1041102f4 Mon Sep 17 00:00:00 2001 From: Nitin Shivpure Date: Mon, 2 Apr 2018 13:45:45 +0530 Subject: [PATCH 0968/1408] BLE: Add service solicitation uuid feature in scan filter Adding service solicitation uuid feature in scan filter, So BLE Scanner can set scan filter for advertising packets that includes the Service Solicitation uuid, which can be one of the below types. - List of 16 bit Service UUIDs - List of 32 bit Service UUIDs - List of 128 bit Service UUIDs Test: BLE Scanner can do filter scan for advertising packets that includes the Service Solicitation uuid. Bug: 78483310 Change-Id: I3d83c50e446fca06a76db002dad716759c145d6e --- .../java/android/bluetooth/le/ScanFilter.java | 130 +++++++++++++++++- .../java/android/bluetooth/le/ScanRecord.java | 53 ++++++- 2 files changed, 175 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index c3fae7d470a..c5d435b7613 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -57,6 +57,11 @@ public final class ScanFilter implements Parcelable { @Nullable private final ParcelUuid mServiceUuidMask; + @Nullable + private final ParcelUuid mServiceSolicitationUuid; + @Nullable + private final ParcelUuid mServiceSolicitationUuidMask; + @Nullable private final ParcelUuid mServiceDataUuid; @Nullable @@ -75,12 +80,15 @@ public final class ScanFilter implements Parcelable { private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, - ParcelUuid uuidMask, ParcelUuid serviceDataUuid, + ParcelUuid uuidMask, ParcelUuid solicitationUuid, + ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; + mServiceSolicitationUuid = solicitationUuid; + mServiceSolicitationUuidMask = solicitationUuidMask; mDeviceAddress = deviceAddress; mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; @@ -113,6 +121,14 @@ public final class ScanFilter implements Parcelable { dest.writeParcelable(mServiceUuidMask, flags); } } + dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1); + if (mServiceSolicitationUuid != null) { + dest.writeParcelable(mServiceSolicitationUuid, flags); + dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1); + if (mServiceSolicitationUuidMask != null) { + dest.writeParcelable(mServiceSolicitationUuidMask, flags); + } + } dest.writeInt(mServiceDataUuid == null ? 0 : 1); if (mServiceDataUuid != null) { dest.writeParcelable(mServiceDataUuid, flags); @@ -171,6 +187,17 @@ public final class ScanFilter implements Parcelable { builder.setServiceUuid(uuid, uuidMask); } } + if (in.readInt() == 1) { + ParcelUuid solicitationUuid = in.readParcelable( + ParcelUuid.class.getClassLoader()); + builder.setServiceSolicitationUuid(solicitationUuid); + if (in.readInt() == 1) { + ParcelUuid solicitationUuidMask = in.readParcelable( + ParcelUuid.class.getClassLoader()); + builder.setServiceSolicitationUuid(solicitationUuid, + solicitationUuidMask); + } + } if (in.readInt() == 1) { ParcelUuid servcieDataUuid = in.readParcelable(ParcelUuid.class.getClassLoader()); @@ -231,6 +258,22 @@ public final class ScanFilter implements Parcelable { return mServiceUuidMask; } + /** + * Returns the filter set on the service Solicitation uuid. + */ + @Nullable + public ParcelUuid getServiceSolicitationUuid() { + return mServiceSolicitationUuid; + } + + /** + * Returns the filter set on the service Solicitation uuid mask. + */ + @Nullable + public ParcelUuid getServiceSolicitationUuidMask() { + return mServiceSolicitationUuidMask; + } + @Nullable public String getDeviceAddress() { return mDeviceAddress; @@ -288,7 +331,7 @@ public final class ScanFilter implements Parcelable { // Scan record is null but there exist filters on it. if (scanRecord == null && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null - || mServiceData != null)) { + || mServiceData != null || mServiceSolicitationUuid != null)) { return false; } @@ -303,6 +346,13 @@ public final class ScanFilter implements Parcelable { return false; } + // solicitation UUID match. + if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids( + mServiceSolicitationUuid, mServiceSolicitationUuidMask, + scanRecord.getServiceSolicitationUuids())) { + return false; + } + // Service data match if (mServiceDataUuid != null) { if (!matchesPartialData(mServiceData, mServiceDataMask, @@ -350,6 +400,36 @@ public final class ScanFilter implements Parcelable { return BitUtils.maskedEquals(data, uuid, mask); } + /** + * Check if the solicitation uuid pattern is contained in a list of parcel uuids. + * + */ + private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid, + ParcelUuid parcelSolicitationUuidMask, List solicitationUuids) { + if (solicitationUuid == null) { + return true; + } + if (solicitationUuids == null) { + return false; + } + + for (ParcelUuid parcelSolicitationUuid : solicitationUuids) { + UUID solicitationUuidMask = parcelSolicitationUuidMask == null + ? null : parcelSolicitationUuidMask.getUuid(); + if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask, + parcelSolicitationUuid.getUuid())) { + return true; + } + } + return false; + } + + // Check if the solicitation uuid pattern matches the particular service solicitation uuid. + private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid, + UUID solicitationUuidMask, UUID data) { + return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask); + } + // Check whether the data pattern matches the parsed data. private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { if (parsedData == null || parsedData.length < data.length) { @@ -376,6 +456,8 @@ public final class ScanFilter implements Parcelable { return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + mDeviceAddress + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid + + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId @@ -391,7 +473,8 @@ public final class ScanFilter implements Parcelable { mServiceDataUuid, Arrays.hashCode(mServiceData), Arrays.hashCode(mServiceDataMask), - mServiceUuid, mServiceUuidMask); + mServiceUuid, mServiceUuidMask, + mServiceSolicitationUuid, mServiceSolicitationUuidMask); } @Override @@ -412,7 +495,10 @@ public final class ScanFilter implements Parcelable { && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) - && Objects.equals(mServiceUuidMask, other.mServiceUuidMask); + && Objects.equals(mServiceUuidMask, other.mServiceUuidMask) + && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid) + && Objects.equals(mServiceSolicitationUuidMask, + other.mServiceSolicitationUuidMask); } /** @@ -435,6 +521,9 @@ public final class ScanFilter implements Parcelable { private ParcelUuid mServiceUuid; private ParcelUuid mUuidMask; + private ParcelUuid mServiceSolicitationUuid; + private ParcelUuid mServiceSolicitationUuidMask; + private ParcelUuid mServiceDataUuid; private byte[] mServiceData; private byte[] mServiceDataMask; @@ -493,6 +582,36 @@ public final class ScanFilter implements Parcelable { return this; } + + /** + * Set filter on service solicitation uuid. + */ + public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) { + mServiceSolicitationUuid = serviceSolicitationUuid; + return this; + } + + + /** + * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the + * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to + * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to + * ignore that bit. + * + * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but + * {@code serviceSolicitationUuidMask} is not {@code null}. + */ + public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid, + ParcelUuid solicitationUuidMask) { + if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) { + throw new IllegalArgumentException( + "SolicitationUuid is null while SolicitationUuidMask is not null!"); + } + mServiceSolicitationUuid = serviceSolicitationUuid; + mServiceSolicitationUuidMask = solicitationUuidMask; + return this; + } + /** * Set filtering on service data. * @@ -598,7 +717,8 @@ public final class ScanFilter implements Parcelable { */ public ScanFilter build() { return new ScanFilter(mDeviceName, mDeviceAddress, - mServiceUuid, mUuidMask, + mServiceUuid, mUuidMask, mServiceSolicitationUuid, + mServiceSolicitationUuidMask, mServiceDataUuid, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask); } diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 07ed18d90ee..7988008f03c 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -51,6 +51,9 @@ public final class ScanRecord { private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16; private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20; private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21; + private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14; + private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F; + private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15; private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; // Flags of the advertising data. @@ -58,6 +61,8 @@ public final class ScanRecord { @Nullable private final List mServiceUuids; + @Nullable + private final List mServiceSolicitationUuids; private final SparseArray mManufacturerSpecificData; @@ -88,6 +93,15 @@ public final class ScanRecord { return mServiceUuids; } + /** + * Returns a list of service solicitation UUIDs within the advertisement that are used to + * identify the Bluetooth GATT services. + */ + @Nullable + public List getServiceSolicitationUuids() { + return mServiceSolicitationUuids; + } + /** * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific * data. @@ -151,10 +165,12 @@ public final class ScanRecord { } private ScanRecord(List serviceUuids, + List serviceSolicitationUuids, SparseArray manufacturerData, Map serviceData, int advertiseFlags, int txPowerLevel, String localName, byte[] bytes) { + mServiceSolicitationUuids = serviceSolicitationUuids; mServiceUuids = serviceUuids; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; @@ -184,6 +200,7 @@ public final class ScanRecord { int currentPos = 0; int advertiseFlag = -1; List serviceUuids = new ArrayList(); + List serviceSolicitationUuids = new ArrayList(); String localName = null; int txPowerLevel = Integer.MIN_VALUE; @@ -220,6 +237,18 @@ public final class ScanRecord { parseServiceUuid(scanRecord, currentPos, dataLength, BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); break; + case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT: + parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids); + break; + case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT: + parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids); + break; + case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT: + parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, + BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids); + break; case DATA_TYPE_LOCAL_NAME_SHORT: case DATA_TYPE_LOCAL_NAME_COMPLETE: localName = new String( @@ -265,19 +294,23 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - return new ScanRecord(serviceUuids, manufacturerData, serviceData, - advertiseFlag, txPowerLevel, localName, scanRecord); + if (serviceSolicitationUuids.isEmpty()) { + serviceSolicitationUuids = null; + } + return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, + serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); } catch (Exception e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); // As the record is invalid, ignore all the parsed results for this packet // and return an empty record with raw scanRecord bytes in results - return new ScanRecord(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord); + return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord); } } @Override public String toString() { return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids + + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString( mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) @@ -297,6 +330,20 @@ public final class ScanRecord { return currentPos; } + /** + * Parse service Solicitation UUIDs. + */ + private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos, + int dataLength, int uuidLength, List serviceSolicitationUuids) { + while (dataLength > 0) { + byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength); + serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); + dataLength -= uuidLength; + currentPos += uuidLength; + } + return currentPos; + } + // Helper method to extract bytes from byte array. private static byte[] extractBytes(byte[] scanRecord, int start, int length) { byte[] bytes = new byte[length]; -- GitLab From f0219f71187399abd44e6cea71678344a2ba1983 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Mon, 20 Aug 2018 13:04:15 -0700 Subject: [PATCH 0969/1408] Add note for FLAG_CANCEL_CURRENT when stopping scan Added a note in the Android API documentation for stopScan to make sure that the flag FLAG_CANCEL_CURRENT is not used when creating the PendingIntent parameter. Bug: 77658091 Test: Just compile since not logic is changed Change-Id: I55f33ae68679310ba4899708a2072a40fea0b3b4 (cherry picked from commit 20fc9a250e4d3f6eb30ca08481af77f4abd01843) --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 347fc4df4eb..804cf9a9071 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -275,7 +275,9 @@ public final class BluetoothLeScanner { } /** - * Stops an ongoing Bluetooth LE scan started using a PendingIntent. + * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the + * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop + * scan may have no effect. * * @param callbackIntent The PendingIntent that was used to start the scan. * @see #startScan(List, ScanSettings, PendingIntent) -- GitLab From 412cbec0c57647b955747e9b55bce071870b9a1e Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 29 Jun 2018 14:05:04 -0700 Subject: [PATCH 0970/1408] Unhide the LE CoC APIs Expose the LE Connection-oriented Channels APIs for applications to use. Test: Run the SL4A ACTS test: BleCocTest Bug: 70683224 Change-Id: I68128bc7154966ec065091c973351f8892da9b4d --- .../android/bluetooth/BluetoothAdapter.java | 59 ++++++++++++------- .../android/bluetooth/BluetoothDevice.java | 56 +++++++++++------- .../bluetooth/BluetoothServerSocket.java | 5 +- .../android/bluetooth/BluetoothSocket.java | 4 ++ 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4c655b5ac02..654bfaf293b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -81,7 +81,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to * listen for incoming RFComm connection requests with {@link - * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for + * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented + * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

        *

        This class is thread safe.

        @@ -2967,7 +2968,7 @@ public final class BluetoothAdapter { /** * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen - * for incoming connections. + * for incoming connections. The supported Bluetooth transport is LE only. *

        A remote device connecting to this socket will be authenticated and communication on this * socket will be encrypted. *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening @@ -2977,21 +2978,16 @@ public final class BluetoothAdapter { * closed, Bluetooth is turned off, or the application exits unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is * defined and performed by the application. - *

        Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server + *

        Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server * socket from another Android device that is given the PSM value. * - * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} * @return an L2CAP CoC BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC - * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingL2capCoc(int transport) + public BluetoothServerSocket listenUsingL2capChannel() throws IOException { - if (transport != BluetoothDevice.TRANSPORT_LE) { - throw new IllegalArgumentException("Unsupported transport: " + transport); - } BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); @@ -3005,7 +3001,7 @@ public final class BluetoothAdapter { throw new IOException("Error: Unable to assign PSM value"); } if (DBG) { - Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to " + Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to " + assignedPsm); } socket.setChannel(assignedPsm); @@ -3013,11 +3009,24 @@ public final class BluetoothAdapter { return socket; } + /** + * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new + * API name, listenUsingL2capChannel. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingL2capCoc(int transport) + throws IOException { + Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel"); + return listenUsingL2capChannel(); + } + /** * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and - * assign a dynamic PSM value. This socket can be used to listen for incoming connections. + * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The + * supported Bluetooth transport is LE only. *

        The link key is not required to be authenticated, i.e the communication may be vulnerable - * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and + * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and * authenticated communication channel is desired. *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. @@ -3027,21 +3036,16 @@ public final class BluetoothAdapter { * unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is * defined and performed by the application. - *

        Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this - * server socket from another Android device that is given the PSM value. + *

        Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server + * socket from another Android device that is given the PSM value. * - * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} * @return an L2CAP CoC BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC - * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) + public BluetoothServerSocket listenUsingInsecureL2capChannel() throws IOException { - if (transport != BluetoothDevice.TRANSPORT_LE) { - throw new IllegalArgumentException("Unsupported transport: " + transport); - } BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); @@ -3055,11 +3059,24 @@ public final class BluetoothAdapter { throw new IOException("Error: Unable to assign PSM value"); } if (DBG) { - Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to " + Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to " + assignedPsm); } socket.setChannel(assignedPsm); return socket; } + + /** + * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new + * API name, listenUsingInsecureL2capChannel. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) + throws IOException { + Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, " + + "listenUsingInsecureL2capChannel"); + return listenUsingInsecureL2capChannel(); + } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 818a749842f..73e98cd99f8 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1963,8 +1963,8 @@ public final class BluetoothDevice implements Parcelable { /** * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can * be used to start a secure outgoing connection to the remote device with the same dynamic - * protocol/service multiplexer (PSM) value. - *

        This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for + * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. + *

        This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for * peer-peer Bluetooth applications. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing connection. *

        Application using this API is responsible for obtaining PSM value from remote device. @@ -1975,59 +1975,71 @@ public final class BluetoothDevice implements Parcelable { * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, * int)}. * - * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions - * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { + public BluetoothSocket createL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { - Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled"); + Log.e(TAG, "createL2capChannel: Bluetooth is not enabled"); throw new IOException(); } - if (transport != BluetoothDevice.TRANSPORT_LE) { - throw new IllegalArgumentException("Unsupported transport: " + transport); - } - if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm); + if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm); return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm, null); } + /** + * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new + * API name, createL2capChannel. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { + Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel"); + return createL2capChannel(psm); + } + /** * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can * be used to start a secure outgoing connection to the remote device with the same dynamic - * protocol/service multiplexer (PSM) value. - *

        This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)} - * for peer-peer Bluetooth applications. + * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. + *

        This is designed to be used with {@link + * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing connection. *

        Application using this API is responsible for obtaining PSM value from remote device. *

        The communication channel may not have an authenticated link key, i.e. it may be subject - * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and + * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and * authenticated communication channel is possible. * - * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions - * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { + public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { - Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled"); + Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled"); throw new IOException(); } - if (transport != BluetoothDevice.TRANSPORT_LE) { - throw new IllegalArgumentException("Unsupported transport: " + transport); - } if (DBG) { - Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm); + Log.d(TAG, "createInsecureL2capChannel: psm=" + psm); } return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm, null); } + + /** + * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new + * API name, createInsecureL2capChannel. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { + Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); + return createInsecureL2capChannel(psm); + } } diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index ba4b5a56679..5fc344a14f9 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -203,12 +203,11 @@ public final class BluetoothServerSocket implements Closeable { /** * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link - * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this + * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link + * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this * method is called on non-L2CAP server sockets. * * @return the assigned PSM or LE_PSM value depending on transport - * @hide */ public int getPsm() { return mChannel; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 780f896139f..3a1e2f58c99 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -667,6 +667,10 @@ public final class BluetoothSocket implements Closeable { * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} */ public int getConnectionType() { + if (mType == TYPE_L2CAP_LE) { + // Treat the LE CoC to be the same type as L2CAP. + return TYPE_L2CAP; + } return mType; } -- GitLab From 7e50b2e0ee2efa6ca61a9f0116ccc0839f40e4f8 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Fri, 14 Sep 2018 12:35:36 +0100 Subject: [PATCH 0971/1408] Move some members to the "Q blacklist". Based on some analysis, these fields/methods are likely false positives. Set maxTargetSdk=P so that any apps using them are required to migrate off them in future. See the bug for more details. Exempted-From-Owner-Approval: Automatic changes to the codebase affecting only @UnsupportedAppUsage annotations, themselves added without requiring owners approval earlier. Bug: 115609023 Test: m Change-Id: I719b5c94e5b1f4fa562dd5d655953422958ad37e --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- framework/java/android/bluetooth/BluetoothGatt.java | 3 ++- framework/java/android/bluetooth/BluetoothProfile.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d21f76d435e..466b9cee10a 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -571,7 +572,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 78248efdd04..29d5a1c583a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -52,7 +53,7 @@ public final class BluetoothGatt implements BluetoothProfile { private BluetoothDevice mDevice; @UnsupportedAppUsage private boolean mAutoConnect; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 9777b5cc6cd..3c3a01b191e 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -21,6 +21,7 @@ import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import java.util.List; @@ -86,7 +87,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int PAN = 5; /** -- GitLab From a06a03302abf47167c40202414750d9c9ecfd80b Mon Sep 17 00:00:00 2001 From: Vasu Nori Date: Fri, 17 Aug 2018 17:25:28 -0700 Subject: [PATCH 0972/1408] Add hidden API to return "Uploading" bit value from SDP record's MapSupportedFeatures. Bug: 111614861 Test: tested w/ KitchenSink App Change-Id: I43895183d7b315f57257e1d2045f17dedcb0cfcd --- .../android/bluetooth/BluetoothMapClient.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 183be5f38bd..559a59b68b4 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"); -- GitLab From aaecb58145708f7fc434b105df8f75bf4031758f Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 19 Jun 2018 08:48:10 -0700 Subject: [PATCH 0973/1408] Add Feature Flag for Hearing Aid Profile Using the Settings App-Developer Options-Feature Flag, allow the user to enable or disable the Hearing Aid Profile. Bug: 116317072 Bug: 116044083 Test: Manual testing using Settings App Change-Id: I58a9d339941e235242c443c85b6f4194b5a296c9 Merged-In: I58a9d339941e235242c443c85b6f4194b5a296c9 (cherry picked from commit dea97b7e14979a37cf247b9204a7681b41a1f819) --- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aa426d3cd31..78b738500a9 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -53,12 +53,16 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; import android.util.Slog; import android.util.StatsLog; @@ -386,6 +390,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); + // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils + boolean isHearingAidEnabled; + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); + if (!TextUtils.isEmpty(value)) { + isHearingAidEnabled = Boolean.parseBoolean(value); + Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); + FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); + } + IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); -- GitLab From efb9173a90b1c90aeaad2ed760e09d4a8efaf384 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 19 Jun 2018 08:48:10 -0700 Subject: [PATCH 0974/1408] Add Feature Flag for Hearing Aid Profile Using the Settings App-Developer Options-Feature Flag, allow the user to enable or disable the Hearing Aid Profile. Bug: 116317072 Bug: 116044083 Test: Manual testing using Settings App Merged-In: I58a9d339941e235242c443c85b6f4194b5a296c9 (cherry picked from commit dea97b7e14979a37cf247b9204a7681b41a1f819) Change-Id: I7d0b6759e7acba3fb2a4d1afdba35d5a95387777 --- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 99e0056254f..49de4b15777 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -53,12 +53,16 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; import android.util.Slog; import android.util.StatsLog; @@ -386,6 +390,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); + // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils + boolean isHearingAidEnabled; + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); + if (!TextUtils.isEmpty(value)) { + isHearingAidEnabled = Boolean.parseBoolean(value); + Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); + FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); + } + IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); -- GitLab From 0b6e7a06a02d968ec58804caeb9998e9a10cc4d6 Mon Sep 17 00:00:00 2001 From: Andrew Solovay Date: Tue, 2 Oct 2018 14:14:42 -0700 Subject: [PATCH 0975/1408] cherry-pick from pi-dev docs: Replacing {#link with {@link Several java files had the typo {#link (for cross-references to other Javadocs) instead of the proper {@link format. This was confusing the new doc publish tool (Mivi) since that's the format used for {# Django comments #}. Fixed a couple of links that had other errors (which prevented building once the {# -> {@ was done) and other typos. Replaced throughout the frameworks/base project; I'll need a separate CL for the AndroidX fixes. (Other files were not in the public Javadocs.) Bug: 111925950 Test: make ds-docs Change-Id: Ia06e1fffd814671289a1caebd5962aedc18a28d7 Original Change-Id: Ia06e1fffd814671289a1caebd5962aedc18a28d7 Exempt-From-Owner-Approval: Docs-only change --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++---- .../java/android/bluetooth/BluetoothServerSocket.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 654bfaf293b..8e6a3856d51 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2973,7 +2973,7 @@ public final class BluetoothAdapter { * socket will be encrypted. *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. - *

        The system will assign a dynamic PSM value. This PSM value can be read from the {#link + *

        The system will assign a dynamic PSM value. This PSM value can be read from the {@link * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is * closed, Bluetooth is turned off, or the application exits unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is @@ -3031,7 +3031,7 @@ public final class BluetoothAdapter { *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. *

        The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value - * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released + * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released * when this server socket is closed, Bluetooth is turned off, or the application exits * unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 73e98cd99f8..30d5fbc7fe9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1596,7 +1596,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocket}. + * In such a case, use {@link createInsecureRfcommSocket}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing @@ -1631,7 +1631,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocket}. + * In such a case, use {@link createInsecureRfcommSocket}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing @@ -1688,7 +1688,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}. + * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Hint: If you are connecting to a Bluetooth serial board then try @@ -1972,7 +1972,7 @@ public final class BluetoothDevice implements Parcelable { * encrypted. *

        Use this socket if an authenticated socket link is possible. Authentication refers * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, + * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int, * int)}. * * @param psm dynamic PSM value from remote device diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 5fc344a14f9..758c68db1c5 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -203,7 +203,7 @@ public final class BluetoothServerSocket implements Closeable { /** * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link + * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this * method is called on non-L2CAP server sockets. * -- GitLab From ee9e4f6b77d8b9ecd011177e1e6cbd62e0ff2d77 Mon Sep 17 00:00:00 2001 From: Andrew Solovay Date: Tue, 2 Oct 2018 14:14:42 -0700 Subject: [PATCH 0976/1408] docs: Replacing {#link with {@link Several java files had the typo {#link (for cross-references to other Javadocs) instead of the proper {@link format. This was confusing the new doc publish tool (Mivi) since that's the format used for {# Django comments #}. Fixed a couple of links that had other errors (which prevented building once the {# -> {@ was done) and other typos. Replaced throughout the frameworks/base project; I'll need a separate CL for the AndroidX fixes. Staged to: go/dac-stage/reference/android/app/Instrumentation.html go/dac-stage/reference/android/bluetooth/BluetoothAdapter.html go/dac-stage/reference/android/bluetooth/BluetoothDevice.html go/dac-stage/reference/android/bluetooth/BluetoothServerSocket.html go/dac-stage/reference/android/inputmethodservice/InputMethodService.html go/dac-stage/reference/android/view/KeyCharacterMap.html go/dac-stage/reference/android/view/KeyEvent.html go/dac-stage/reference/android/media/AudioManager.html go/dac-stage/reference/android/net/wifi/WifiConfiguration.html (Other files were not in the public Javadocs.) Bug: 111925950 Test: make ds-docs Exempt-From-Owner-Approval: Docs-only change Change-Id: Ia06e1fffd814671289a1caebd5962aedc18a28d7 Merged-In: Ia06e1fffd814671289a1caebd5962aedc18a28d7 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++---- .../java/android/bluetooth/BluetoothServerSocket.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 1b6b5a01ec7..44051081b59 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2797,7 +2797,7 @@ public final class BluetoothAdapter { * socket will be encrypted. *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. - *

        The system will assign a dynamic PSM value. This PSM value can be read from the {#link + *

        The system will assign a dynamic PSM value. This PSM value can be read from the {@link * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is * closed, Bluetooth is turned off, or the application exits unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is @@ -2847,7 +2847,7 @@ public final class BluetoothAdapter { *

        Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. *

        The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value - * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released + * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released * when this server socket is closed, Bluetooth is turned off, or the application exits * unexpectedly. *

        The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7a6b72e980f..f4b7d7c700c 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1567,7 +1567,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocket}. + * In such a case, use {@link createInsecureRfcommSocket}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing @@ -1601,7 +1601,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocket}. + * In such a case, use {@link createInsecureRfcommSocket}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Use {@link BluetoothSocket#connect} to initiate the outgoing @@ -1658,7 +1658,7 @@ public final class BluetoothDevice implements Parcelable { * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. - * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}. + * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

        Hint: If you are connecting to a Bluetooth serial board then try @@ -1938,7 +1938,7 @@ public final class BluetoothDevice implements Parcelable { * encrypted. *

        Use this socket if an authenticated socket link is possible. Authentication refers * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, + * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int, * int)}. * * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index ebb7f187aea..ca29ef37a22 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -201,7 +201,7 @@ public final class BluetoothServerSocket implements Closeable { /** * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link + * {@link BluetoothAdapter.listenUsingL2capCoc(int)} or {@link * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this * method is called on non-L2CAP server sockets. * -- GitLab From 985ec7982448413a314a129cd08ef0f13e81641d Mon Sep 17 00:00:00 2001 From: kopriva Date: Tue, 9 Oct 2018 13:42:28 -0700 Subject: [PATCH 0977/1408] docs: fixing errors found with lint checker through /bluetooth directory amending through /content directory Test: make ds-docs Bug: 117494359 Change-Id: I751e15d60f0b9cc441998b27560050bf62994fef Exempt-From-Owner-Approval: Docs-only change --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- framework/java/android/bluetooth/BluetoothGattServer.java | 4 ++-- .../java/android/bluetooth/le/AdvertisingSetCallback.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f4b7d7c700c..3cf80746652 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -640,7 +640,7 @@ public final class BluetoothDevice implements Parcelable { public static final int ACCESS_REJECTED = 2; /** - * No preferrence of physical transport for GATT connections to remote dual-mode devices + * No preference of physical transport for GATT connections to remote dual-mode devices */ public static final int TRANSPORT_AUTO = 0; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 457119dc5bf..77b65f0d1b8 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1281,7 +1281,7 @@ public final class BluetoothGatt implements BluetoothProfile { *

        After all characteristics have been queued up and verified, * {@link #executeReliableWrite} will execute all writes. If a characteristic * was not written correctly, calling {@link #abortReliableWrite} will - * cancel the current transaction without commiting any values on the + * cancel the current transaction without committing any values on the * remote device. * *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index ef1b0bd7188..13b1b4f93cf 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -522,7 +522,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be * invoked when the connection state changes as a result of this function. * - *

        The autoConnect paramter determines whether to actively connect to + *

        The autoConnect parameter determines whether to actively connect to * the remote device, or rather passively scan and finalize the connection * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and @@ -695,7 +695,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Add a service to the list of services to be hosted. * - *

        Once a service has been addded to the the list, the service and its + *

        Once a service has been addded to the list, the service and its * included characteristics will be provided by the local device. * *

        If the local device has already exposed services when this function diff --git a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java index 58a3696fc09..51324fdb01f 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetCallback.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetCallback.java @@ -56,7 +56,7 @@ public abstract class AdvertisingSetCallback { /** * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet - * contains the started set and it is advertising. If error occured, advertisingSet is + * contains the started set and it is advertising. If error occurred, advertisingSet is * null, and status will be set to proper error code. * * @param advertisingSet The advertising set that was started or null if error. -- GitLab From e88bc0aec788c7367919ea865ac75b68954442e3 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 10 Oct 2018 17:42:33 -0700 Subject: [PATCH 0978/1408] Remove BluetoothDevice.ACTION_DISAPPEARED This intent was never sent by the Bluetooth stack Fixes: 112115770 Test: make Change-Id: Iaf71aeafa21f44ec9b59c1b766c936ab7dde39f1 --- .../java/android/bluetooth/BluetoothDevice.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 73e98cd99f8..d9e6fa20382 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -106,20 +106,6 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND"; - /** - * Broadcast Action: Remote device disappeared. - *

        Sent when a remote device that was found in the last discovery is not - * found in the current discovery. - *

        Always contains the extra field {@link #EXTRA_DEVICE}. - *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage - public static final String ACTION_DISAPPEARED = - "android.bluetooth.device.action.DISAPPEARED"; - /** * Broadcast Action: Bluetooth class of a remote device has changed. *

        Always contains the extra fields {@link #EXTRA_DEVICE} and {@link -- GitLab From 9f56957c12f68829f864a602771adcfa0c83b328 Mon Sep 17 00:00:00 2001 From: Nan Zhang Date: Thu, 11 Oct 2018 17:54:36 -0700 Subject: [PATCH 0979/1408] Fix the incorrect javadoc links A lot of unresolved link errors showing up after go/ag/5172152. Test: m -j docs with -lerror enabled Bug: b/116163454 Change-Id: I74d1f75e0f00015410a63e13103c28a9c84b4fe0 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 +--- framework/java/android/bluetooth/BluetoothServerSocket.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index fc0c1dc4387..b8e7e3044ca 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1971,9 +1971,7 @@ public final class BluetoothDevice implements Parcelable { *

        The remote device will be authenticated and communication on this socket will be * encrypted. *

        Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int, - * int)}. + * to the authentication of the link key to prevent man-in-the-middle type of attacks. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 758c68db1c5..4e886250b4f 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -203,8 +203,8 @@ public final class BluetoothServerSocket implements Closeable { /** * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link - * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this + * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link + * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this * method is called on non-L2CAP server sockets. * * @return the assigned PSM or LE_PSM value depending on transport -- GitLab From 2a2cd5db7bc3924c92fe75f53f8b848b1a6a3a86 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 19 Jun 2018 08:48:10 -0700 Subject: [PATCH 0980/1408] Add Feature Flag for Hearing Aid Profile Using the Settings App-Developer Options-Feature Flag, allow the user to enable or disable the Hearing Aid Profile. Bug: 116317072 Bug: 116044083 Test: Manual testing using Settings App Change-Id: I58a9d339941e235242c443c85b6f4194b5a296c9 Merged-In: I58a9d339941e235242c443c85b6f4194b5a296c9 (cherry picked from commit dea97b7e14979a37cf247b9204a7681b41a1f819) --- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 99e0056254f..49de4b15777 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -53,12 +53,16 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; import android.util.Slog; import android.util.StatsLog; @@ -386,6 +390,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); + // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils + boolean isHearingAidEnabled; + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); + if (!TextUtils.isEmpty(value)) { + isHearingAidEnabled = Boolean.parseBoolean(value); + Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); + FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); + } + IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); -- GitLab From 86999c2001953beead860b15a898adad0092ebf9 Mon Sep 17 00:00:00 2001 From: jovanak Date: Mon, 15 Oct 2018 17:50:19 -0700 Subject: [PATCH 0981/1408] Makes bluetooth profile services bind to current (foreground) user. If profile services are created from a singleton process (like SystemUI), they need to be re-bind to a new foreground user when we switch to a secondary user. This is achieved by binding to UserHandle.CURRENT_OR_SELF. If the process doesn't have adequate permissions to bind to current, it will bind to self (which is the same as the previous behavior). Change-Id: Ib1134a1a62887d6f5b2c97301dccd3223ade9ed2 Fixes: 117517812 Test: ran the existing core bluetooth tests; manual testing on a mojave, verifying that the service correctly rebinds from sys ui after user switch. --- framework/java/android/bluetooth/BluetoothA2dpSink.java | 3 ++- framework/java/android/bluetooth/BluetoothAvrcpController.java | 3 ++- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 3 ++- framework/java/android/bluetooth/BluetoothHealth.java | 3 ++- framework/java/android/bluetooth/BluetoothHearingAid.java | 3 ++- framework/java/android/bluetooth/BluetoothHidDevice.java | 3 ++- framework/java/android/bluetooth/BluetoothHidHost.java | 3 ++- framework/java/android/bluetooth/BluetoothMap.java | 3 ++- framework/java/android/bluetooth/BluetoothMapClient.java | 3 ++- framework/java/android/bluetooth/BluetoothPan.java | 3 ++- framework/java/android/bluetooth/BluetoothPbap.java | 3 ++- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 ++- framework/java/android/bluetooth/BluetoothSap.java | 3 ++- 13 files changed, 26 insertions(+), 13 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index fda2f892753..cb996f3381b 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -183,7 +184,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index e7c8944788f..c447868d6f0 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -138,7 +139,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index ec18d42698c..549c1faddd9 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -25,6 +25,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -428,7 +429,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index b967fb20f02..22d41d9c896 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -24,6 +24,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -491,7 +492,7 @@ public final class BluetoothHealth implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 606f00a8239..47c4ee6139d 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -29,6 +29,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -205,7 +206,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - android.os.Process.myUserHandle())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); return; } diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 3bc8544ebf8..e44f36e90c7 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -454,7 +455,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 0ca39f169a7..58a25221552 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -25,6 +25,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -279,7 +280,7 @@ public final class BluetoothHidHost implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 98c23c600f1..fc5f830a894 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -110,7 +111,7 @@ public final class BluetoothMap implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 559a59b68b4..1c82e1984b6 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -25,6 +25,7 @@ import android.content.ServiceConnection; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -128,7 +129,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, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 58be7329602..8923d734c84 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -26,6 +26,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -150,7 +151,7 @@ public final class BluetoothPan implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index ae264e19bb7..a601df02d03 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -164,7 +165,7 @@ public class BluetoothPbap implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 1446adc8b9c..cbc96c07333 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -116,7 +117,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 1b732062f61..ebf6bed5447 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -24,6 +24,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -148,7 +149,7 @@ public final class BluetoothSap implements BluetoothProfile { ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { + UserHandle.CURRENT_OR_SELF)) { Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent); return false; } -- GitLab From f70e37d70cf1b49128ba838b6d0d21d4918ca601 Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 17 Oct 2018 11:09:25 -0700 Subject: [PATCH 0982/1408] Synchronize on a static final object to lock mDeviceBusy Synchronizing on a field doesn't lock the object in a predictable way. Bug: 63389270 Test: Compile Change-Id: Idd3111bc94d32bc4bb320a5c25da37fe68276337 --- .../java/android/bluetooth/BluetoothGatt.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 78248efdd04..d8a7834eb75 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -56,6 +56,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); + private final Object mDeviceBusyLock = new Object(); @UnsupportedAppUsage private Boolean mDeviceBusy = false; @UnsupportedAppUsage @@ -281,7 +282,7 @@ public final class BluetoothGatt implements BluetoothProfile { } } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } } @@ -356,7 +357,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -412,7 +413,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -495,7 +496,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -546,7 +547,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -595,7 +596,7 @@ public final class BluetoothGatt implements BluetoothProfile { return; } - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { mDeviceBusy = false; } @@ -1097,7 +1098,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1131,7 +1132,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); if (mService == null || mClientIf == 0) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1177,7 +1178,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1220,7 +1221,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1261,7 +1262,7 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } @@ -1329,7 +1330,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; - synchronized (mDeviceBusy) { + synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; mDeviceBusy = true; } -- GitLab From 484573f039f03c0266d6bff44bb604378a8e1cda Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Fri, 14 Sep 2018 12:35:36 +0100 Subject: [PATCH 0983/1408] Move some members to the "Q blacklist". Based on some analysis, these fields/methods are likely false positives. Set maxTargetSdk=P so that any apps using them are required to migrate off them in future. See the bug for more details. Exempted-From-Owner-Approval: Automatic changes to the codebase affecting only @UnsupportedAppUsage annotations, themselves added without requiring owners approval earlier. Bug: 115609023 Test: m Merged-In: I719b5c94e5b1f4fa562dd5d655953422958ad37e Change-Id: I719b5c94e5b1f4fa562dd5d655953422958ad37e (cherry picked from commit 7e50b2e0ee2efa6ca61a9f0116ccc0839f40e4f8) --- framework/java/android/bluetooth/BluetoothA2dp.java | 3 ++- framework/java/android/bluetooth/BluetoothGatt.java | 3 ++- framework/java/android/bluetooth/BluetoothProfile.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d21f76d435e..466b9cee10a 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -571,7 +572,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index d8a7834eb75..b248b896cdb 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; import android.os.RemoteException; @@ -52,7 +53,7 @@ public final class BluetoothGatt implements BluetoothProfile { private BluetoothDevice mDevice; @UnsupportedAppUsage private boolean mAutoConnect; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mAuthRetryState; private int mConnState; private final Object mStateLock = new Object(); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 9777b5cc6cd..3c3a01b191e 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -21,6 +21,7 @@ import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import java.util.List; @@ -86,7 +87,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int PAN = 5; /** -- GitLab From ed8d339e688e74ed19600bf626dddb26b9211950 Mon Sep 17 00:00:00 2001 From: Benson Li Date: Tue, 17 Jul 2018 18:19:59 +0800 Subject: [PATCH 0984/1408] HFP: Send caller ID in +CLIP command (1/4) * Modify BluetoothHeadset so that phoneStateChanged method can provide caller display name Bug: 111378831 Test: runtest bluetooth, runtest -x BluetoothPhoneServiceTest.java Change-Id: Ibc3611a50ec9f103fd235dde606ecbd10657e014 --- framework/java/android/bluetooth/BluetoothHeadset.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 636b1b9b13c..8d9d340ee68 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -973,11 +973,11 @@ public final class BluetoothHeadset implements BluetoothProfile { */ @UnsupportedAppUsage public void phoneStateChanged(int numActive, int numHeld, int callState, String number, - int type) { + int type, String name) { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.phoneStateChanged(numActive, numHeld, callState, number, type); + service.phoneStateChanged(numActive, numHeld, callState, number, type, name); } catch (RemoteException e) { Log.e(TAG, e.toString()); } -- GitLab From 8d98726f7bcfa985b42772d330e208bcc0424040 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 12 Nov 2018 14:32:03 -0800 Subject: [PATCH 0985/1408] BluetoothDevice.ACTION_UUID requires BLUETOOTH_ADMIN Fix the doc - intent BluetoothDevice.ACTION_UUID requries android.Manifest.permission#BLUETOOTH_ADMIN instead of #BLUETOOTH to receive. This is already enforced by Bluetooth service. Bug: 113160320 Test: manual Change-Id: I977d58e6d534f512da80bd6b13bebb515765626f --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d9e6fa20382..b2b02850e2e 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -347,7 +347,7 @@ public final class BluetoothDevice implements Parcelable { * device are requested to be fetched using Service Discovery Protocol *

        Always contains the extra field {@link #EXTRA_DEVICE} *

        Always contains the extra field {@link #EXTRA_UUID} - *

        Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + *

        Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = -- GitLab From 61d17c8019f0a506bde114f87761475ead26e319 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Wed, 14 Nov 2018 14:40:14 -0800 Subject: [PATCH 0986/1408] BluetoothManager: Set DBG to false Fixes: 71491860 Test: Pair and connect with a phone Change-Id: I3ead8154765267105c74cd800f571933bbe82e07 --- framework/java/android/bluetooth/BluetoothManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 11f8ab7551c..e3672a7e064 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -52,8 +52,7 @@ import java.util.List; @RequiresFeature(PackageManager.FEATURE_BLUETOOTH) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; - private static final boolean DBG = true; - private static final boolean VDBG = true; + private static final boolean DBG = false; private final BluetoothAdapter mAdapter; -- GitLab From 3fc40a31857bc62cb82b34f809ad8104b881e3e6 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 15 Nov 2018 10:22:07 -0800 Subject: [PATCH 0987/1408] Enable the Debug Logs for Hearing Aids Profile Bug: 119617521 Test: Compile Change-Id: I40de1fcb3ca9e0ee66b4650a161f3735722df7ed --- framework/java/android/bluetooth/BluetoothHearingAid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 606f00a8239..2bf7daddcb2 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -50,7 +50,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public final class BluetoothHearingAid implements BluetoothProfile { private static final String TAG = "BluetoothHearingAid"; - private static final boolean DBG = false; + private static final boolean DBG = true; private static final boolean VDBG = false; /** -- GitLab From bb73549613486b3818cd281bab7ac2309b99084a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 28 Nov 2018 23:36:53 +0100 Subject: [PATCH 0988/1408] Bluetooth: Fix NPE when accesing Manufacturer Data of invalid ScanRecord Bug: 118805852 Test: compilation Change-Id: I537bec61cd3e7df700cc972683458f977ff370b8 --- framework/java/android/bluetooth/le/ScanRecord.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 7988008f03c..2174255a361 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -116,6 +116,9 @@ public final class ScanRecord { */ @Nullable public byte[] getManufacturerSpecificData(int manufacturerId) { + if (mManufacturerSpecificData == null) { + return null; + } return mManufacturerSpecificData.get(manufacturerId); } -- GitLab From 097358bfa31b1c83e88403fff4e91f644346f82a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 29 Nov 2018 18:54:21 +0100 Subject: [PATCH 0989/1408] Bluetooth: Check descriptors size in BluetoothHidDeviceAppSdpSettings Bug: 119819889 Test: compilation Change-Id: If51d0e2af74d99758f79a603d40cc2f5c84e4dde --- .../bluetooth/BluetoothHidDeviceAppSdpSettings.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 237082e4cb6..2f0b44f76ff 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.util.EventLog; /** @@ -30,6 +31,8 @@ import android.os.Parcelable; */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + private static final int MAX_DESCRIPTOR_SIZE = 2048; + private final String mName; private final String mDescription; private final String mProvider; @@ -55,6 +58,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { mDescription = description; mProvider = provider; mSubclass = subclass; + + if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) { + EventLog.writeEvent(0x534e4554, "119819889", -1, ""); + throw new IllegalArgumentException("descriptors must be not null and shorter than " + + MAX_DESCRIPTOR_SIZE); + } mDescriptors = descriptors.clone(); } -- GitLab From 7d9f833aa2878e0a3e31dcbeedc86cdfd615e4dd Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 29 Nov 2018 18:54:21 +0100 Subject: [PATCH 0990/1408] Bluetooth: Check descriptors size in BluetoothHidDeviceAppSdpSettings Bug: 119819889 Test: compilation Change-Id: If51d0e2af74d99758f79a603d40cc2f5c84e4dde Merged-In: If51d0e2af74d99758f79a603d40cc2f5c84e4dde --- .../bluetooth/BluetoothHidDeviceAppSdpSettings.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index f9a22458195..5061fa0b3b6 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -18,12 +18,15 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.util.EventLog; import java.util.Random; /** @hide */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + private static final int MAX_DESCRIPTOR_SIZE = 2048; + final public String name; final public String description; final public String provider; @@ -36,6 +39,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { this.description = description; this.provider = provider; this.subclass = subclass; + + if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) { + EventLog.writeEvent(0x534e4554, "119819889", -1, ""); + throw new IllegalArgumentException("descriptors must be not null and shorter than " + + MAX_DESCRIPTOR_SIZE); + } this.descriptors = descriptors.clone(); } -- GitLab From e8a14bfa2dde7905c99ce682109d5254bef4029d Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 29 Nov 2018 18:54:21 +0100 Subject: [PATCH 0991/1408] Bluetooth: Check descriptors size in BluetoothHidDeviceAppSdpSettings Bug: 119819889 Test: compilation Change-Id: If51d0e2af74d99758f79a603d40cc2f5c84e4dde (cherry picked from commit 097358bfa31b1c83e88403fff4e91f644346f82a) --- .../bluetooth/BluetoothHidDeviceAppSdpSettings.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 237082e4cb6..2f0b44f76ff 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import android.util.EventLog; /** @@ -30,6 +31,8 @@ import android.os.Parcelable; */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + private static final int MAX_DESCRIPTOR_SIZE = 2048; + private final String mName; private final String mDescription; private final String mProvider; @@ -55,6 +58,12 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { mDescription = description; mProvider = provider; mSubclass = subclass; + + if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) { + EventLog.writeEvent(0x534e4554, "119819889", -1, ""); + throw new IllegalArgumentException("descriptors must be not null and shorter than " + + MAX_DESCRIPTOR_SIZE); + } mDescriptors = descriptors.clone(); } -- GitLab From 9ef032b089f5340abc204ee26091b459e636b810 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Wed, 12 Dec 2018 17:11:25 -0800 Subject: [PATCH 0992/1408] Pass package name as part of startDiscovery Test: manual Bug: 118347252 Change-Id: Icbc2e7e756b16ffd181924b586a0292c2bf32ec5 --- .../android/bluetooth/BluetoothAdapter.java | 20 ++++++++++++++++++- .../android/bluetooth/BluetoothManager.java | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 654bfaf293b..10c8b15a7a7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -643,6 +643,7 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; @UnsupportedAppUsage private IBluetooth mService; + private Context mContext; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); private final Object mLock = new Object(); @@ -1540,6 +1541,23 @@ public final class BluetoothAdapter { return -1; } + /** + * Set the context for this BluetoothAdapter (only called from BluetoothManager) + * @hide + */ + public void setContext(Context context) { + mContext = context; + } + + private String getOpPackageName() { + // Workaround for legacy API for getting a BluetoothAdapter not + // passing a context + if (mContext != null) { + return mContext.getOpPackageName(); + } + return ActivityThread.currentOpPackageName(); + } + /** * Start the remote device discovery process. *

        The discovery process usually involves an inquiry scan of about 12 @@ -1577,7 +1595,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(); + return mService.startDiscovery(getOpPackageName()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index e3672a7e064..e08d405324e 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -67,6 +67,7 @@ public final class BluetoothManager { } // Legacy api - getDefaultAdapter does not take in the context mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter.setContext(context); } /** -- GitLab From b7de86eccfb71f537013500dbb293c86e7e56852 Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Mon, 17 Dec 2018 21:31:40 +0800 Subject: [PATCH 0993/1408] BluetoothManagerService: catch NoSuchElementException for unlinkToDeath linkToDeath fail will cause NoSuchElementException when following unlinkToDeath, add try catch to handle it. Bug: 121104808 Test: build pass Change-Id: I2f3337ad417567ea3b4dea3eb004c60f96527c41 --- .../android/server/bluetooth/BluetoothManagerService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 78b738500a9..8f5b228e720 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -76,6 +76,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -1259,7 +1260,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mService == null) { return; } - mService.unlinkToDeath(this, 0); + try { + mService.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.e(TAG, "error unlinking to death", e); + } mService = null; mClassName = null; -- GitLab From 4554d5a132649aad6f14e224d3c9e29e73f23985 Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Tue, 18 Dec 2018 10:40:25 +0800 Subject: [PATCH 0994/1408] BluetoothManagerService: catch NoSuchElementException for unlinkToDeath linkToDeath fail will cause NoSuchElementException when following unlinkToDeath, add try catch to handle it. Bug: 121104808 Test: build pass Change-Id: Idc4959331841946e2fc099bdc0fab7c753caf369 --- .../android/server/bluetooth/BluetoothManagerService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 49de4b15777..555e89947a6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -76,6 +76,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -1279,7 +1280,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mService == null) { return; } - mService.unlinkToDeath(this, 0); + try { + mService.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.e(TAG, "error unlinking to death", e); + } mService = null; mClassName = null; -- GitLab From 945c249742252f1da5888adef3b1799eb5943f7d Mon Sep 17 00:00:00 2001 From: bohu Date: Tue, 18 Dec 2018 12:18:45 -0800 Subject: [PATCH 0995/1408] bluetooth: fix crash on accessing null adapter By checking bluetooth adaptor before accessing it, because it could be null on devices that do not support bluetooth, such as android emulator. BUG: 121129248 Test: lunch sdk_gphone_x86-userdebug make -j emulator launch chrome and it should not crash Change-Id: Ia75b5ee1efa3a8195056dada079239931eb9d901 --- framework/java/android/bluetooth/BluetoothManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index e08d405324e..adedff3e938 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -67,7 +67,9 @@ public final class BluetoothManager { } // Legacy api - getDefaultAdapter does not take in the context mAdapter = BluetoothAdapter.getDefaultAdapter(); - mAdapter.setContext(context); + if (mAdapter != null) { + mAdapter.setContext(context); + } } /** -- GitLab From af861850bfbf6fc26b76f7719acd39315e39edc9 Mon Sep 17 00:00:00 2001 From: jonerlin Date: Thu, 9 Aug 2018 16:39:43 +0800 Subject: [PATCH 0996/1408] AdapterService: Only bind HeadsetService in ON state. * there will be an endless loop to call doBind and cause many connectionRecords of HeadsetService while binding HeadsetService in BLE_ON state. * allow to bind HeadsetService only when BT state is ON Bug: 111730478 Bug: 121024323 Test: Keep device in BLE_ON state -> kill com.android.phone process -> As time goes on, adb shell dumpsys activity services to check the connectionRecords of HeadsetService will not become more and more Change-Id: I5f66d015b8aa3c94c7e887ef033213af74235b4a --- .../bluetooth/BluetoothManagerService.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 78b738500a9..49de4b15777 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1174,6 +1174,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean bindService() { + int state = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + state = mBluetooth.getState(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return false; + } finally { + mBluetoothLock.readLock().unlock(); + } + + if (!mEnable || state != BluetoothAdapter.STATE_ON) { + if (DBG) { + Slog.d(TAG, "Unable to bindService while Bluetooth is disabled"); + } + return false; + } + if (mIntent != null && mService == null && doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) { Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); -- GitLab From 14c2b631b9b7a974f23c1492a19b2a68e32446db Mon Sep 17 00:00:00 2001 From: Rene Mayrhofer Date: Wed, 28 Nov 2018 11:32:40 -0800 Subject: [PATCH 0997/1408] Enable airplane mode when booting in safe mode Make it possible to boot in airplane mode when it was not turned on before shutdown/reboot, either for situations where this is required by regulation or to work around problems in normal boot. When safe mode is engaged (Vol-Down), disable in two stages: 1. Set Settings.Global.AIRPLANE_MODE_ON as early as possible for subsystems to react to it during their initialization. 2. Trigger the public ConnectiviyService API to enable airplane mode. This has to be done during later stages in initialization because it requires sending broadcasts, so some RF leakage may still happen before airplane mode is enabled (time window of a few seconds). Bluetooth is not currently disabled and needs to be fixed by another change. WiFi and NFC are turned off alongside cellular modems. Test: manual Bug: 120145930 Change-Id: I1d61990a9d6f7cd046d28b3e49ecf402dd485063 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 7bbc543a6aa..a33338164ca 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -279,7 +279,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( - st)); + st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off -- GitLab From 7344059322fee5d4a13af2f53f276880aed246ac Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 20 Dec 2018 13:53:36 +0000 Subject: [PATCH 0998/1408] Limit access to suspected false positives. Members modified herein are suspected to be false positives: i.e. things that were added to the greylist in P, but subsequent data analysis suggests that they are not, in fact, used after all. Add a maxTargetSdk=P to these APIs. This is lower-risk that simply removing these things from the greylist, as none of out data sources are perfect nor complete. For APIs that are not supported yet by annotations, move them to hiddenapi-greylist-max-p.txt instead which has the same effect. Exempted-From-Owner-Approval: Automatic changes to the codebase affecting only @UnsupportedAppUsage annotations, themselves added without requiring owners approval earlier. Bug: 115609023 Test: m Change-Id: I020a9c09672ebcae64c5357abc4993e07e744687 --- framework/java/android/bluetooth/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 3 ++- framework/java/android/bluetooth/BluetoothUuid.java | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 466b9cee10a..171c2f5b1a0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -878,7 +878,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static String stateToString(int state) { switch (state) { case STATE_DISCONNECTED: diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 3a78cbdd4d0..1edbacbae4f 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -65,7 +66,7 @@ public final class BluetoothClass implements Parcelable { private final int mClass; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public BluetoothClass(int classInt) { mClass = classInt; } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index fdbfec00e62..bc3c9a9ebf4 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -38,20 +39,20 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid AudioSink = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AudioSource = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid AdvAudioDist = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid Handsfree = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid Handsfree_AG = -- GitLab From 84995ea1de36b4b6e14a103f2932c76da315d2bf Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 20 Dec 2018 15:30:45 +0000 Subject: [PATCH 0999/1408] Limit access to suspected false positives. Members modified herein are suspected to be false positives: i.e. things that were added to the greylist in P, but subsequent data analysis suggests that they are not, in fact, used after all. Add a maxTargetSdk=P to these APIs. This is lower-risk that simply removing these things from the greylist, as none of out data sources are perfect nor complete. For APIs that are not supported yet by annotations, move them to hiddenapi-greylist-max-p.txt instead which has the same effect. Exempted-From-Owner-Approval: Automatic changes to the codebase affecting only @UnsupportedAppUsage annotations, themselves added without requiring owners approval earlier. Bug: 115609023 Test: m Change-Id: Ia937d8c41512e7f1b6e7f67b9104c1878b5cc3a0 Merged-In: I020a9c09672ebcae64c5357abc4993e07e744687 --- framework/java/android/bluetooth/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 3 ++- framework/java/android/bluetooth/BluetoothUuid.java | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 466b9cee10a..171c2f5b1a0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -878,7 +878,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static String stateToString(int state) { switch (state) { case STATE_DISCONNECTED: diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 3a78cbdd4d0..1edbacbae4f 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -65,7 +66,7 @@ public final class BluetoothClass implements Parcelable { private final int mClass; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public BluetoothClass(int classInt) { mClass = classInt; } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index fdbfec00e62..bc3c9a9ebf4 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -38,20 +39,20 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid AudioSink = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid AudioSource = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid AdvAudioDist = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final ParcelUuid Handsfree = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid Handsfree_AG = -- GitLab From 07ffaa447fdd967689901cca38eba386a8d97b23 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 3 Jan 2019 16:23:41 -0800 Subject: [PATCH 1000/1408] Deprecate BluetoothHealth APIs * Mark all BluetoothHealth related APIs as deprecated * Make BluetoothAdapter#getProfileProxy(context, BluetoothProfile.HEALTH) always return false * Remove all logic behind BluetoothHealth APIs and add deprecation error log * Health Device Profile (HDP) and MCAP protocol has been largely replaced by BLE. New applications should use Bluetooth Low Energy instead of legacy Bluetooth Health Device Profile Bug: 111562841 Test: make, unit test, use Bluetooth Change-Id: If99a9d79e9e1b89b75b9b74bd3b1c965247a1892 --- .../android/bluetooth/BluetoothAdapter.java | 27 +- .../android/bluetooth/BluetoothHealth.java | 484 +++++------------- .../BluetoothHealthAppConfiguration.java | 122 ++--- .../bluetooth/BluetoothHealthCallback.java | 18 + .../android/bluetooth/BluetoothProfile.java | 13 +- 5 files changed, 207 insertions(+), 457 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 87b64797f3c..1945b2fd51d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2066,8 +2066,7 @@ public final class BluetoothAdapter { * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter * is connected to any remote device for a specific profile. - * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}. + * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. * *

        Return value can be one of * {@link BluetoothProfile#STATE_DISCONNECTED}, @@ -2441,16 +2440,15 @@ public final class BluetoothAdapter { /** * Get the profile proxy object associated with the profile. * - *

        Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or - * {@link BluetoothProfile#GATT_SERVER}. Clients must implement - * {@link BluetoothProfile.ServiceListener} to get notified of - * the connection status and to get the proxy object. + *

        Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must + * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status + * and to get the proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. - * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link - * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or + * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or * {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ @@ -2479,8 +2477,8 @@ public final class BluetoothAdapter { BluetoothPan pan = new BluetoothPan(context, listener); return true; } else if (profile == BluetoothProfile.HEALTH) { - BluetoothHealth health = new BluetoothHealth(context, listener); - return true; + Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); + return false; } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; @@ -2512,8 +2510,7 @@ public final class BluetoothAdapter { * *

        Clients should call this when they are no longer using * the proxy obtained from {@link #getProfileProxy}. - * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or - * {@link BluetoothProfile#A2DP} + * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP} * * @param profile * @param proxy Profile proxy object @@ -2548,10 +2545,6 @@ public final class BluetoothAdapter { BluetoothPan pan = (BluetoothPan) proxy; pan.close(); break; - case BluetoothProfile.HEALTH: - BluetoothHealth health = (BluetoothHealth) proxy; - health.close(); - break; case BluetoothProfile.GATT: BluetoothGatt gatt = (BluetoothGatt) proxy; gatt.close(); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 22d41d9c896..e2e56fd02ab 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -16,15 +16,7 @@ package android.bluetooth; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Binder; -import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -54,79 +46,59 @@ import java.util.List; *

      • When done, close the health channel by calling {@link #disconnectChannel} * and unregister the application configuration calling * {@link #unregisterAppConfiguration} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps + * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - /** * Health Profile Source Role - the health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int SOURCE_ROLE = 1 << 0; /** * Health Profile Sink Role the device talking to the health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int SINK_ROLE = 1 << 1; /** * Health Profile - Channel Type used - Reliable + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int CHANNEL_TYPE_RELIABLE = 10; /** * Health Profile - Channel Type used - Streaming + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int CHANNEL_TYPE_STREAMING = 11; - /** - * @hide - */ - public static final int CHANNEL_TYPE_ANY = 12; - - /** @hide */ - public static final int HEALTH_OPERATION_SUCCESS = 6000; - /** @hide */ - public static final int HEALTH_OPERATION_ERROR = 6001; - /** @hide */ - public static final int HEALTH_OPERATION_INVALID_ARGS = 6002; - /** @hide */ - public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003; - /** @hide */ - public static final int HEALTH_OPERATION_NOT_FOUND = 6004; - /** @hide */ - public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005; - - 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); - } - } - } - } - }; - /** * Register an application configuration that acts as a Health SINK. @@ -142,53 +114,17 @@ public final class BluetoothHealth implements BluetoothProfile { * @param callback A callback to indicate success or failure of the registration and all * operations done on this application configuration. * @return If true, callback will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean registerSinkAppConfiguration(String name, int dataType, BluetoothHealthCallback callback) { - if (!isEnabled() || name == null) return false; - - if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); - return registerAppConfiguration(name, dataType, SINK_ROLE, - CHANNEL_TYPE_ANY, callback); - } - - /** - * Register an application configuration that acts as a Health SINK or in a Health - * SOURCE role.This is an asynchronous call and so - * the callback is used to notify success or failure if the function returns true. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param name The friendly name associated with the application or configuration. - * @param dataType The dataType of the Source role of Health Profile. - * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link - * #CHANNEL_TYPE_STREAMING} - * @param callback - A callback to indicate success or failure. - * @return If true, callback will be called. - * @hide - */ - public boolean registerAppConfiguration(String name, int dataType, int role, - int channelType, BluetoothHealthCallback callback) { - boolean result = false; - if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; - - if (VDBG) log("registerApplication(" + name + ":" + dataType + ")"); - BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); - BluetoothHealthAppConfiguration config = - new BluetoothHealthAppConfiguration(name, dataType, role, channelType); - - final IBluetoothHealth service = mService; - if (service != null) { - try { - result = service.registerAppConfiguration(config, wrapper); - } 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 result; + Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated"); + return false; } /** @@ -199,22 +135,16 @@ public final class BluetoothHealth implements BluetoothProfile { * * @param config The health app configuration * @return Success or failure. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { - boolean result = false; - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && config != null) { - try { - result = service.unregisterAppConfiguration(config); - } 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 result; + Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated"); + return false; } /** @@ -228,49 +158,16 @@ public final class BluetoothHealth implements BluetoothProfile { * @param config The application configuration which has been registered using {@link * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.connectChannelToSource(device, config); - } 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; - } - - /** - * Connect to a health device which has the {@link #SINK_ROLE}. - * This is an asynchronous call. If this function returns true, the callback - * associated with the application configuration will be called. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using {@link - * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @return If true, the callback associated with the application config will be called. - * @hide - */ - public boolean connectChannelToSink(BluetoothDevice device, - BluetoothHealthAppConfiguration config, int channelType) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.connectChannelToSink(device, config, channelType); - } 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())); - } + Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated"); return false; } @@ -286,20 +183,16 @@ public final class BluetoothHealth implements BluetoothProfile { * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param channelId The channel id associated with the channel * @return If true, the callback associated with the application config will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.disconnectChannel(device, config, channelId); - } 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())); - } + Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated"); return false; } @@ -315,20 +208,16 @@ public final class BluetoothHealth implements BluetoothProfile { * @param device The remote Bluetooth health device * @param config The application configuration * @return null on failure, ParcelFileDescriptor on success. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.getMainChannelFd(device, config); - } 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())); - } + Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated"); return null; } @@ -348,17 +237,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getHealthDeviceConnectionState(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())); - } + Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated"); return STATE_DISCONNECTED; } @@ -378,17 +257,8 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getConnectedDevices() { - final IBluetoothHealth service = mService; - if (service != null && isEnabled()) { - try { - return service.getConnectedHealthDevices(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); + Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated"); + return new ArrayList<>(); } /** @@ -410,163 +280,81 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled()) { - try { - return service.getHealthDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); + Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated"); + return new ArrayList<>(); } - private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub { - private BluetoothHealthCallback mCallback; - - public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) { - mCallback = callback; - } - - @Override - public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - mCallback.onHealthAppConfigurationStatusChange(config, status); - } - - @Override - public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd, int channelId) { - mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd, - channelId); - } - } - - /** Health Channel Connection State - Disconnected */ + /** Health Channel Connection State - Disconnected + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_DISCONNECTED = 0; - /** Health Channel Connection State - Connecting */ + /** Health Channel Connection State - Connecting + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_CONNECTING = 1; - /** Health Channel Connection State - Connected */ + /** Health Channel Connection State - Connected + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_CONNECTED = 2; - /** Health Channel Connection State - Disconnecting */ + /** Health Channel Connection State - Disconnecting + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_DISCONNECTING = 3; - /** Health App Configuration registration success */ + /** Health App Configuration registration success + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; - /** Health App Configuration registration failure */ + /** Health App Configuration registration failure + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; - /** Health App Configuration un-registration success */ + /** Health App Configuration un-registration success + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; - /** Health App Configuration un-registration failure */ - public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; - - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHealth mService; - BluetoothAdapter mAdapter; - - /** - * Create a BluetoothHealth proxy object. + /** Health App Configuration un-registration failure + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ - /*package*/ BluetoothHealth(Context context, ServiceListener l) { - 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(IBluetoothHealth.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent); - return false; - } - return true; - } - - /*package*/ void close() { - if (VDBG) log("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; - } - - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH); - } - } - }; - - private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - log("Bluetooth is Not enabled"); - return false; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private boolean checkAppParam(String name, int role, int channelType, - BluetoothHealthCallback callback) { - if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) - || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING - && channelType != CHANNEL_TYPE_ANY) - || callback == null) { - return false; - } - if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false; - return true; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } + @Deprecated + public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; } diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 7c9db6f7213..9788bbf74e3 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -25,72 +25,14 @@ import android.os.Parcelable; * the {@link BluetoothHealth} class. This class represents an application configuration * that the Bluetooth Health third party application will register to communicate with the * remote Bluetooth health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public final class BluetoothHealthAppConfiguration implements Parcelable { - private final String mName; - private final int mDataType; - private final int mRole; - private final int mChannelType; - - /** - * Constructor to register the SINK role - * - * @param name Friendly name associated with the application configuration - * @param dataType Data Type of the remote Bluetooth Health device - * @hide - */ - BluetoothHealthAppConfiguration(String name, int dataType) { - mName = name; - mDataType = dataType; - mRole = BluetoothHealth.SINK_ROLE; - mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; - } - - /** - * Constructor to register the application configuration. - * - * @param name Friendly name associated with the application configuration - * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} - * @hide - */ - BluetoothHealthAppConfiguration(String name, int dataType, int role, int - channelType) { - mName = name; - mDataType = dataType; - mRole = role; - mChannelType = channelType; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHealthAppConfiguration) { - BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o; - - if (mName == null) return false; - - return mName.equals(config.getName()) && mDataType == config.getDataType() - && mRole == config.getRole() && mChannelType == config.getChannelType(); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (mName != null ? mName.hashCode() : 0); - result = 31 * result + mDataType; - result = 31 * result + mRole; - result = 31 * result + mChannelType; - return result; - } - - @Override - public String toString() { - return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType - + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]"; - } - @Override public int describeContents() { return 0; @@ -100,50 +42,59 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * Return the data type associated with this application configuration. * * @return dataType + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public int getDataType() { - return mDataType; + return 0; } /** * Return the name of the application configuration. * * @return String name + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public String getName() { - return mName; + return null; } /** * Return the role associated with this application configuration. * * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public int getRole() { - return mRole; + return 0; } /** - * Return the channel type associated with this application configuration. - * - * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link - * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}. - * @hide + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ - public int getChannelType() { - return mChannelType; - } - + @Deprecated public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { - String name = in.readString(); - int type = in.readInt(); - int role = in.readInt(); - int channelType = in.readInt(); - return new BluetoothHealthAppConfiguration(name, type, role, - channelType); + return new BluetoothHealthAppConfiguration(); } @Override @@ -153,10 +104,5 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { }; @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(mName); - out.writeInt(mDataType); - out.writeInt(mRole); - out.writeInt(mChannelType); - } + public void writeToParcel(Parcel out, int flags) {} } diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index 40234856b8a..4769212c536 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -23,7 +23,13 @@ import android.util.Log; /** * This abstract class is used to implement {@link BluetoothHealth} callbacks. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public abstract class BluetoothHealthCallback { private static final String TAG = "BluetoothHealthCallback"; @@ -38,8 +44,14 @@ public abstract class BluetoothHealthCallback { * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ @BinderThread + @Deprecated public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, int status) { Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); @@ -58,8 +70,14 @@ public abstract class BluetoothHealthCallback { * @param fd The Parcel File Descriptor when the channel state is connected. * @param channelId The id associated with the channel. This id will be used in future calls * like when disconnecting the channel. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ @BinderThread + @Deprecated public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, int channelId) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3c3a01b191e..3c87c739e1f 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -72,7 +72,13 @@ public interface BluetoothProfile { /** * Health Profile + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated int HEALTH = 3; /** @@ -269,9 +275,8 @@ public interface BluetoothProfile { * Called to notify the client when the proxy object has been * connected to the service. * - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} - * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link - * BluetoothA2dp} + * @param profile - One of {@link #HEADSET} or {@link #A2DP} + * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp} */ public void onServiceConnected(int profile, BluetoothProfile proxy); @@ -279,7 +284,7 @@ public interface BluetoothProfile { * Called to notify the client that this proxy object has been * disconnected from the service. * - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} + * @param profile - One of {@link #HEADSET} or {@link #A2DP} */ public void onServiceDisconnected(int profile); } -- GitLab From 8bb9c7d4acc24f45fad0b1b964f4973e71fab3e3 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 3 Jan 2019 16:23:41 -0800 Subject: [PATCH 1001/1408] Deprecate BluetoothHealth APIs * Mark all BluetoothHealth related APIs as deprecated * Make BluetoothAdapter#getProfileProxy(context, BluetoothProfile.HEALTH) always return false * Remove all logic behind BluetoothHealth APIs and add deprecation error log * Health Device Profile (HDP) and MCAP protocol has been largely replaced by BLE. New applications should use Bluetooth Low Energy instead of legacy Bluetooth Health Device Profile Bug: 111562841 Test: make, unit test, use Bluetooth Change-Id: If99a9d79e9e1b89b75b9b74bd3b1c965247a1892 Merged-In: If99a9d79e9e1b89b75b9b74bd3b1c965247a1892 (cherry picked from commit 07ffaa447fdd967689901cca38eba386a8d97b23) --- .../android/bluetooth/BluetoothAdapter.java | 27 +- .../android/bluetooth/BluetoothHealth.java | 483 +++++------------- .../BluetoothHealthAppConfiguration.java | 122 ++--- .../bluetooth/BluetoothHealthCallback.java | 18 + .../android/bluetooth/BluetoothProfile.java | 13 +- 5 files changed, 207 insertions(+), 456 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 10c8b15a7a7..38245fb2ad2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2066,8 +2066,7 @@ public final class BluetoothAdapter { * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter * is connected to any remote device for a specific profile. - * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}. + * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. * *

        Return value can be one of * {@link BluetoothProfile#STATE_DISCONNECTED}, @@ -2441,16 +2440,15 @@ public final class BluetoothAdapter { /** * Get the profile proxy object associated with the profile. * - *

        Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or - * {@link BluetoothProfile#GATT_SERVER}. Clients must implement - * {@link BluetoothProfile.ServiceListener} to get notified of - * the connection status and to get the proxy object. + *

        Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must + * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status + * and to get the proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. - * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link - * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or + * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or * {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ @@ -2479,8 +2477,8 @@ public final class BluetoothAdapter { BluetoothPan pan = new BluetoothPan(context, listener); return true; } else if (profile == BluetoothProfile.HEALTH) { - BluetoothHealth health = new BluetoothHealth(context, listener); - return true; + Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); + return false; } else if (profile == BluetoothProfile.MAP) { BluetoothMap map = new BluetoothMap(context, listener); return true; @@ -2512,8 +2510,7 @@ public final class BluetoothAdapter { * *

        Clients should call this when they are no longer using * the proxy obtained from {@link #getProfileProxy}. - * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or - * {@link BluetoothProfile#A2DP} + * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP} * * @param profile * @param proxy Profile proxy object @@ -2548,10 +2545,6 @@ public final class BluetoothAdapter { BluetoothPan pan = (BluetoothPan) proxy; pan.close(); break; - case BluetoothProfile.HEALTH: - BluetoothHealth health = (BluetoothHealth) proxy; - health.close(); - break; case BluetoothProfile.GATT: BluetoothGatt gatt = (BluetoothGatt) proxy; gatt.close(); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index b967fb20f02..e2e56fd02ab 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -16,14 +16,7 @@ package android.bluetooth; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Binder; -import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; @@ -53,79 +46,59 @@ import java.util.List; *

      • When done, close the health channel by calling {@link #disconnectChannel} * and unregister the application configuration calling * {@link #unregisterAppConfiguration} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps + * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - /** * Health Profile Source Role - the health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int SOURCE_ROLE = 1 << 0; /** * Health Profile Sink Role the device talking to the health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int SINK_ROLE = 1 << 1; /** * Health Profile - Channel Type used - Reliable + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int CHANNEL_TYPE_RELIABLE = 10; /** * Health Profile - Channel Type used - Streaming + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public static final int CHANNEL_TYPE_STREAMING = 11; - /** - * @hide - */ - public static final int CHANNEL_TYPE_ANY = 12; - - /** @hide */ - public static final int HEALTH_OPERATION_SUCCESS = 6000; - /** @hide */ - public static final int HEALTH_OPERATION_ERROR = 6001; - /** @hide */ - public static final int HEALTH_OPERATION_INVALID_ARGS = 6002; - /** @hide */ - public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003; - /** @hide */ - public static final int HEALTH_OPERATION_NOT_FOUND = 6004; - /** @hide */ - public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005; - - 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); - } - } - } - } - }; - /** * Register an application configuration that acts as a Health SINK. @@ -141,53 +114,17 @@ public final class BluetoothHealth implements BluetoothProfile { * @param callback A callback to indicate success or failure of the registration and all * operations done on this application configuration. * @return If true, callback will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean registerSinkAppConfiguration(String name, int dataType, BluetoothHealthCallback callback) { - if (!isEnabled() || name == null) return false; - - if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); - return registerAppConfiguration(name, dataType, SINK_ROLE, - CHANNEL_TYPE_ANY, callback); - } - - /** - * Register an application configuration that acts as a Health SINK or in a Health - * SOURCE role.This is an asynchronous call and so - * the callback is used to notify success or failure if the function returns true. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param name The friendly name associated with the application or configuration. - * @param dataType The dataType of the Source role of Health Profile. - * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link - * #CHANNEL_TYPE_STREAMING} - * @param callback - A callback to indicate success or failure. - * @return If true, callback will be called. - * @hide - */ - public boolean registerAppConfiguration(String name, int dataType, int role, - int channelType, BluetoothHealthCallback callback) { - boolean result = false; - if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; - - if (VDBG) log("registerApplication(" + name + ":" + dataType + ")"); - BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); - BluetoothHealthAppConfiguration config = - new BluetoothHealthAppConfiguration(name, dataType, role, channelType); - - final IBluetoothHealth service = mService; - if (service != null) { - try { - result = service.registerAppConfiguration(config, wrapper); - } 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 result; + Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated"); + return false; } /** @@ -198,22 +135,16 @@ public final class BluetoothHealth implements BluetoothProfile { * * @param config The health app configuration * @return Success or failure. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { - boolean result = false; - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && config != null) { - try { - result = service.unregisterAppConfiguration(config); - } 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 result; + Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated"); + return false; } /** @@ -227,49 +158,16 @@ public final class BluetoothHealth implements BluetoothProfile { * @param config The application configuration which has been registered using {@link * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @return If true, the callback associated with the application config will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.connectChannelToSource(device, config); - } 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; - } - - /** - * Connect to a health device which has the {@link #SINK_ROLE}. - * This is an asynchronous call. If this function returns true, the callback - * associated with the application configuration will be called. - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using {@link - * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @return If true, the callback associated with the application config will be called. - * @hide - */ - public boolean connectChannelToSink(BluetoothDevice device, - BluetoothHealthAppConfiguration config, int channelType) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.connectChannelToSink(device, config, channelType); - } 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())); - } + Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated"); return false; } @@ -285,20 +183,16 @@ public final class BluetoothHealth implements BluetoothProfile { * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * @param channelId The channel id associated with the channel * @return If true, the callback associated with the application config will be called. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.disconnectChannel(device, config, channelId); - } 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())); - } + Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated"); return false; } @@ -314,20 +208,16 @@ public final class BluetoothHealth implements BluetoothProfile { * @param device The remote Bluetooth health device * @param config The application configuration * @return null on failure, ParcelFileDescriptor on success. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device) && config != null) { - try { - return service.getMainChannelFd(device, config); - } 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())); - } + Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated"); return null; } @@ -347,17 +237,7 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getHealthDeviceConnectionState(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())); - } + Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated"); return STATE_DISCONNECTED; } @@ -377,17 +257,8 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getConnectedDevices() { - final IBluetoothHealth service = mService; - if (service != null && isEnabled()) { - try { - return service.getConnectedHealthDevices(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); + Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated"); + return new ArrayList<>(); } /** @@ -409,163 +280,81 @@ public final class BluetoothHealth implements BluetoothProfile { */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHealth service = mService; - if (service != null && isEnabled()) { - try { - return service.getHealthDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); + Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated"); + return new ArrayList<>(); } - private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub { - private BluetoothHealthCallback mCallback; - - public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) { - mCallback = callback; - } - - @Override - public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - mCallback.onHealthAppConfigurationStatusChange(config, status); - } - - @Override - public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd, int channelId) { - mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd, - channelId); - } - } - - /** Health Channel Connection State - Disconnected */ + /** Health Channel Connection State - Disconnected + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_DISCONNECTED = 0; - /** Health Channel Connection State - Connecting */ + /** Health Channel Connection State - Connecting + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_CONNECTING = 1; - /** Health Channel Connection State - Connected */ + /** Health Channel Connection State - Connected + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_CONNECTED = 2; - /** Health Channel Connection State - Disconnecting */ + /** Health Channel Connection State - Disconnecting + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int STATE_CHANNEL_DISCONNECTING = 3; - /** Health App Configuration registration success */ + /** Health App Configuration registration success + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; - /** Health App Configuration registration failure */ + /** Health App Configuration registration failure + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; - /** Health App Configuration un-registration success */ + /** Health App Configuration un-registration success + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} + */ + @Deprecated public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; - /** Health App Configuration un-registration failure */ - public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; - - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHealth mService; - BluetoothAdapter mAdapter; - - /** - * Create a BluetoothHealth proxy object. + /** Health App Configuration un-registration failure + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ - /*package*/ BluetoothHealth(Context context, ServiceListener l) { - 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(IBluetoothHealth.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 Health Service with " + intent); - return false; - } - return true; - } - - /*package*/ void close() { - if (VDBG) log("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; - } - - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH); - } - } - }; - - private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - log("Bluetooth is Not enabled"); - return false; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private boolean checkAppParam(String name, int role, int channelType, - BluetoothHealthCallback callback) { - if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) - || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING - && channelType != CHANNEL_TYPE_ANY) - || callback == null) { - return false; - } - if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false; - return true; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } + @Deprecated + public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; } diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 7c9db6f7213..9788bbf74e3 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -25,72 +25,14 @@ import android.os.Parcelable; * the {@link BluetoothHealth} class. This class represents an application configuration * that the Bluetooth Health third party application will register to communicate with the * remote Bluetooth health device. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public final class BluetoothHealthAppConfiguration implements Parcelable { - private final String mName; - private final int mDataType; - private final int mRole; - private final int mChannelType; - - /** - * Constructor to register the SINK role - * - * @param name Friendly name associated with the application configuration - * @param dataType Data Type of the remote Bluetooth Health device - * @hide - */ - BluetoothHealthAppConfiguration(String name, int dataType) { - mName = name; - mDataType = dataType; - mRole = BluetoothHealth.SINK_ROLE; - mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY; - } - - /** - * Constructor to register the application configuration. - * - * @param name Friendly name associated with the application configuration - * @param dataType Data Type of the remote Bluetooth Health device - * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} - * @hide - */ - BluetoothHealthAppConfiguration(String name, int dataType, int role, int - channelType) { - mName = name; - mDataType = dataType; - mRole = role; - mChannelType = channelType; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothHealthAppConfiguration) { - BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o; - - if (mName == null) return false; - - return mName.equals(config.getName()) && mDataType == config.getDataType() - && mRole == config.getRole() && mChannelType == config.getChannelType(); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (mName != null ? mName.hashCode() : 0); - result = 31 * result + mDataType; - result = 31 * result + mRole; - result = 31 * result + mChannelType; - return result; - } - - @Override - public String toString() { - return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType - + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]"; - } - @Override public int describeContents() { return 0; @@ -100,50 +42,59 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * Return the data type associated with this application configuration. * * @return dataType + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public int getDataType() { - return mDataType; + return 0; } /** * Return the name of the application configuration. * * @return String name + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public String getName() { - return mName; + return null; } /** * Return the role associated with this application configuration. * * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated public int getRole() { - return mRole; + return 0; } /** - * Return the channel type associated with this application configuration. - * - * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link - * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}. - * @hide + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ - public int getChannelType() { - return mChannelType; - } - + @Deprecated public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { - String name = in.readString(); - int type = in.readInt(); - int role = in.readInt(); - int channelType = in.readInt(); - return new BluetoothHealthAppConfiguration(name, type, role, - channelType); + return new BluetoothHealthAppConfiguration(); } @Override @@ -153,10 +104,5 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { }; @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(mName); - out.writeInt(mDataType); - out.writeInt(mRole); - out.writeInt(mChannelType); - } + public void writeToParcel(Parcel out, int flags) {} } diff --git a/framework/java/android/bluetooth/BluetoothHealthCallback.java b/framework/java/android/bluetooth/BluetoothHealthCallback.java index 40234856b8a..4769212c536 100644 --- a/framework/java/android/bluetooth/BluetoothHealthCallback.java +++ b/framework/java/android/bluetooth/BluetoothHealthCallback.java @@ -23,7 +23,13 @@ import android.util.Log; /** * This abstract class is used to implement {@link BluetoothHealth} callbacks. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ +@Deprecated public abstract class BluetoothHealthCallback { private static final String TAG = "BluetoothHealthCallback"; @@ -38,8 +44,14 @@ public abstract class BluetoothHealthCallback { * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ @BinderThread + @Deprecated public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, int status) { Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); @@ -58,8 +70,14 @@ public abstract class BluetoothHealthCallback { * @param fd The Parcel File Descriptor when the channel state is connected. * @param channelId The id associated with the channel. This id will be used in future calls * like when disconnecting the channel. + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ @BinderThread + @Deprecated public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, int channelId) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3c3a01b191e..3c87c739e1f 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -72,7 +72,13 @@ public interface BluetoothProfile { /** * Health Profile + * + * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New + * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, + * {@link BluetoothAdapter#listenUsingL2capChannel()}, or + * {@link BluetoothDevice#createL2capChannel(int)} */ + @Deprecated int HEALTH = 3; /** @@ -269,9 +275,8 @@ public interface BluetoothProfile { * Called to notify the client when the proxy object has been * connected to the service. * - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} - * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link - * BluetoothA2dp} + * @param profile - One of {@link #HEADSET} or {@link #A2DP} + * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp} */ public void onServiceConnected(int profile, BluetoothProfile proxy); @@ -279,7 +284,7 @@ public interface BluetoothProfile { * Called to notify the client that this proxy object has been * disconnected from the service. * - * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP} + * @param profile - One of {@link #HEADSET} or {@link #A2DP} */ public void onServiceDisconnected(int profile); } -- GitLab From 2d4e9092b015b14419cf9a2ecbd64b12c431ee89 Mon Sep 17 00:00:00 2001 From: Rene Mayrhofer Date: Wed, 28 Nov 2018 11:32:40 -0800 Subject: [PATCH 1002/1408] Enable airplane mode when booting in safe mode Make it possible to boot in airplane mode when it was not turned on before shutdown/reboot, either for situations where this is required by regulation or to work around problems in normal boot. When safe mode is engaged (Vol-Down), disabled in two stages: 1. Set Settings.Global.AIRPLANE_MODE_ON as early as possible for subsystems to react to it during their initialization. 2. Trigger the public ConnectiviyService API to enable airplane mode. This has to be done during later stages in initialization because it requires sending broadcasts, so some RF leakage may still happen before airplane mode is enabled (time window of a few seconds). Bluetooth is not currently disabled and needs to be fixed by another change. WiFi and NFC are turned off alongside cellular modems. Test: manual Bug: 120145930 Change-Id: I1d61990a9d6f7cd046d28b3e49ecf402dd485063 (cherry picked from commit afc9bc80941ea015cb591a0032e7553b5805fcc6) --- .../com/android/server/bluetooth/BluetoothManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 555e89947a6..5ea3390d4e4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -279,7 +279,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( - st)); + st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); if (isAirplaneModeOn()) { // Clear registered LE apps to force shut-off -- GitLab From 5113dd680716211be7b7c23c267abf45c51b729c Mon Sep 17 00:00:00 2001 From: Arun Mirpuri Date: Fri, 11 Jan 2019 18:39:21 -0800 Subject: [PATCH 1003/1408] audio: Add API for BT to query offload A2DP encoding formats Add API in AudioManager to query offload A2DP encoding formats supported on primary HAL. This can be used instead of reading from property Bug: 111812273 Test: make Change-Id: I168f288d0bf32d6c9733c9b57934084667e794ee --- .../android/bluetooth/BluetoothCodecConfig.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 79c0a3a207c..c9d0ef247ca 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -114,6 +114,19 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage + public BluetoothCodecConfig(int codecType) { + mCodecType = codecType; + mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; + mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; + mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; + mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; + mCodecSpecific1 = 0; + mCodecSpecific2 = 0; + mCodecSpecific3 = 0; + mCodecSpecific4 = 0; + } + @Override public boolean equals(Object o) { if (o instanceof BluetoothCodecConfig) { -- GitLab From d22fb8f068841b8848589cb99eea7ef9c2e09d29 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 8 Jan 2019 09:00:09 +0800 Subject: [PATCH 1004/1408] Skeleton implementation of Bluetooth metadata APIs Bug: 121051445 Test: Build pass Change-Id: I5e80210205b37294b1eb8356502ebf242e627ce4 --- .../android/bluetooth/BluetoothAdapter.java | 175 ++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 188 ++++++++++++++++++ 2 files changed, 363 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 38245fb2ad2..e04aac49cfb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -36,6 +36,7 @@ import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -648,6 +649,32 @@ public final class BluetoothAdapter { private final Object mLock = new Object(); private final Map mLeScanClients; + private static final Map>> + sMetadataListeners = new HashMap<>(); + + /** + * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener + * implementation. + */ + private static final IBluetoothMetadataListener sBluetoothMetadataListener = + new IBluetoothMetadataListener.Stub() { + @Override + public void onMetadataChanged(BluetoothDevice device, int key, String value) { + synchronized (sMetadataListeners) { + if (sMetadataListeners.containsKey(device)) { + List> list = sMetadataListeners.get(device); + for (Pair pair : list) { + MetadataListener listener = pair.first; + Handler handler = pair.second; + handler.post(() -> { + listener.onMetadataChanged(device, key, value); + }); + } + } + } + return; + } + }; /** * Get a handle to the default local Bluetooth adapter. @@ -2607,6 +2634,16 @@ public final class BluetoothAdapter { } } } + synchronized (sMetadataListeners) { + sMetadataListeners.forEach((device, pair) -> { + try { + mService.registerMetadataListener(sBluetoothMetadataListener, + device); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register metadata listener", e); + } + }); + } } public void onBluetoothServiceDown() { @@ -3090,4 +3127,142 @@ public final class BluetoothAdapter { + "listenUsingInsecureL2capChannel"); return listenUsingInsecureL2capChannel(); } + + /** + * Register a {@link #MetadataListener} to receive update about metadata + * changes for this {@link BluetoothDevice}. + * Registration must be done when Bluetooth is ON and will last until + * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth + * restarted in the middle. + * All input parameters should not be null or {@link NullPointerException} will be triggered. + * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered + * once, double registration would cause {@link IllegalArgumentException}. + * + * @param device {@link BluetoothDevice} that will be registered + * @param listener {@link #MetadataListener} that will receive asynchronous callbacks + * @param handler the handler for listener callback + * @return true on success, false on error + * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} + * is null. + * @throws IllegalArgumentException The same {@link #MetadataListener} and + * {@link BluetoothDevice} are registered twice. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener, + Handler handler) { + if (DBG) Log.d(TAG, "registerMetdataListener()"); + + final IBluetooth service = mService; + if (service == null) { + Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener"); + return false; + } + if (listener == null) { + throw new NullPointerException("listener is null"); + } + if (device == null) { + throw new NullPointerException("device is null"); + } + if (handler == null) { + throw new NullPointerException("handler is null"); + } + + synchronized (sMetadataListeners) { + List> listenerList = sMetadataListeners.get(device); + if (listenerList == null) { + // Create new listener/handler list for registeration + listenerList = new ArrayList<>(); + sMetadataListeners.put(device, listenerList); + } else { + // Check whether this device was already registed by the lisenter + if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { + throw new IllegalArgumentException("listener was already regestered" + + " for the device"); + } + } + + Pair listenerPair = new Pair(listener, handler); + listenerList.add(listenerPair); + + boolean ret = false; + try { + ret = service.registerMetadataListener(sBluetoothMetadataListener, device); + } catch (RemoteException e) { + Log.e(TAG, "registerMetadataListener fail", e); + } finally { + if (!ret) { + // Remove listener registered earlier when fail. + listenerList.remove(listenerPair); + if (listenerList.isEmpty()) { + // Remove the device if its listener list is empty + sMetadataListeners.remove(device); + } + } + } + return ret; + } + } + + /** + * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}. + * Unregistration can be done when Bluetooth is either ON or OFF. + * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must + * be called before unregisteration. + * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}. + * + * @param device {@link BluetoothDevice} that will be unregistered. it + * should not be null or {@link NullPointerException} will be triggered. + * @return true on success, false on error + * @throws NullPointerException If {@code device} is null. + * @throws IllegalArgumentException If {@code device} has not been registered before. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean unregisterMetadataListener(BluetoothDevice device) { + if (DBG) Log.d(TAG, "unregisterMetdataListener()"); + if (device == null) { + throw new NullPointerException("device is null"); + } + + synchronized (sMetadataListeners) { + if (sMetadataListeners.containsKey(device)) { + sMetadataListeners.remove(device); + } else { + throw new IllegalArgumentException("device was not registered"); + } + + final IBluetooth service = mService; + if (service == null) { + // Bluetooth is OFF, do nothing to Bluetooth service. + return true; + } + try { + return service.unregisterMetadataListener(device); + } catch (RemoteException e) { + Log.e(TAG, "unregisterMetadataListener fail", e); + return false; + } + } + } + + /** + * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. + * @hide + */ + @SystemApi + public abstract class MetadataListener { + /** + * Callback triggered if the metadata of {@link BluetoothDevice} registered in + * {@link #registerMetadataListener}. + * + * @param device changed {@link BluetoothDevice}. + * @param key changed metadata key, one of BluetoothDevice.METADATA_*. + * @param value the new value of metadata. + */ + public void onMetadataChanged(BluetoothDevice device, int key, String value) { + } + } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index b2b02850e2e..7a29c273dae 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -340,6 +340,137 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; + /** + * Maximum length of a metadata entry, this is to avoid exploding Bluetooth + * disk usage + * @hide + */ + @SystemApi + public static final int METADATA_MAX_LENGTH = 2048; + + /** + * Manufacturer name of this Bluetooth device + * @hide + */ + @SystemApi + public static final int METADATA_MANUFACTURER_NAME = 0; + + /** + * Model name of this Bluetooth device + * @hide + */ + @SystemApi + public static final int METADATA_MODEL_NAME = 1; + + /** + * Software version of this Bluetooth device + * @hide + */ + @SystemApi + public static final int METADATA_SOFTWARE_VERSION = 2; + + /** + * Hardware version of this Bluetooth device + * @hide + */ + @SystemApi + public static final int METADATA_HARDWARE_VERSION = 3; + + /** + * Package name of the companion app, if any + * @hide + */ + @SystemApi + public static final int METADATA_COMPANION_APP = 4; + + /** + * URI to the main icon shown on the settings UI + * @hide + */ + @SystemApi + public static final int METADATA_MAIN_ICON = 5; + + /** + * Whether this device is an untethered headset with left, right and case + * @hide + */ + @SystemApi + public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; + + /** + * URI to icon of the left headset + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; + + /** + * URI to icon of the right headset + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; + + /** + * URI to icon of the headset charging case + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_CASE_ICON = 9; + + /** + * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} + * is invalid, of the left headset + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; + + /** + * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} + * is invalid, of the right headset + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; + + /** + * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} + * is invalid, of the headset charging case + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; + + /** + * Whether the left headset is charging + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; + + /** + * Whether the right headset is charging + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; + + /** + * Whether the headset charging case is charging + * @hide + */ + @SystemApi + public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; + + /** + * URI to the enhanced settings UI slice, null or empty String means + * the UI does not exist + * @hide + */ + @SystemApi + public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; + /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it @@ -2028,4 +2159,61 @@ public final class BluetoothDevice implements Parcelable { Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); return createInsecureL2capChannel(psm); } + + /** + * Set a keyed metadata of this {@link BluetoothDevice} to a + * {@link String} value. + * Only bonded devices's metadata will be persisted across Bluetooth + * restart. + * Metadata will be removed when the device's bond state is moved to + * {@link #BOND_NONE}. + * + * @param key must be within the list of BluetoothDevice.METADATA_* + * @param value the string data to set for key. Must be less than + * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length + * @return true on success, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setMetadata(int key, String value) { + final IBluetooth service = sService; + if (service == null) { + Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); + return false; + } + if (value.length() > METADATA_MAX_LENGTH) { + throw new IllegalArgumentException("value length is " + value.length() + + ", should not over " + METADATA_MAX_LENGTH); + } + try { + return service.setMetadata(this, key, value); + } catch (RemoteException e) { + Log.e(TAG, "setMetadata fail", e); + return false; + } + } + + /** + * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} + * + * @param key must be within the list of BluetoothDevice.METADATA_* + * @return Metadata of the key as string, null on error or not found + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public String getMetadata(int key) { + final IBluetooth service = sService; + if (service == null) { + Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); + return null; + } + try { + return service.getMetadata(this, key); + } catch (RemoteException e) { + Log.e(TAG, "getMetadata fail", e); + return null; + } + } } -- GitLab From 370a8ae8d0e015a5e01a3c1c2c3b1387e3026b08 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Sun, 13 Jan 2019 16:04:31 -0800 Subject: [PATCH 1005/1408] Add 2 new ways to check for Support for Hearing Aids Profile The getProfileProxy will return false if Hearing Aids Profile is not supported. Also the getSupportedProfiles will return the correct support for Hearing Aids even when Bluetooth is disabled. Test: Manual testing with configuration enabled and disabled. Bug: 119617521 Change-Id: I146bd3bc36d4c474f7bca18a05b679fb8e70ca63 --- .../android/bluetooth/BluetoothAdapter.java | 26 +++++++++++++++++-- .../bluetooth/BluetoothManagerService.java | 17 ++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 38245fb2ad2..db8674b5240 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1872,6 +1872,20 @@ public final class BluetoothAdapter { return 0; } + /** + * Return true if Hearing Aid Profile is supported. + * + * @return true if phone supports Hearing Aid Profile + */ + private boolean isHearingAidProfileSupported() { + try { + return mManagerService.isHearingAidProfileSupported(); + } catch (RemoteException e) { + Log.e(TAG, "remote expection when calling isHearingAidProfileSupported", e); + return false; + } + } + /** * Get the maximum number of connected audio devices. * @@ -2024,6 +2038,11 @@ public final class BluetoothAdapter { supportedProfiles.add(i); } } + } else { + // Bluetooth is disabled. Just fill in known supported Profiles + if (isHearingAidProfileSupported()) { + supportedProfiles.add(BluetoothProfile.HEARING_AID); + } } } } catch (RemoteException e) { @@ -2498,8 +2517,11 @@ public final class BluetoothAdapter { BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); return true; } else if (profile == BluetoothProfile.HEARING_AID) { - BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); - return true; + if (isHearingAidProfileSupported()) { + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + return true; + } + return false; } else { return false; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 5ea3390d4e4..b60dd0f52c7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -208,6 +208,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private int mErrorRecoveryRetryCounter; private final int mSystemUiUid; + private boolean mIsHearingAidProfileSupported; + // Save a ProfileServiceConnections object for each of the bound // bluetooth profile services private final Map mProfileServices = new HashMap<>(); @@ -391,13 +393,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mCallbacks = new RemoteCallbackList(); mStateChangeCallbacks = new RemoteCallbackList(); + mIsHearingAidProfileSupported = context.getResources() + .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported); + // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils - boolean isHearingAidEnabled; String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); if (!TextUtils.isEmpty(value)) { - isHearingAidEnabled = Boolean.parseBoolean(value); + boolean isHearingAidEnabled = Boolean.parseBoolean(value); Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); + if (isHearingAidEnabled && !mIsHearingAidProfileSupported) { + // Overwrite to enable support by FeatureFlag + mIsHearingAidProfileSupported = true; + } } IntentFilter filter = new IntentFilter(); @@ -679,6 +687,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + @Override + public boolean isHearingAidProfileSupported() { + return mIsHearingAidProfileSupported; + } + // Monitor change of BLE scan only mode settings. private void registerForBleScanModeChange() { ContentObserver contentObserver = new ContentObserver(null) { -- GitLab From d171d884786d129a20adf7685d869d887ab5d430 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 15 Nov 2018 17:11:36 -0800 Subject: [PATCH 1006/1408] Add Android APIs for Hearing Aids Profile Add the new public Android APIs for the ASHA Hearing Aids Profile. Bug: 119617521 Bug: 120222233 Test: Run with the new HearingAidProfileTest CTS test Change-Id: I05fc3d565bd22b5000765122da7714d961dbc15b --- .../android/bluetooth/BluetoothAdapter.java | 11 +-- .../bluetooth/BluetoothHearingAid.java | 81 +++++++------------ .../android/bluetooth/BluetoothProfile.java | 1 - 3 files changed, 35 insertions(+), 58 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index db8674b5240..724fef028d2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2460,15 +2460,16 @@ public final class BluetoothAdapter { * Get the profile proxy object associated with the profile. * *

        Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must - * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status - * and to get the proxy object. + * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link + * BluetoothProfile#GATT_SERVER}. Clients must implement {@link + * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the + * proxy object. * * @param context Context of the application * @param listener The service Listener for connection callbacks. * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or - * {@link BluetoothProfile#GATT_SERVER}. + * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link + * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 2bf7daddcb2..23a8159de4c 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -38,15 +38,14 @@ import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * This class provides the public APIs to control the Bluetooth Hearing Aid - * profile. + * This class provides the public APIs to control the Hearing Aid profile. * *

        BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get * the BluetoothHearingAid proxy object. * - *

        Each method is protected with its appropriate permission. - * @hide + *

        Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each + * method is protected with its appropriate permission. */ public final class BluetoothHearingAid implements BluetoothProfile { private static final String TAG = "BluetoothHearingAid"; @@ -55,7 +54,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * Intent used to broadcast the change in connection state of the Hearing Aid - * profile. + * profile. Please note that in the binaural case, there will be two different LE devices for + * the left and right side and each device will have their own connection state changes.S * *

        This intent will have 3 extras: *

          @@ -75,27 +75,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; - /** - * Intent used to broadcast the change in the Playing state of the Hearing Aid - * profile. - * - *

          This intent will have 3 extras: - *

            - *
          • {@link #EXTRA_STATE} - The current state of the profile.
          • - *
          • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
          • - *
          • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
          • - *
          - * - *

          {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PLAYING_STATE_CHANGED = - "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED"; - /** * Intent used to broadcast the selection of a connected device as active. * @@ -107,6 +86,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. + * + * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage @@ -114,32 +95,38 @@ public final class BluetoothHearingAid implements BluetoothProfile { "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; /** - * Hearing Aid device is streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + * This device represents Left Hearing Aid. + * + * @hide */ - public static final int STATE_PLAYING = 10; + public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; /** - * Hearing Aid device is NOT streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. + * This device represents Right Hearing Aid. + * + * @hide */ - public static final int STATE_NOT_PLAYING = 11; - - /** This device represents Left Hearing Aid. */ - public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; - - /** This device represents Right Hearing Aid. */ public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT; - /** This device is Monaural. */ + /** + * This device is Monaural. + * + * @hide + */ public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL; - /** This device is Binaural (should receive only left or right audio). */ + /** + * This device is Binaural (should receive only left or right audio). + * + * @hide + */ public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL; - /** Can't read ClientID for this device */ + /** + * Indicates the HiSyncID could not be read and is unavailable. + * + * @hide + */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; private Context mContext; @@ -235,12 +222,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { } } - @Override - public void finalize() { - // The empty finalize needs to be kept or the - // cts signature tests would fail. - } - /** * Initiate connection to a profile of the remote bluetooth device. * @@ -537,10 +518,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { return "connected"; case STATE_DISCONNECTING: return "disconnecting"; - case STATE_PLAYING: - return "playing"; - case STATE_NOT_PLAYING: - return "not playing"; default: return ""; } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3c87c739e1f..b8670dbeada 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -185,7 +185,6 @@ public interface BluetoothProfile { /** * Hearing Aid Device * - * @hide */ int HEARING_AID = 21; -- GitLab From 93802016417b69824d1a00a928cf900343f3e4e2 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Sun, 13 Jan 2019 02:50:07 +0800 Subject: [PATCH 1007/1408] Introduce system APIs for Bluetooth silence mode. Policies of silence mode: 1) If an active device (for A2DP or HFP) enters silence mode, the active device for that profile will be set to null. 2) If a device exits silence mode while the A2DP or HFP active device is null, the device will be set as the active device for that profile. 3) If a device is disconnected, it exits silence mode. 4) If a device is set as the active device for A2DP or HFP, while silence mode is enabled, then the device will exit silence mode. 5) If a device is in silence mode, AVRCP position change event and HFP AG indicators will be disabled. 6) If a device is not connected with A2DP or HFP, it cannot enter silence mode. Bug: 112323989 Test: Manual, runtest bluetooth Change-Id: If7f340de38c350f17c37f00a3a2e871876baa20d --- .../android/bluetooth/BluetoothDevice.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7a29c273dae..2803856fb3b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -531,6 +531,28 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_CONNECTION_ACCESS_CANCEL = "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; + /** + * Intent to broadcast silence mode changed. + * Alway contains the extra field {@link #EXTRA_DEVICE} + * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED} + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SystemApi + public static final String ACTION_SILENCE_MODE_CHANGED = + "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; + + /** + * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent, + * contains whether device is in silence mode as boolean. + * + * @hide + */ + @SystemApi + public static final String EXTRA_SILENCE_ENABLED = + "android.bluetooth.device.extra.SILENCE_ENABLED"; + /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. * @@ -1591,6 +1613,70 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } + /** + * Set the Bluetooth device silence mode. + * + * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice} + * is an active device (for A2DP or HFP), the active device for that profile + * will be set to null. + * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP + * active device is null, the {@link BluetoothDevice} will be set as the + * active device for that profile. + * If the {@link BluetoothDevice} is disconnected, it exits silence mode. + * If the {@link BluetoothDevice} is set as the active device for A2DP or + * HFP, while silence mode is enabled, then the device will exit silence mode. + * If the {@link BluetoothDevice} is in silence mode, AVRCP position change + * event and HFP AG indicators will be disabled. + * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot + * enter silence mode. + * + *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @param silence true to enter silence mode, false to exit + * @return true on success, false on error. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setSilenceMode(boolean silence) { + final IBluetooth service = sService; + if (service == null) { + return false; + } + try { + if (getSilenceMode() == silence) { + return true; + } + return service.setSilenceMode(this, silence); + } catch (RemoteException e) { + Log.e(TAG, "setSilenceMode fail", e); + return false; + } + } + + /** + * Get the device silence mode status + * + *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @return true on device in silence mode, otherwise false. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean getSilenceMode() { + final IBluetooth service = sService; + if (service == null) { + return false; + } + try { + return service.getSilenceMode(this); + } catch (RemoteException e) { + Log.e(TAG, "getSilenceMode fail", e); + return false; + } + } + /** * Sets whether the phonebook access is allowed to this device. *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. -- GitLab From 064375d6031318fdf0e5a95eabafe9c4ce677014 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 24 Jan 2019 09:13:00 +0800 Subject: [PATCH 1008/1408] Change MetadataListener to a abstract static class Bug: 121051445 Test: build pass Change-Id: I8148d13d2eb0899d54817197ae0be236e2914e47 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e04aac49cfb..010a7b20493 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3253,7 +3253,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - public abstract class MetadataListener { + public abstract static class MetadataListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in * {@link #registerMetadataListener}. -- GitLab From dfd389ca2c818b1c345f8858c357ee57ac1755b5 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 14 Sep 2018 16:01:28 -0700 Subject: [PATCH 1009/1408] AudioDeviceBroker in audio service New AudioDeviceBroker class running in audio service. Has dedicated message loop for handling audio device connections and disconnections. New helper classes for AudioDeviceBroker: - BtHelper for Bluetooth - AudioDeviceInventory to manage list of devices Bug: 112863932 Test: media CTS + audio CTS Verifier Change-Id: I3e8f662a9d82fa7245695888e14fac7f4fc6e728 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++-- .../android/bluetooth/BluetoothCodecStatus.java | 5 +++-- .../java/android/bluetooth/BluetoothProfile.java | 13 +++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 171c2f5b1a0..c4bf1ebd1a6 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -434,7 +434,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(BluetoothDevice device) { + public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { mServiceLock.readLock().lock(); @@ -689,7 +689,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage - public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { + public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { mServiceLock.readLock().lock(); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 78560d2de42..2cb7b2d3c7e 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -42,7 +43,7 @@ public final class BluetoothCodecStatus implements Parcelable { public static final String EXTRA_CODEC_STATUS = "android.bluetooth.codec.extra.CODEC_STATUS"; - private final BluetoothCodecConfig mCodecConfig; + private final @Nullable BluetoothCodecConfig mCodecConfig; private final BluetoothCodecConfig[] mCodecsLocalCapabilities; private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; @@ -140,7 +141,7 @@ public final class BluetoothCodecStatus implements Parcelable { * @return the current codec configuration */ @UnsupportedAppUsage - public BluetoothCodecConfig getCodecConfig() { + public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 3c87c739e1f..9e0604f2987 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -18,11 +18,14 @@ package android.bluetooth; import android.Manifest; +import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -60,6 +63,16 @@ public interface BluetoothProfile { /** The profile is in disconnecting state */ int STATE_DISCONNECTING = 3; + /** @hide */ + @IntDef({ + STATE_DISCONNECTED, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfileState {} + /** * Headset and Handsfree profile */ -- GitLab From 85f78efb24d0b904b4976c1db3c8ed0ac44bb205 Mon Sep 17 00:00:00 2001 From: Nan Zhang Date: Thu, 11 Oct 2018 17:54:36 -0700 Subject: [PATCH 1010/1408] Fix the incorrect javadoc links A lot of unresolved link errors showing up after go/ag/5172152. Test: m -j docs with -lerror enabled Bug: b/116163454 Change-Id: I74d1f75e0f00015410a63e13103c28a9c84b4fe0 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 193ff6346ae..4d8dc35d714 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -2174,9 +2174,7 @@ public final class BluetoothDevice implements Parcelable { *

          The remote device will be authenticated and communication on this socket will be * encrypted. *

          Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int, - * int)}. + * to the authentication of the link key to prevent man-in-the-middle type of attacks. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection -- GitLab From bb9d7760860ef96972372761d793b9d2a033b5fc Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 15 Feb 2019 13:13:45 -0800 Subject: [PATCH 1011/1408] Fix the bad link due to typo Replaces the link with the new API name, createInsecureL2capChannel. Bug: 70683224 Test: Compile Change-Id: I9580d90722f8b0c0146a902bb5fcace4ef58d421 Merged-In: Ia06e1fffd814671289a1caebd5962aedc18a28d7 --- framework/java/android/bluetooth/BluetoothDevice.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 2803856fb3b..23f29041487 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -2175,8 +2175,7 @@ public final class BluetoothDevice implements Parcelable { * encrypted. *

          Use this socket if an authenticated socket link is possible. Authentication refers * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, - * int)}. + * secure socket connection is not possible, use {#link createInsecureL2capChannel(int)}. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection -- GitLab From 5075a69b222e323a8eb91fa1eb20a9f33068d3ff Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 25 Feb 2019 21:10:08 +0800 Subject: [PATCH 1012/1408] Change BluetoothCodecStatus.sameCapabilities() to public - Public this API to help A2DP state machine check selectable codec capabilities status. Bug: 124254557 Bug: 125551347 Test: runtest bluetooth Change-Id: If44887f756d2e8348e8f76dfb67b77b993ffd8db --- framework/java/android/bluetooth/BluetoothCodecStatus.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 78560d2de42..32bb681f2e8 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -74,8 +74,8 @@ public final class BluetoothCodecStatus implements Parcelable { * @param c2 the second array of capabilities to compare * @return true if both arrays contain same capabilities */ - private static boolean sameCapabilities(BluetoothCodecConfig[] c1, - BluetoothCodecConfig[] c2) { + public static boolean sameCapabilities(BluetoothCodecConfig[] c1, + BluetoothCodecConfig[] c2) { if (c1 == null) { return (c2 == null); } -- GitLab From 13613a3f176ab3e10367685367443dddeee77db5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 28 Feb 2019 12:06:45 -0700 Subject: [PATCH 1013/1408] All Parcelable CREATOR fields are @NonNull. If they were null, then the Parcelable would fail to work. Bug: 126726802 Test: manual Change-Id: I7929ffa2f20e5de1c8e68e8263cca99496e9d014 Exempt-From-Owner-Approval: Trivial API annotations --- .../java/android/bluetooth/BluetoothActivityEnergyInfo.java | 2 +- framework/java/android/bluetooth/BluetoothAudioConfig.java | 2 +- .../java/android/bluetooth/BluetoothAvrcpPlayerSettings.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 2 +- framework/java/android/bluetooth/BluetoothCodecConfig.java | 2 +- framework/java/android/bluetooth/BluetoothCodecStatus.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- framework/java/android/bluetooth/BluetoothGattDescriptor.java | 2 +- .../java/android/bluetooth/BluetoothGattIncludedService.java | 2 +- framework/java/android/bluetooth/BluetoothGattService.java | 2 +- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 2 +- .../java/android/bluetooth/BluetoothHealthAppConfiguration.java | 2 +- .../android/bluetooth/BluetoothHidDeviceAppQosSettings.java | 2 +- .../android/bluetooth/BluetoothHidDeviceAppSdpSettings.java | 2 +- framework/java/android/bluetooth/BluetoothMasInstance.java | 2 +- framework/java/android/bluetooth/OobData.java | 2 +- framework/java/android/bluetooth/UidTraffic.java | 2 +- framework/java/android/bluetooth/le/AdvertiseData.java | 2 +- framework/java/android/bluetooth/le/AdvertiseSettings.java | 2 +- .../java/android/bluetooth/le/AdvertisingSetParameters.java | 2 +- .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 2 +- .../java/android/bluetooth/le/ResultStorageDescriptor.java | 2 +- framework/java/android/bluetooth/le/ScanFilter.java | 2 +- framework/java/android/bluetooth/le/ScanResult.java | 2 +- framework/java/android/bluetooth/le/ScanSettings.java | 2 +- 26 files changed, 26 insertions(+), 26 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index 43b79db6f35..df065bf1bf8 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -76,7 +76,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { + " }"; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { return new BluetoothActivityEnergyInfo(in); diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java index a4410563c99..9591a70b05d 100644 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.java +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java @@ -64,7 +64,7 @@ public final class BluetoothAudioConfig implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothAudioConfig createFromParcel(Parcel in) { int sampleRate = in.readInt(); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java index 3d3d80e2b4d..30aea1abf73 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java @@ -118,7 +118,7 @@ public final class BluetoothAvrcpPlayerSettings implements Parcelable { } } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) { return new BluetoothAvrcpPlayerSettings(in); diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 1edbacbae4f..260e2fb1b80 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -94,7 +94,7 @@ public final class BluetoothClass implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothClass createFromParcel(Parcel in) { return new BluetoothClass(in.readInt()); diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index c9d0ef247ca..591c418c936 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -247,7 +247,7 @@ public final class BluetoothCodecConfig implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothCodecConfig createFromParcel(Parcel in) { final int codecType = in.readInt(); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 2cb7b2d3c7e..8eae2b4cf59 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -108,7 +108,7 @@ public final class BluetoothCodecStatus implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothCodecStatus createFromParcel(Parcel in) { final BluetoothCodecConfig codecConfig = in.readTypedObject( diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4d8dc35d714..f71841563a6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -959,7 +959,7 @@ public final class BluetoothDevice implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothDevice createFromParcel(Parcel in) { return new BluetoothDevice(in.readString()); diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 6d46b3a4186..edacf3e0b70 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -302,7 +302,7 @@ public class BluetoothGattCharacteristic implements Parcelable { out.writeTypedList(mDescriptors); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothGattCharacteristic createFromParcel(Parcel in) { return new BluetoothGattCharacteristic(in); diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 3ffbb9e0c05..0783cd2b73c 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -177,7 +177,7 @@ public class BluetoothGattDescriptor implements Parcelable { out.writeInt(mPermissions); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothGattDescriptor createFromParcel(Parcel in) { return new BluetoothGattDescriptor(in); diff --git a/framework/java/android/bluetooth/BluetoothGattIncludedService.java b/framework/java/android/bluetooth/BluetoothGattIncludedService.java index bccf20ef96b..5580619033a 100644 --- a/framework/java/android/bluetooth/BluetoothGattIncludedService.java +++ b/framework/java/android/bluetooth/BluetoothGattIncludedService.java @@ -64,7 +64,7 @@ public class BluetoothGattIncludedService implements Parcelable { out.writeInt(mServiceType); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothGattIncludedService createFromParcel(Parcel in) { return new BluetoothGattIncludedService(in); diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 8e740ee387d..c20faf9db69 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -165,7 +165,7 @@ public class BluetoothGattService implements Parcelable { out.writeTypedList(includedServices); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothGattService createFromParcel(Parcel in) { return new BluetoothGattService(in); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index e02a2f4ae5d..7165dd56a21 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -280,7 +280,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { /** * {@link Parcelable.Creator} interface implementation. */ - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 9788bbf74e3..88e06e58f9c 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -90,7 +90,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index a485b898c2d..b21ebe59d81 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -98,7 +98,7 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override diff --git a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2f0b44f76ff..4e1a2aaedcf 100644 --- a/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/framework/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -92,7 +92,7 @@ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java index 7a31328eaf0..b64d0492faf 100644 --- a/framework/java/android/bluetooth/BluetoothMasInstance.java +++ b/framework/java/android/bluetooth/BluetoothMasInstance.java @@ -57,7 +57,7 @@ public final class BluetoothMasInstance implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothMasInstance createFromParcel(Parcel in) { return new BluetoothMasInstance(in.readInt(), in.readString(), diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index d6325728697..0d0c6ab2efa 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -98,7 +98,7 @@ public class OobData implements Parcelable { out.writeByteArray(mLeSecureConnectionsRandom); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public OobData createFromParcel(Parcel in) { return new OobData(in); diff --git a/framework/java/android/bluetooth/UidTraffic.java b/framework/java/android/bluetooth/UidTraffic.java index cef362b3b57..2ee786a5909 100644 --- a/framework/java/android/bluetooth/UidTraffic.java +++ b/framework/java/android/bluetooth/UidTraffic.java @@ -95,7 +95,7 @@ public class UidTraffic implements Cloneable, Parcelable { + mTxBytes + '}'; } - public static final Creator CREATOR = new Creator() { + public static final @android.annotation.NonNull Creator CREATOR = new Creator() { @Override public UidTraffic createFromParcel(Parcel source) { return new UidTraffic(source); diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index b65c31d1dcf..5fd82583764 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -159,7 +159,7 @@ public final class AdvertiseData implements Parcelable { dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public AdvertiseData[] newArray(int size) { diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 35e232c7500..7129d762cd9 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -139,7 +139,7 @@ public final class AdvertiseSettings implements Parcelable { dest.writeInt(mAdvertiseTimeoutMillis); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public AdvertiseSettings[] newArray(int size) { diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index 0c0291eb0ed..e39b198ae38 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -227,7 +227,7 @@ public final class AdvertisingSetParameters implements Parcelable { dest.writeInt(mTxPowerLevel); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public AdvertisingSetParameters[] newArray(int size) { diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 73a2e74de5b..7a8c2c6716e 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -171,7 +171,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { + ", data=" + Objects.toString(mData) + ", timestampNanos=" + mTimestampNanos + '}'; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public PeriodicAdvertisingReport createFromParcel(Parcel source) { diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java index 63bdf69e727..796c815d69b 100644 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -78,7 +78,7 @@ public final class ResultStorageDescriptor implements Parcelable { mLength = in.readInt(); } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public ResultStorageDescriptor createFromParcel(Parcel source) { diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index c5d435b7613..78140cf65d0 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -161,7 +161,7 @@ public final class ScanFilter implements Parcelable { /** * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. */ - public static final Creator CREATOR = + public static final @android.annotation.NonNull Creator CREATOR = new Creator() { @Override diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index f87a47fb936..855d3454170 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -338,7 +338,7 @@ public final class ScanResult implements Parcelable { + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; } - public static final Parcelable.Creator CREATOR = new Creator() { + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public ScanResult createFromParcel(Parcel source) { return new ScanResult(source); diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 8fdcba85d33..504118ec5da 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -242,7 +242,7 @@ public final class ScanSettings implements Parcelable { return 0; } - public static final Parcelable.Creator CREATOR = + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Creator() { @Override public ScanSettings[] newArray(int size) { -- GitLab From c85bd85a57fd22f12ee2f311c4965f77ded76e80 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 28 Feb 2019 12:22:45 -0800 Subject: [PATCH 1014/1408] Add @NonNull annotations to Hearing Aids Profile APIs Bug: 126699327 Bug: 119617521 Test: Ran with CTS tests for Hearing Aids Profile, android.bluetooth.cts.HearingAidProfileTest Change-Id: If83052a68c867d839e263f490592b80bef513a01 --- .../java/android/bluetooth/BluetoothHearingAid.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 23a8159de4c..c5a0bbd2f80 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -302,7 +303,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override - public List getConnectedDevices() { + public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { mServiceLock.readLock().lock(); @@ -322,8 +323,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * {@inheritDoc} */ - @Override - public List getDevicesMatchingConnectionStates(int[] states) { + @Override public @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { mServiceLock.readLock().lock(); @@ -344,7 +345,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(BluetoothDevice device) { + public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { mServiceLock.readLock().lock(); -- GitLab From d43beed21599dadb89b83628f982420b6f735f08 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Mon, 12 Nov 2018 13:37:06 -0800 Subject: [PATCH 1015/1408] [DO NOT MERGE] Bluetooth support for Headless User If Headless User 0 is enabled then don't launch Bluetooth until after user has switched out of 0. This is specifically an issue for automotive platforms. Bug: 118400949 Test: Boot device while headless user 0 is enabled, with a paired Bluetooth device in a phonecall. Change-Id: I9ea833df7ae7dc8841102ab9693f3810b31f672f (cherry picked from commit 195a1c026fb17ab8b4fa0dd812381fe39126d213) --- .../server/bluetooth/BluetoothService.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 1bf4e3a563c..6018f008054 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -18,15 +18,26 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; +import android.os.SystemProperties; class BluetoothService extends SystemService { + private static final String HEADLESS_SYSTEM_USER = "android.car.systemuser.headless"; + private BluetoothManagerService mBluetoothManagerService; + private boolean mInitialized = false; public BluetoothService(Context context) { super(context); mBluetoothManagerService = new BluetoothManagerService(context); } + private void initialize() { + if (!mInitialized) { + mBluetoothManagerService.handleOnBootPhase(); + mInitialized = true; + } + } + @Override public void onStart() { } @@ -36,13 +47,15 @@ class BluetoothService extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); - } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { - mBluetoothManagerService.handleOnBootPhase(); + } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && + !SystemProperties.getBoolean(HEADLESS_SYSTEM_USER, false)) { + initialize(); } } @Override public void onSwitchUser(int userHandle) { + initialize(); mBluetoothManagerService.handleOnSwitchUser(userHandle); } -- GitLab From 16441d05794af2f51185fbabf218f6cb330df1d6 Mon Sep 17 00:00:00 2001 From: Deqiang Chen Date: Tue, 12 Mar 2019 14:08:12 -0700 Subject: [PATCH 1016/1408] Avoid IllegalArgumentException when bluetooth is disabled When the corresponding profile service is not available on device, bluetoothA2dp, bluetoothHidHost and bluetoothPbap will report IllegalArgumentException when bluetooth is disabled. This change avoid that exception. Bug: 127359897 Test: local test and observe there is no IllegalArgumentException when bluetooth is disabled Change-Id: I457e5225b8710dd5d02c405ef558892e99d1736f (cherry picked from commit ee8371007b82ff8c6d26e8ac90c6e3d3cf6bf2a8) --- framework/java/android/bluetooth/BluetoothA2dp.java | 6 ++++-- framework/java/android/bluetooth/BluetoothHidHost.java | 6 ++++-- framework/java/android/bluetooth/BluetoothPbap.java | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 171c2f5b1a0..9faa1d675d8 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -224,8 +224,10 @@ public final class BluetoothA2dp implements BluetoothProfile { if (VDBG) Log.d(TAG, "Unbinding service..."); try { mServiceLock.writeLock().lock(); - mService = null; - mContext.unbindService(mConnection); + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } } catch (Exception re) { Log.e(TAG, "", re); } finally { diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 0ca39f169a7..289e769a99e 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -232,8 +232,10 @@ public final class BluetoothHidHost implements BluetoothProfile { if (VDBG) Log.d(TAG, "Unbinding service..."); synchronized (mConnection) { try { - mService = null; - mContext.unbindService(mConnection); + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } } catch (Exception re) { Log.e(TAG, "", re); } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index ae264e19bb7..b303c34ee0a 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -120,8 +120,10 @@ public class BluetoothPbap implements BluetoothProfile { log("Unbinding service..."); synchronized (mConnection) { try { - mService = null; - mContext.unbindService(mConnection); + if (mService != null) { + mService = null; + mContext.unbindService(mConnection); + } } catch (Exception re) { Log.e(TAG, "", re); } -- GitLab From a2285ae16646af4b391212b5e0057458de42909a Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Tue, 12 Mar 2019 10:19:49 -0700 Subject: [PATCH 1017/1408] Add @NonNull annotations to LE CoC APIs Added NonNull annotations to the 4 LE Connection-oriented channel API in BluetoothDevice and BluetoothAdapter. Bug: 126701988 Bug: 126701989 Test: Compile only Change-Id: I2d4dc8fbd06e30c782123e01f8481d249e40ee02 --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 +++-- framework/java/android/bluetooth/BluetoothDevice.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ab8c196edcc..b8a741ab2b7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -19,6 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -3057,7 +3058,7 @@ public final class BluetoothAdapter { * permissions, or unable to start this CoC */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingL2capChannel() + public @NonNull BluetoothServerSocket listenUsingL2capChannel() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, @@ -3115,7 +3116,7 @@ public final class BluetoothAdapter { * permissions, or unable to start this CoC */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingInsecureL2capChannel() + public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 4d8dc35d714..c4c14d1fad5 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -2182,7 +2183,7 @@ public final class BluetoothDevice implements Parcelable { * permissions */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createL2capChannel(int psm) throws IOException { + public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "createL2capChannel: Bluetooth is not enabled"); throw new IOException(); @@ -2221,7 +2222,7 @@ public final class BluetoothDevice implements Parcelable { * permissions */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { + public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled"); throw new IOException(); -- GitLab From a76ec8b43ba67a456f5112ab372f3e573761e4fe Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Thu, 14 Mar 2019 16:18:29 -0700 Subject: [PATCH 1018/1408] Cleanup annontations for Hearing Aids Profile API Remove the @UnsupportedAppUsage and add the @IntDef for the Profile Connection States in the Hearing Aids Profile API. Also, the parent class BluetoothProfile has its Profile Connection States annontated. Bug: 128523382 Test: Compile only Change-Id: Ibd02516fa637ddb48d70a8dfacf607f047aec282 --- .../android/bluetooth/BluetoothHearingAid.java | 10 ++++------ .../java/android/bluetooth/BluetoothProfile.java | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index c5a0bbd2f80..58ff2e14fd5 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -91,7 +90,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -323,7 +321,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * {@inheritDoc} */ - @Override public @NonNull List getDevicesMatchingConnectionStates( + @Override + public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { @@ -345,7 +344,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(@NonNull BluetoothDevice device) { + public @BluetoothProfile.BtProfileState int getConnectionState( + @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { mServiceLock.readLock().lock(); @@ -385,7 +385,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -417,7 +416,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index b8670dbeada..dabe0fdac39 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -18,11 +18,14 @@ package android.bluetooth; import android.Manifest; +import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -60,6 +63,16 @@ public interface BluetoothProfile { /** The profile is in disconnecting state */ int STATE_DISCONNECTING = 3; + /** @hide */ + @IntDef({ + STATE_DISCONNECTED, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfileState {} + /** * Headset and Handsfree profile */ @@ -263,7 +276,7 @@ public interface BluetoothProfile { * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionState(BluetoothDevice device); + @BtProfileState int getConnectionState(BluetoothDevice device); /** * An interface for notifying BluetoothProfile IPC clients when they have -- GitLab From 94069066162b2bb7ed91072cd59627f9dd7f386d Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Wed, 6 Mar 2019 19:36:44 +0800 Subject: [PATCH 1019/1408] Refine Bluetooth silence mode API - Remove silence state extra data from ACTION_SILENCE_MODE_CHANGED intent - Rename getSilenceMode -> isInSilenceMode - Throw IllegalStateException if Bluetooth is not enabled Bug: 124448652 Test: runtest bluetooth Change-Id: I6a8d8d848249faaac34e87408dcf750073b03584 --- .../android/bluetooth/BluetoothDevice.java | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c4c14d1fad5..6af1096e8b3 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -535,7 +535,6 @@ public final class BluetoothDevice implements Parcelable { /** * Intent to broadcast silence mode changed. * Alway contains the extra field {@link #EXTRA_DEVICE} - * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED} * * @hide */ @@ -544,16 +543,6 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; - /** - * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent, - * contains whether device is in silence mode as boolean. - * - * @hide - */ - @SystemApi - public static final String EXTRA_SILENCE_ENABLED = - "android.bluetooth.device.extra.SILENCE_ENABLED"; - /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. * @@ -1615,7 +1604,8 @@ public final class BluetoothDevice implements Parcelable { } /** - * Set the Bluetooth device silence mode. + * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not + * be routed to the {@link BluetoothDevice} if set to {@code true}. * * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice} * is an active device (for A2DP or HFP), the active device for that profile @@ -1635,6 +1625,7 @@ public final class BluetoothDevice implements Parcelable { * * @param silence true to enter silence mode, false to exit * @return true on success, false on error. + * @throws IllegalStateException if Bluetooth is not turned ON. * @hide */ @SystemApi @@ -1642,12 +1633,9 @@ public final class BluetoothDevice implements Parcelable { public boolean setSilenceMode(boolean silence) { final IBluetooth service = sService; if (service == null) { - return false; + throw new IllegalStateException("Bluetooth is not turned ON"); } try { - if (getSilenceMode() == silence) { - return true; - } return service.setSilenceMode(this, silence); } catch (RemoteException e) { Log.e(TAG, "setSilenceMode fail", e); @@ -1656,24 +1644,25 @@ public final class BluetoothDevice implements Parcelable { } /** - * Get the device silence mode status + * Check whether the {@link BluetoothDevice} is in silence mode * *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true on device in silence mode, otherwise false. + * @throws IllegalStateException if Bluetooth is not turned ON. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean getSilenceMode() { + public boolean isInSilenceMode() { final IBluetooth service = sService; if (service == null) { - return false; + throw new IllegalStateException("Bluetooth is not turned ON"); } try { return service.getSilenceMode(this); } catch (RemoteException e) { - Log.e(TAG, "getSilenceMode fail", e); + Log.e(TAG, "isInSilenceMode fail", e); return false; } } -- GitLab From 846ba92a2be8416f55a88a598471f7852ca98e4f Mon Sep 17 00:00:00 2001 From: Sasha Smundak Date: Tue, 26 Mar 2019 16:20:32 -0700 Subject: [PATCH 1020/1408] Convert core/tests/**/Android.mk file to Android.bp See build/soong/README.md for more information. Bug: 122332340 Test: treehugger Change-Id: I368327d0f3b4bd3068e10c8750ece45e227531cf --- framework/tests/Android.bp | 12 ++++++++++++ framework/tests/Android.mk | 19 ------------------- 2 files changed, 12 insertions(+), 19 deletions(-) create mode 100644 framework/tests/Android.bp delete mode 100644 framework/tests/Android.mk diff --git a/framework/tests/Android.bp b/framework/tests/Android.bp new file mode 100644 index 00000000000..4b6f9db33af --- /dev/null +++ b/framework/tests/Android.bp @@ -0,0 +1,12 @@ +android_test { + name: "BluetoothTests", + // Include all test java files. + srcs: ["src/**/*.java"], + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: ["junit"], + platform_apis: true, + certificate: "platform", +} diff --git a/framework/tests/Android.mk b/framework/tests/Android.mk deleted file mode 100644 index bb4e302b75c..00000000000 --- a/framework/tests/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := junit -LOCAL_PACKAGE_NAME := BluetoothTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) -- GitLab From 876095721954013abe4d33a359fcae34d8d7f2aa Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 5 Mar 2019 16:20:27 +0800 Subject: [PATCH 1021/1408] Refine Bluetooth Metadata API - Modify MetadataListener to as an interface and rename it to OnMetadataChangedListener - Fix typo UNTHETHERED -> UNTETHERED - Add NonNull annotation for metadata API parameters - Re-design metadata unregister API - Change metadata type to byte array Bug: 124448651 Bug: 126701203 Bug: 126699213 Test: build pass Change-Id: I79460071c7693f648e92cf849738c24f8bc269d9 Merged-In: I79460071c7693f648e92cf849738c24f8bc269d9 --- .../android/bluetooth/BluetoothAdapter.java | 116 ++++++++++-------- .../android/bluetooth/BluetoothDevice.java | 67 ++++++---- 2 files changed, 108 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b8a741ab2b7..31bbd16497c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,6 +20,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -37,7 +38,6 @@ import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -61,6 +61,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -650,7 +651,7 @@ public final class BluetoothAdapter { private final Object mLock = new Object(); private final Map mLeScanClients; - private static final Map>> + private static final Map>> sMetadataListeners = new HashMap<>(); /** @@ -660,14 +661,15 @@ public final class BluetoothAdapter { private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override - public void onMetadataChanged(BluetoothDevice device, int key, String value) { + public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { synchronized (sMetadataListeners) { if (sMetadataListeners.containsKey(device)) { - List> list = sMetadataListeners.get(device); - for (Pair pair : list) { - MetadataListener listener = pair.first; - Handler handler = pair.second; - handler.post(() -> { + List> list = + sMetadataListeners.get(device); + for (Pair pair : list) { + OnMetadataChangedListener listener = pair.first; + Executor executor = pair.second; + executor.execute(() -> { listener.onMetadataChanged(device, key, value); }); } @@ -3153,30 +3155,30 @@ public final class BluetoothAdapter { } /** - * Register a {@link #MetadataListener} to receive update about metadata + * Register a {@link #OnMetadataChangedListener} to receive update about metadata * changes for this {@link BluetoothDevice}. * Registration must be done when Bluetooth is ON and will last until - * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth + * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth * restarted in the middle. * All input parameters should not be null or {@link NullPointerException} will be triggered. - * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered - * once, double registration would cause {@link IllegalArgumentException}. + * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be + * registered once, double registration would cause {@link IllegalArgumentException}. * * @param device {@link BluetoothDevice} that will be registered - * @param listener {@link #MetadataListener} that will receive asynchronous callbacks - * @param handler the handler for listener callback + * @param executor the executor for listener callback + * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks * @return true on success, false on error - * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} + * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor} * is null. - * @throws IllegalArgumentException The same {@link #MetadataListener} and + * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and * {@link BluetoothDevice} are registered twice. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener, - Handler handler) { - if (DBG) Log.d(TAG, "registerMetdataListener()"); + public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); final IBluetooth service = mService; if (service == null) { @@ -3189,14 +3191,15 @@ public final class BluetoothAdapter { if (device == null) { throw new NullPointerException("device is null"); } - if (handler == null) { - throw new NullPointerException("handler is null"); + if (executor == null) { + throw new NullPointerException("executor is null"); } synchronized (sMetadataListeners) { - List> listenerList = sMetadataListeners.get(device); + List> listenerList = + sMetadataListeners.get(device); if (listenerList == null) { - // Create new listener/handler list for registeration + // Create new listener/executor list for registeration listenerList = new ArrayList<>(); sMetadataListeners.put(device, listenerList); } else { @@ -3207,7 +3210,7 @@ public final class BluetoothAdapter { } } - Pair listenerPair = new Pair(listener, handler); + Pair listenerPair = new Pair(listener, executor); listenerList.add(listenerPair); boolean ret = false; @@ -3230,63 +3233,74 @@ public final class BluetoothAdapter { } /** - * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}. + * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}. * Unregistration can be done when Bluetooth is either ON or OFF. - * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must - * be called before unregisteration. - * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}. + * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)} + * must be called before unregisteration. * - * @param device {@link BluetoothDevice} that will be unregistered. it + * @param device {@link BluetoothDevice} that will be unregistered. It + * should not be null or {@link NullPointerException} will be triggered. + * @param listener {@link OnMetadataChangedListener} that will be unregistered. It * should not be null or {@link NullPointerException} will be triggered. * @return true on success, false on error - * @throws NullPointerException If {@code device} is null. + * @throws NullPointerException If {@code listener} or {@code device} is null. * @throws IllegalArgumentException If {@code device} has not been registered before. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean unregisterMetadataListener(BluetoothDevice device) { - if (DBG) Log.d(TAG, "unregisterMetdataListener()"); + public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); if (device == null) { throw new NullPointerException("device is null"); } + if (listener == null) { + throw new NullPointerException("listener is null"); + } synchronized (sMetadataListeners) { - if (sMetadataListeners.containsKey(device)) { - sMetadataListeners.remove(device); - } else { + if (!sMetadataListeners.containsKey(device)) { throw new IllegalArgumentException("device was not registered"); } + // Remove issued listener from the registered device + sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - final IBluetooth service = mService; - if (service == null) { - // Bluetooth is OFF, do nothing to Bluetooth service. - return true; - } - try { - return service.unregisterMetadataListener(device); - } catch (RemoteException e) { - Log.e(TAG, "unregisterMetadataListener fail", e); - return false; + if (sMetadataListeners.get(device).isEmpty()) { + // Unregister to Bluetooth service if all listeners are removed from + // the registered device + sMetadataListeners.remove(device); + final IBluetooth service = mService; + if (service == null) { + // Bluetooth is OFF, do nothing to Bluetooth service. + return true; + } + try { + return service.unregisterMetadataListener(device); + } catch (RemoteException e) { + Log.e(TAG, "unregisterMetadataListener fail", e); + return false; + } } } + return true; } /** - * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. + * This interface is used to implement {@link BluetoothAdapter} metadata listener. * @hide */ @SystemApi - public abstract static class MetadataListener { + public interface OnMetadataChangedListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in - * {@link #registerMetadataListener}. + * {@link #addOnMetadataChangedListener}. * * @param device changed {@link BluetoothDevice}. * @param key changed metadata key, one of BluetoothDevice.METADATA_*. - * @param value the new value of metadata. + * @param value the new value of metadata as byte array. */ - public void onMetadataChanged(BluetoothDevice device, int key, String value) { - } + void onMetadataChanged(@NonNull BluetoothDevice device, int key, + @Nullable byte[] value); } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6af1096e8b3..1e128010596 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -351,6 +352,7 @@ public final class BluetoothDevice implements Parcelable { /** * Manufacturer name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -358,6 +360,7 @@ public final class BluetoothDevice implements Parcelable { /** * Model name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -365,6 +368,7 @@ public final class BluetoothDevice implements Parcelable { /** * Software version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -372,6 +376,7 @@ public final class BluetoothDevice implements Parcelable { /** * Hardware version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -379,6 +384,7 @@ public final class BluetoothDevice implements Parcelable { /** * Package name of the companion app, if any + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -386,6 +392,7 @@ public final class BluetoothDevice implements Parcelable { /** * URI to the main icon shown on the settings UI + * Data type should be {@link Byte} array. * @hide */ @SystemApi @@ -393,80 +400,91 @@ public final class BluetoothDevice implements Parcelable { /** * Whether this device is an untethered headset with left, right and case + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; + public static final int METADATA_IS_UNTETHERED_HEADSET = 6; /** * URI to icon of the left headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; + public static final int METADATA_UNTETHERED_LEFT_ICON = 7; /** * URI to icon of the right headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; + public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; /** * URI to icon of the headset charging case + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_ICON = 9; + public static final int METADATA_UNTETHERED_CASE_ICON = 9; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the left headset + * Battery level of left headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; + public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the right headset + * Battery level of rigth headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; + public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the headset charging case + * Battery level of the headset charging case + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; + public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; /** * Whether the left headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; + public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; /** * Whether the right headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; + public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; /** * Whether the headset charging case is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; + public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; /** - * URI to the enhanced settings UI slice, null or empty String means - * the UI does not exist + * URI to the enhanced settings UI slice + * Data type should be {@String} as {@link Byte} array, null means + * the UI does not exist. * @hide */ @SystemApi @@ -2243,21 +2261,21 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}. * * @param key must be within the list of BluetoothDevice.METADATA_* - * @param value the string data to set for key. Must be less than + * @param value a byte array data to set for key. Must be less than * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length * @return true on success, false on error * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setMetadata(int key, String value) { + public boolean setMetadata(int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); return false; } - if (value.length() > METADATA_MAX_LENGTH) { - throw new IllegalArgumentException("value length is " + value.length() + if (value.length > METADATA_MAX_LENGTH) { + throw new IllegalArgumentException("value length is " + value.length + ", should not over " + METADATA_MAX_LENGTH); } try { @@ -2272,12 +2290,13 @@ public final class BluetoothDevice implements Parcelable { * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} * * @param key must be within the list of BluetoothDevice.METADATA_* - * @return Metadata of the key as string, null on error or not found + * @return Metadata of the key as byte array, null on error or not found * @hide */ @SystemApi + @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public String getMetadata(int key) { + public byte[] getMetadata(int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); -- GitLab From f2d49bda6a69f74226c6e364a3eed5707f2cfd20 Mon Sep 17 00:00:00 2001 From: Jack He Date: Thu, 28 Mar 2019 17:42:26 -0700 Subject: [PATCH 1022/1408] BluetoothHealth: hide auto-created default constructors Fixes: 123926561 Test: make Change-Id: I388472c82eaca245285b5ecf2959c415508d7e69 --- framework/java/android/bluetooth/BluetoothHealth.java | 5 +++++ .../android/bluetooth/BluetoothHealthAppConfiguration.java | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index e2e56fd02ab..5fd60e00169 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -99,6 +99,11 @@ public final class BluetoothHealth implements BluetoothProfile { @Deprecated public static final int CHANNEL_TYPE_STREAMING = 11; + /** + * Hide auto-created default constructor + * @hide + */ + BluetoothHealth() {} /** * Register an application configuration that acts as a Health SINK. diff --git a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 9788bbf74e3..e960ed64dcb 100644 --- a/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/framework/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -33,6 +33,13 @@ import android.os.Parcelable; */ @Deprecated public final class BluetoothHealthAppConfiguration implements Parcelable { + + /** + * Hide auto-created default constructor + * @hide + */ + BluetoothHealthAppConfiguration() {} + @Override public int describeContents() { return 0; -- GitLab From 35fd9fb29d6c3ae9115613bb193f0972b3fb98f4 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 5 Mar 2019 16:20:27 +0800 Subject: [PATCH 1023/1408] Refine Bluetooth Metadata API - Modify MetadataListener to as an interface and rename it to OnMetadataChangedListener - Fix typo UNTHETHERED -> UNTETHERED - Add NonNull annotation for metadata API parameters - Re-design metadata unregister API - Change metadata type to byte array Bug: 124448651 Bug: 126701203 Bug: 126699213 Test: build pass Change-Id: I79460071c7693f648e92cf849738c24f8bc269d9 --- .../android/bluetooth/BluetoothAdapter.java | 116 ++++++++++-------- .../android/bluetooth/BluetoothDevice.java | 67 ++++++---- 2 files changed, 108 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b8a741ab2b7..31bbd16497c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -20,6 +20,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -37,7 +38,6 @@ import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -61,6 +61,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -650,7 +651,7 @@ public final class BluetoothAdapter { private final Object mLock = new Object(); private final Map mLeScanClients; - private static final Map>> + private static final Map>> sMetadataListeners = new HashMap<>(); /** @@ -660,14 +661,15 @@ public final class BluetoothAdapter { private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override - public void onMetadataChanged(BluetoothDevice device, int key, String value) { + public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { synchronized (sMetadataListeners) { if (sMetadataListeners.containsKey(device)) { - List> list = sMetadataListeners.get(device); - for (Pair pair : list) { - MetadataListener listener = pair.first; - Handler handler = pair.second; - handler.post(() -> { + List> list = + sMetadataListeners.get(device); + for (Pair pair : list) { + OnMetadataChangedListener listener = pair.first; + Executor executor = pair.second; + executor.execute(() -> { listener.onMetadataChanged(device, key, value); }); } @@ -3153,30 +3155,30 @@ public final class BluetoothAdapter { } /** - * Register a {@link #MetadataListener} to receive update about metadata + * Register a {@link #OnMetadataChangedListener} to receive update about metadata * changes for this {@link BluetoothDevice}. * Registration must be done when Bluetooth is ON and will last until - * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth + * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth * restarted in the middle. * All input parameters should not be null or {@link NullPointerException} will be triggered. - * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered - * once, double registration would cause {@link IllegalArgumentException}. + * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be + * registered once, double registration would cause {@link IllegalArgumentException}. * * @param device {@link BluetoothDevice} that will be registered - * @param listener {@link #MetadataListener} that will receive asynchronous callbacks - * @param handler the handler for listener callback + * @param executor the executor for listener callback + * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks * @return true on success, false on error - * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} + * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor} * is null. - * @throws IllegalArgumentException The same {@link #MetadataListener} and + * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and * {@link BluetoothDevice} are registered twice. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener, - Handler handler) { - if (DBG) Log.d(TAG, "registerMetdataListener()"); + public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); final IBluetooth service = mService; if (service == null) { @@ -3189,14 +3191,15 @@ public final class BluetoothAdapter { if (device == null) { throw new NullPointerException("device is null"); } - if (handler == null) { - throw new NullPointerException("handler is null"); + if (executor == null) { + throw new NullPointerException("executor is null"); } synchronized (sMetadataListeners) { - List> listenerList = sMetadataListeners.get(device); + List> listenerList = + sMetadataListeners.get(device); if (listenerList == null) { - // Create new listener/handler list for registeration + // Create new listener/executor list for registeration listenerList = new ArrayList<>(); sMetadataListeners.put(device, listenerList); } else { @@ -3207,7 +3210,7 @@ public final class BluetoothAdapter { } } - Pair listenerPair = new Pair(listener, handler); + Pair listenerPair = new Pair(listener, executor); listenerList.add(listenerPair); boolean ret = false; @@ -3230,63 +3233,74 @@ public final class BluetoothAdapter { } /** - * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}. + * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}. * Unregistration can be done when Bluetooth is either ON or OFF. - * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must - * be called before unregisteration. - * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}. + * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)} + * must be called before unregisteration. * - * @param device {@link BluetoothDevice} that will be unregistered. it + * @param device {@link BluetoothDevice} that will be unregistered. It + * should not be null or {@link NullPointerException} will be triggered. + * @param listener {@link OnMetadataChangedListener} that will be unregistered. It * should not be null or {@link NullPointerException} will be triggered. * @return true on success, false on error - * @throws NullPointerException If {@code device} is null. + * @throws NullPointerException If {@code listener} or {@code device} is null. * @throws IllegalArgumentException If {@code device} has not been registered before. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean unregisterMetadataListener(BluetoothDevice device) { - if (DBG) Log.d(TAG, "unregisterMetdataListener()"); + public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); if (device == null) { throw new NullPointerException("device is null"); } + if (listener == null) { + throw new NullPointerException("listener is null"); + } synchronized (sMetadataListeners) { - if (sMetadataListeners.containsKey(device)) { - sMetadataListeners.remove(device); - } else { + if (!sMetadataListeners.containsKey(device)) { throw new IllegalArgumentException("device was not registered"); } + // Remove issued listener from the registered device + sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - final IBluetooth service = mService; - if (service == null) { - // Bluetooth is OFF, do nothing to Bluetooth service. - return true; - } - try { - return service.unregisterMetadataListener(device); - } catch (RemoteException e) { - Log.e(TAG, "unregisterMetadataListener fail", e); - return false; + if (sMetadataListeners.get(device).isEmpty()) { + // Unregister to Bluetooth service if all listeners are removed from + // the registered device + sMetadataListeners.remove(device); + final IBluetooth service = mService; + if (service == null) { + // Bluetooth is OFF, do nothing to Bluetooth service. + return true; + } + try { + return service.unregisterMetadataListener(device); + } catch (RemoteException e) { + Log.e(TAG, "unregisterMetadataListener fail", e); + return false; + } } } + return true; } /** - * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. + * This interface is used to implement {@link BluetoothAdapter} metadata listener. * @hide */ @SystemApi - public abstract static class MetadataListener { + public interface OnMetadataChangedListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in - * {@link #registerMetadataListener}. + * {@link #addOnMetadataChangedListener}. * * @param device changed {@link BluetoothDevice}. * @param key changed metadata key, one of BluetoothDevice.METADATA_*. - * @param value the new value of metadata. + * @param value the new value of metadata as byte array. */ - public void onMetadataChanged(BluetoothDevice device, int key, String value) { - } + void onMetadataChanged(@NonNull BluetoothDevice device, int key, + @Nullable byte[] value); } } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 204d7e3ceca..74ceeb92f75 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -351,6 +352,7 @@ public final class BluetoothDevice implements Parcelable { /** * Manufacturer name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -358,6 +360,7 @@ public final class BluetoothDevice implements Parcelable { /** * Model name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -365,6 +368,7 @@ public final class BluetoothDevice implements Parcelable { /** * Software version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -372,6 +376,7 @@ public final class BluetoothDevice implements Parcelable { /** * Hardware version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -379,6 +384,7 @@ public final class BluetoothDevice implements Parcelable { /** * Package name of the companion app, if any + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -386,6 +392,7 @@ public final class BluetoothDevice implements Parcelable { /** * URI to the main icon shown on the settings UI + * Data type should be {@link Byte} array. * @hide */ @SystemApi @@ -393,80 +400,91 @@ public final class BluetoothDevice implements Parcelable { /** * Whether this device is an untethered headset with left, right and case + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; + public static final int METADATA_IS_UNTETHERED_HEADSET = 6; /** * URI to icon of the left headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; + public static final int METADATA_UNTETHERED_LEFT_ICON = 7; /** * URI to icon of the right headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; + public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; /** * URI to icon of the headset charging case + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_ICON = 9; + public static final int METADATA_UNTETHERED_CASE_ICON = 9; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the left headset + * Battery level of left headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; + public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the right headset + * Battery level of rigth headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; + public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the headset charging case + * Battery level of the headset charging case + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; + public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; /** * Whether the left headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; + public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; /** * Whether the right headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; + public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; /** * Whether the headset charging case is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; + public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; /** - * URI to the enhanced settings UI slice, null or empty String means - * the UI does not exist + * URI to the enhanced settings UI slice + * Data type should be {@String} as {@link Byte} array, null means + * the UI does not exist. * @hide */ @SystemApi @@ -2243,21 +2261,21 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}. * * @param key must be within the list of BluetoothDevice.METADATA_* - * @param value the string data to set for key. Must be less than + * @param value a byte array data to set for key. Must be less than * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length * @return true on success, false on error * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setMetadata(int key, String value) { + public boolean setMetadata(int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); return false; } - if (value.length() > METADATA_MAX_LENGTH) { - throw new IllegalArgumentException("value length is " + value.length() + if (value.length > METADATA_MAX_LENGTH) { + throw new IllegalArgumentException("value length is " + value.length + ", should not over " + METADATA_MAX_LENGTH); } try { @@ -2272,12 +2290,13 @@ public final class BluetoothDevice implements Parcelable { * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} * * @param key must be within the list of BluetoothDevice.METADATA_* - * @return Metadata of the key as string, null on error or not found + * @return Metadata of the key as byte array, null on error or not found * @hide */ @SystemApi + @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public String getMetadata(int key) { + public byte[] getMetadata(int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); -- GitLab From e8099b907f6c959b21e2f269cc000fb990b49479 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 10 Apr 2019 18:48:57 +0200 Subject: [PATCH 1024/1408] Bluetooth: Make ScanRecord.getServiceSolicitationUuids NonNull Test: compilation Bug: 115639878 Change-Id: I5f895d65a7036275ab0faa0afd2ee1eebc416476 --- framework/java/android/bluetooth/le/ScanRecord.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 2174255a361..30868bfeac6 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; @@ -97,7 +98,7 @@ public final class ScanRecord { * Returns a list of service solicitation UUIDs within the advertisement that are used to * identify the Bluetooth GATT services. */ - @Nullable + @NonNull public List getServiceSolicitationUuids() { return mServiceSolicitationUuids; } @@ -297,9 +298,6 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - if (serviceSolicitationUuids.isEmpty()) { - serviceSolicitationUuids = null; - } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); } catch (Exception e) { -- GitLab From 4aec35339fd013301d762d9be4bcfd2eb2350eaa Mon Sep 17 00:00:00 2001 From: Ying Zheng Date: Wed, 10 Apr 2019 16:34:54 -0700 Subject: [PATCH 1025/1408] Migrate to the new ro property for headless multiuser check. Test: build Bug: 129549399 Change-Id: I42ab7daa80da62e3d754e61721b5d7297f376419 --- .../com/android/server/bluetooth/BluetoothService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 6018f008054..5c5b477352b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -18,11 +18,10 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; -import android.os.SystemProperties; -class BluetoothService extends SystemService { - private static final String HEADLESS_SYSTEM_USER = "android.car.systemuser.headless"; +import com.android.internal.os.RoSystemProperties; +class BluetoothService extends SystemService { private BluetoothManagerService mBluetoothManagerService; private boolean mInitialized = false; @@ -48,7 +47,7 @@ class BluetoothService extends SystemService { publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && - !SystemProperties.getBoolean(HEADLESS_SYSTEM_USER, false)) { + !RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER) { initialize(); } } -- GitLab From 3aa97335ffa331580b1d24dcf2fc21290f124596 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 10 Apr 2019 18:48:57 +0200 Subject: [PATCH 1026/1408] Bluetooth: Make ScanRecord.getServiceSolicitationUuids NonNull Test: compilation Bug: 115639878 Merged-In: I5f895d65a7036275ab0faa0afd2ee1eebc416476 Change-Id: I5f895d65a7036275ab0faa0afd2ee1eebc416476 --- framework/java/android/bluetooth/le/ScanRecord.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 2174255a361..30868bfeac6 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; @@ -97,7 +98,7 @@ public final class ScanRecord { * Returns a list of service solicitation UUIDs within the advertisement that are used to * identify the Bluetooth GATT services. */ - @Nullable + @NonNull public List getServiceSolicitationUuids() { return mServiceSolicitationUuids; } @@ -297,9 +298,6 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - if (serviceSolicitationUuids.isEmpty()) { - serviceSolicitationUuids = null; - } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); } catch (Exception e) { -- GitLab From 0f672c0a9cd59be53ad00c71256a13eb90913815 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 10 Apr 2019 18:48:57 +0200 Subject: [PATCH 1027/1408] Bluetooth: Make ScanRecord.getServiceSolicitationUuids NonNull Test: compilation Bug: 115639878 Merged-In: I5f895d65a7036275ab0faa0afd2ee1eebc416476 Change-Id: I5f895d65a7036275ab0faa0afd2ee1eebc416476 --- framework/java/android/bluetooth/le/ScanRecord.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 2174255a361..30868bfeac6 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; @@ -97,7 +98,7 @@ public final class ScanRecord { * Returns a list of service solicitation UUIDs within the advertisement that are used to * identify the Bluetooth GATT services. */ - @Nullable + @NonNull public List getServiceSolicitationUuids() { return mServiceSolicitationUuids; } @@ -297,9 +298,6 @@ public final class ScanRecord { if (serviceUuids.isEmpty()) { serviceUuids = null; } - if (serviceSolicitationUuids.isEmpty()) { - serviceSolicitationUuids = null; - } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); } catch (Exception e) { -- GitLab From 24966886c65d1a209d3a7976060ed396263bbeda Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Fri, 19 Apr 2019 14:13:03 -0700 Subject: [PATCH 1028/1408] Add nullability annotations to android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid Test: compilation Fixes: 126698824 Change-Id: I4873b46a37df3e103ddc62001cc9f0bc01aea4dc --- framework/java/android/bluetooth/le/ScanFilter.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index c5d435b7613..66d1da8909c 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -586,7 +587,8 @@ public final class ScanFilter implements Parcelable { /** * Set filter on service solicitation uuid. */ - public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) { + public @NonNull Builder setServiceSolicitationUuid( + @Nullable ParcelUuid serviceSolicitationUuid) { mServiceSolicitationUuid = serviceSolicitationUuid; return this; } @@ -601,8 +603,9 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but * {@code serviceSolicitationUuidMask} is not {@code null}. */ - public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid, - ParcelUuid solicitationUuidMask) { + public @NonNull Builder setServiceSolicitationUuid( + @Nullable ParcelUuid serviceSolicitationUuid, + @Nullable ParcelUuid solicitationUuidMask) { if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) { throw new IllegalArgumentException( "SolicitationUuid is null while SolicitationUuidMask is not null!"); -- GitLab From dd916b8166b3448811ec9d56dbf1e5fbcc1a3da9 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Fri, 19 Apr 2019 14:13:03 -0700 Subject: [PATCH 1029/1408] Add nullability annotations to android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid Test: compilation Fixes: 126698824 Change-Id: I4873b46a37df3e103ddc62001cc9f0bc01aea4dc --- framework/java/android/bluetooth/le/ScanFilter.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 78140cf65d0..07ba2c6680a 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -586,7 +587,8 @@ public final class ScanFilter implements Parcelable { /** * Set filter on service solicitation uuid. */ - public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) { + public @NonNull Builder setServiceSolicitationUuid( + @Nullable ParcelUuid serviceSolicitationUuid) { mServiceSolicitationUuid = serviceSolicitationUuid; return this; } @@ -601,8 +603,9 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but * {@code serviceSolicitationUuidMask} is not {@code null}. */ - public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid, - ParcelUuid solicitationUuidMask) { + public @NonNull Builder setServiceSolicitationUuid( + @Nullable ParcelUuid serviceSolicitationUuid, + @Nullable ParcelUuid solicitationUuidMask) { if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) { throw new IllegalArgumentException( "SolicitationUuid is null while SolicitationUuidMask is not null!"); -- GitLab From 3b28545bacc130289331e3aa6b73124fb547f990 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 19 Apr 2019 14:27:09 -0700 Subject: [PATCH 1030/1408] Add more documentation for LE CoC Added more information into Android SDK for LE Connection-oriented Channels (CoC) so that it matches the corresponding RFComm documentation. Bug: 70683224 Test: Compile Change-Id: I40abde70a7ca6bcd194ee75bd8367c0ed9e97d05 --- .../android/bluetooth/BluetoothDevice.java | 3 +- .../bluetooth/BluetoothServerSocket.java | 35 +++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6af1096e8b3..a2e8e5fdae3 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -55,7 +55,8 @@ import java.util.UUID; * returned by {@link BluetoothAdapter#getBondedDevices() * BluetoothAdapter.getBondedDevices()}. You can then open a * {@link BluetoothSocket} for communication with the remote device, using - * {@link #createRfcommSocketToServiceRecord(UUID)}. + * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using + * {@link #createL2capChannel(int)} over Bluetooth LE. * *

          Note: * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 4e886250b4f..c06b837a9ee 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -35,21 +35,28 @@ import java.io.IOException; * On the client side, use a single {@link BluetoothSocket} to both initiate * an outgoing connection and to manage the connection. * - *

          The most common type of Bluetooth socket is RFCOMM, which is the type - * supported by the Android APIs. RFCOMM is a connection-oriented, streaming - * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). + *

          For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by + * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It + * is also known as the Serial Port Profile (SPP). To create a listening + * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord + * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. * - *

          To create a listening {@link BluetoothServerSocket} that's ready for - * incoming connections, use - * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord - * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call - * {@link #accept()} to listen for incoming connection requests. This call - * will block until a connection is established, at which point, it will return - * a {@link BluetoothSocket} to manage the connection. Once the {@link - * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on - * the {@link BluetoothServerSocket} when it's no longer needed for accepting - * connections. Closing the {@link BluetoothServerSocket} will not - * close the returned {@link BluetoothSocket}. + *

          For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a + * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control. + * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel + * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket} + * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()} + * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your + * socket. + * + *

          After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to + * listen for incoming connection requests. This call will block until a connection is established, + * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the + * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link + * BluetoothServerSocket} when it's no longer needed for accepting + * connections. Closing the {@link BluetoothServerSocket} will not close the returned + * {@link BluetoothSocket}. * *

          {@link BluetoothServerSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing -- GitLab From 11b45bb70b048c6a3ed91e5eaf93b80df618c8ff Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 23 Apr 2019 20:00:18 -0700 Subject: [PATCH 1031/1408] Bluetooth: Check if packageName belongs to callingUid in binder calls * Several Bluetooth APIs take packageName parameter from Apps, which is resolved through ActivityThread.currentPackageName() * Add checks to make sure that packageName passed through binder calls actually belongs to the calling uid Bug: 120574260 Test: make, enable/disable Bluetooth Change-Id: If6593cf8cbab91eb27b9f22b635f984c04ec2de2 --- .../bluetooth/BluetoothManagerService.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c4bc52c6f4d..e88568424f1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -19,6 +19,7 @@ package com.android.server; import android.Manifest; import android.app.ActivityManager; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; @@ -210,6 +211,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mIsHearingAidProfileSupported; + private AppOpsManager mAppOps; + // Save a ProfileServiceConnections object for each of the bound // bluetooth profile services private final Map mProfileServices = new HashMap<>(); @@ -742,6 +745,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public int updateBleAppCount(IBinder token, boolean enable, String packageName) { + // Check if packageName belongs to callingUid + final int callingUid = Binder.getCallingUid(); + final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!isCallerSystem) { + checkPackage(callingUid, packageName); + } ClientDeathRecipient r = mBleApps.get(token); if (r == null && enable) { ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); @@ -857,6 +866,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + // Check if packageName belongs to callingUid + final int callingUid = Binder.getCallingUid(); + final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!isCallerSystem) { + checkPackage(callingUid, packageName); + } + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); @@ -864,7 +880,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding); } - int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); + int callingAppId = UserHandle.getAppId(callingUid); if (callingAppId != Process.NFC_UID) { throw new SecurityException("no permission to enable Bluetooth quietly"); @@ -891,6 +907,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (!callerSystem) { + // Check if packageName belongs to callingUid + checkPackage(callingUid, packageName); + if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "enable(): not allowed for non-active and non system user"); return false; @@ -928,6 +947,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; if (!callerSystem) { + // Check if packageName belongs to callingUid + checkPackage(callingUid, packageName); + if (!checkIfCallerIsForegroundUser()) { Slog.w(TAG, "disable(): not allowed for non-active and non system user"); return false; @@ -990,6 +1012,30 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Check if AppOpsManager is available and the packageName belongs to uid + * + * A null package belongs to any uid + */ + private void checkPackage(int uid, String packageName) { + if (mAppOps == null) { + Slog.w(TAG, "checkPackage(): called before system boot up, uid " + + uid + ", packageName " + packageName); + throw new IllegalStateException("System has not boot yet"); + } + if (packageName == null) { + Slog.w(TAG, "checkPackage(): called with null packageName from " + uid); + return; + } + try { + mAppOps.checkPackage(uid, packageName); + } catch (SecurityException e) { + Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid); + android.util.EventLog.writeEvent(0x534e4554, "120574260", uid, ""); + throw new SecurityException(e.getMessage()); + } + } + /** * Check if the caller must still pass permission check or if the caller is exempted * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check. @@ -1122,6 +1168,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Bluetooth boot completed"); } + mAppOps = mContext.getSystemService(AppOpsManager.class); UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); -- GitLab From 1fdd082227fa47769e90d5d41cda93ba7aee2e96 Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Fri, 19 Apr 2019 14:27:09 -0700 Subject: [PATCH 1032/1408] Add more documentation for LE CoC Added more information into Android SDK for LE Connection-oriented Channels (CoC) so that it matches the corresponding RFComm documentation. Bug: 70683224 Test: Compile Change-Id: I40abde70a7ca6bcd194ee75bd8367c0ed9e97d05 (cherry picked from commit bcbc1302351cbba2f786384293271fdc4289678f) Merged-In: I40abde70a7ca6bcd194ee75bd8367c0ed9e97d05 --- .../android/bluetooth/BluetoothDevice.java | 3 +- .../bluetooth/BluetoothServerSocket.java | 35 +++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 74ceeb92f75..388161d7630 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -56,7 +56,8 @@ import java.util.UUID; * returned by {@link BluetoothAdapter#getBondedDevices() * BluetoothAdapter.getBondedDevices()}. You can then open a * {@link BluetoothSocket} for communication with the remote device, using - * {@link #createRfcommSocketToServiceRecord(UUID)}. + * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using + * {@link #createL2capChannel(int)} over Bluetooth LE. * *

          Note: * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 4e886250b4f..c06b837a9ee 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -35,21 +35,28 @@ import java.io.IOException; * On the client side, use a single {@link BluetoothSocket} to both initiate * an outgoing connection and to manage the connection. * - *

          The most common type of Bluetooth socket is RFCOMM, which is the type - * supported by the Android APIs. RFCOMM is a connection-oriented, streaming - * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). + *

          For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by + * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It + * is also known as the Serial Port Profile (SPP). To create a listening + * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord + * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. * - *

          To create a listening {@link BluetoothServerSocket} that's ready for - * incoming connections, use - * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord - * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call - * {@link #accept()} to listen for incoming connection requests. This call - * will block until a connection is established, at which point, it will return - * a {@link BluetoothSocket} to manage the connection. Once the {@link - * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on - * the {@link BluetoothServerSocket} when it's no longer needed for accepting - * connections. Closing the {@link BluetoothServerSocket} will not - * close the returned {@link BluetoothSocket}. + *

          For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a + * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control. + * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel + * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket} + * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()} + * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your + * socket. + * + *

          After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to + * listen for incoming connection requests. This call will block until a connection is established, + * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the + * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link + * BluetoothServerSocket} when it's no longer needed for accepting + * connections. Closing the {@link BluetoothServerSocket} will not close the returned + * {@link BluetoothSocket}. * *

          {@link BluetoothServerSocket} is thread * safe. In particular, {@link #close} will always immediately abort ongoing -- GitLab From 4e34f0d8884bb32936bc30e2446f6c8c0a5b7560 Mon Sep 17 00:00:00 2001 From: Jack He Date: Wed, 24 Apr 2019 16:21:12 -0700 Subject: [PATCH 1033/1408] Bluetooth: Remove safetynet logging for 120574260 Bug: 120574260 Test: make Change-Id: Id86c139e654428f8d4376f2d0359329e47c052b9 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index e88568424f1..6573c3bf797 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1031,7 +1031,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mAppOps.checkPackage(uid, packageName); } catch (SecurityException e) { Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid); - android.util.EventLog.writeEvent(0x534e4554, "120574260", uid, ""); throw new SecurityException(e.getMessage()); } } -- GitLab From 1652b94a121a19863c554551c57406cbef504ce8 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 26 Mar 2019 21:38:08 +0800 Subject: [PATCH 1034/1408] 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/BluetoothA2dp.java | 313 +++++------------- .../android/bluetooth/BluetoothA2dpSink.java | 128 ++----- .../bluetooth/BluetoothAvrcpController.java | 129 ++------ .../android/bluetooth/BluetoothHeadset.java | 40 ++- .../bluetooth/BluetoothHeadsetClient.java | 195 ++++------- .../bluetooth/BluetoothHearingAid.java | 273 ++++----------- .../android/bluetooth/BluetoothHidDevice.java | 138 ++------ .../android/bluetooth/BluetoothHidHost.java | 144 ++------ .../java/android/bluetooth/BluetoothMap.java | 128 ++----- .../android/bluetooth/BluetoothMapClient.java | 137 ++------ .../java/android/bluetooth/BluetoothPan.java | 134 ++------ .../java/android/bluetooth/BluetoothPbap.java | 86 +++-- .../bluetooth/BluetoothPbapClient.java | 135 ++------ .../bluetooth/BluetoothProfileConnector.java | 166 ++++++++++ .../java/android/bluetooth/BluetoothSap.java | 128 ++----- .../bluetooth/BluetoothManagerService.java | 16 +- 16 files changed, 681 insertions(+), 1609 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothProfileConnector.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 9faa1d675d8..e7f2d18ad6a 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -22,23 +22,16 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -209,101 +202,32 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; - - 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..."); - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", + IBluetoothA2dp.class.getName()) { + @Override + public IBluetoothA2dp getServiceInterface(IBinder service) { + return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { 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(IBluetoothA2dp.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothA2dp getService() { + return mProfileConnector.getService(); } @Override @@ -333,17 +257,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -376,17 +298,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -397,17 +317,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -418,17 +336,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -439,18 +355,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - 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; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -480,18 +394,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return mService.setActiveDevice(device); + return service.setActiveDevice(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -511,17 +423,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevice(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getActiveDevice(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -543,22 +453,20 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -578,18 +486,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -602,17 +508,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.isAvrcpAbsoluteVolumeSupported(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.isAvrcpAbsoluteVolumeSupported(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -625,15 +529,13 @@ public final class BluetoothA2dp implements BluetoothProfile { public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setAvrcpAbsoluteVolume(volume); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setAvrcpAbsoluteVolume(volume); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); } } @@ -646,18 +548,16 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isA2dpPlaying(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -694,19 +594,17 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getCodecStatus(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getCodecStatus(device); } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -723,17 +621,15 @@ public final class BluetoothA2dp implements BluetoothProfile { BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(device, codecConfig); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setCodecConfigPreference(device, codecConfig); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -772,21 +668,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(device); + service.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(device); + service.disableOptionalCodecs(device); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -801,17 +695,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.supportsOptionalCodecs(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.supportsOptionalCodecs(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -826,17 +718,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.getOptionalCodecsEnabled(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getOptionalCodecsEnabled(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_PREF_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -858,18 +748,16 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); return; } - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - mService.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -900,35 +788,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index fda2f892753..5a8055a29cf 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,10 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -124,93 +121,31 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String EXTRA_AUDIO_CONFIG = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; - - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, + "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { + @Override + public IBluetoothA2dpSink getServiceInterface(IBinder service) { + return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { 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(IBluetoothA2dpSink.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 A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - 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); - } - } - } + private IBluetoothA2dpSink getService() { + return mProfileConnector.getService(); } @Override @@ -241,7 +176,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -282,7 +217,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -301,7 +236,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -320,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -339,7 +274,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -365,7 +300,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getAudioConfig(device); @@ -395,7 +330,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -427,7 +362,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -448,7 +383,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.isA2dpPlaying(device); @@ -487,25 +422,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, - BluetoothA2dpSink.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index e7c8944788f..4e7e4415c54 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,10 +16,7 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -79,93 +76,32 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; - - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, + "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { + @Override + public IBluetoothAvrcpController getServiceInterface(IBinder service) { + return IBluetoothAvrcpController.Stub.asInterface( + Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { 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(IBluetoothAvrcpController.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 AVRCP Controller Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - 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); - } - } - } + private IBluetoothAvrcpController getService() { + return mProfileConnector.getService(); } @Override @@ -179,7 +115,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -198,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -217,7 +155,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -238,7 +177,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { settings = service.getPlayerSettings(device); @@ -256,7 +196,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.setPlayerApplicationSetting(plAppSetting); @@ -276,7 +217,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { service.sendGroupNavigationCmd(device, keyCode, keyState); @@ -289,25 +231,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, - BluetoothAvrcpController.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8d9d340ee68..9862a63ef23 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -337,19 +337,9 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -374,24 +364,32 @@ public final class BluetoothHeadset implements BluetoothProfile { doBind(); } - boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager().bindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind HeadsetService", e); + } + } } return false; } - void doUnbind() { + private void doUnbind() { synchronized (mConnection) { if (mService != null) { + if (VDBG) Log.d(TAG, "Unbinding service..."); try { mAdapter.getBluetoothManager().unbindBluetoothProfileService( BluetoothProfile.HEADSET, mConnection); } catch (RemoteException e) { Log.e(TAG, "Unable to unbind HeadsetService", e); + } finally { + mService = null; } } } @@ -411,8 +409,8 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } mServiceListener = null; @@ -1169,7 +1167,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_DISCONNECTED)); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index ec18d42698c..05833b5f571 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,10 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -366,73 +363,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, + "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override - 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..."); - Intent intent = new Intent( - IBluetoothHeadsetClient.class.getName()); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + public IBluetoothHeadsetClient getServiceInterface(IBinder service) { + return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { 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(IBluetoothHeadsetClient.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 Headset Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /** @@ -443,27 +389,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - 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; + private IBluetoothHeadsetClient getService() { + return mProfileConnector.getService(); } /** @@ -480,7 +410,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -503,7 +434,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -524,7 +456,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -547,7 +480,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -569,7 +503,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -589,7 +524,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -611,7 +547,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -637,7 +574,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.startVoiceRecognition(device); @@ -662,7 +600,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.stopVoiceRecognition(device); @@ -682,7 +621,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentCalls(device); @@ -702,7 +642,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentAgEvents(device); @@ -726,7 +667,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.acceptCall(device, flag); @@ -747,7 +689,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.holdCall(device); @@ -773,7 +716,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.rejectCall(device); @@ -803,7 +747,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.terminateCall(device, call); @@ -831,7 +776,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.enterPrivateMode(device, index); @@ -858,7 +804,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.explicitCallTransfer(device); @@ -881,7 +828,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.dial(device, number); @@ -905,7 +853,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendDTMF(device, code); @@ -931,7 +880,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getLastVoiceTagNumber(device); @@ -951,7 +901,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioState(device); @@ -974,7 +925,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { service.setAudioRouteAllowed(device, allowed); @@ -996,7 +948,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioRouteAllowed(device); @@ -1020,7 +973,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.connectAudio(device); @@ -1044,7 +998,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.disconnectAudio(device); @@ -1065,7 +1020,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getCurrentAgFeatures(device); @@ -1079,29 +1035,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return null; } - - private final ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, - BluetoothHeadsetClient.this); - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 58ff2e14fd5..60fb6fb122e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,20 +22,14 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This class provides the public APIs to control the Hearing Aid profile. @@ -128,97 +122,31 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothHearingAid mService; private BluetoothAdapter mAdapter; - - 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..."); - try { - mServiceLock.writeLock().lock(); - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, + "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { + @Override + public IBluetoothHearingAid getServiceInterface(IBinder service) { + return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - void doBind() { - Intent intent = new Intent(IBluetoothHearingAid.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 Hearing Aid Service with " + intent); - return; - } + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothHearingAid getService() { + return mProfileConnector.getService(); } /** @@ -240,18 +168,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -282,18 +208,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -303,18 +227,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @Override public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -325,18 +247,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -347,19 +267,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - 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; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -387,20 +305,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - mService.setActiveDevice(device); + service.setActiveDevice(device); return true; } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -418,18 +334,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevices(); + if (service != null && isEnabled()) { + return service.getActiveDevices(); } - 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<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -450,23 +364,21 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -484,19 +396,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -535,18 +445,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getVolume()"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getVolume(); + if (service != null && isEnabled()) { + return service.getVolume(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return 0; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return 0; - } finally { - mServiceLock.readLock().unlock(); } } @@ -566,21 +474,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void adjustVolume(int direction) { if (DBG) log("adjustVolume(" + direction + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.adjustVolume(direction); + service.adjustVolume(direction); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -593,20 +498,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.setVolume(volume); + service.setVolume(volume); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -622,21 +525,19 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getCustomerId(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return HI_SYNC_ID_INVALID; } if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return mService.getHiSyncId(device); + return service.getHiSyncId(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; - } finally { - mServiceLock.readLock().unlock(); } } @@ -652,19 +553,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceSide(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceSide(device); + return service.getDeviceSide(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return SIDE_LEFT; - } finally { - mServiceLock.readLock().unlock(); } } @@ -680,52 +579,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceMode(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceMode(device); + return service.getDeviceMode(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return MODE_MONAURAL; - } finally { - mServiceLock.readLock().unlock(); } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, - BluetoothHearingAid.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 3bc8544ebf8..e9b0be2c4cd 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -18,10 +18,8 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -327,11 +325,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHidDevice mService; - private BluetoothAdapter mAdapter; - private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { private final Executor mExecutor; @@ -385,114 +378,33 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - public void onBluetoothStateChange(boolean up) { - Log.d(TAG, "onBluetoothStateChange: up=" + up); - synchronized (mConnection) { - if (up) { - try { - if (mService == null) { - Log.d(TAG, "Binding HID Device service..."); - doBind(); - } - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } - } else { - Log.d(TAG, "Unbinding service..."); - doUnbind(); - } - } - } - }; - - private final ServiceConnection mConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected( - BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, + "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { + @Override + public IBluetoothHidDevice getServiceInterface(IBinder service) { + return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; BluetoothHidDevice(Context context, ServiceListener listener) { - mContext = context; - mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidDevice.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 HID Device Service with " + intent); - return false; - } - Log.d(TAG, "Bound to HID Device Service"); - return true; - } - - void doUnbind() { - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to unbind HidDevService", e); - } - } + mProfileConnector.connect(context, listener); } void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - doUnbind(); - } - mServiceListener = null; + private IBluetoothHidDevice getService() { + return mProfileConnector.getService(); } /** {@inheritDoc} */ @Override public List getConnectedDevices() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectedDevices(); @@ -509,7 +421,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -526,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectionState(device); @@ -583,7 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { throw new IllegalArgumentException("callback parameter cannot be null"); } - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); @@ -611,7 +523,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean unregisterApp() { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.unregisterApp(); @@ -636,7 +548,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.sendReport(device, id, data); @@ -662,7 +574,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -686,7 +598,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.reportError(device, error); @@ -707,7 +619,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public String getUserAppName() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { @@ -733,7 +645,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean connect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.connect(device); @@ -757,7 +669,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.disconnect(device); diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 289e769a99e..4afb382c066 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -18,10 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -219,97 +216,32 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothHidHost mService; - - 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 { - if (mService != null) { - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, + "BluetoothHidHost", IBluetoothHidHost.class.getName()) { + @Override + public IBluetoothHidHost getServiceInterface(IBinder service) { + return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { 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(IBluetoothHidHost.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 HID Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { if (VDBG) log("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 IBluetoothHidHost getService() { + return mProfileConnector.getService(); } /** @@ -333,7 +265,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -373,7 +305,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -392,7 +324,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -411,7 +343,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -430,7 +362,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -460,7 +392,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -492,7 +424,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -505,26 +437,6 @@ public final class BluetoothHidHost implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, - BluetoothHidHost.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } @@ -544,7 +456,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -570,7 +482,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -594,7 +506,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -625,7 +537,7 @@ public final class BluetoothHidHost implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -651,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -676,7 +588,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -700,7 +612,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -725,7 +637,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 98c23c600f1..dd2f150ad4e 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -17,10 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -44,11 +41,6 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothMap 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; @@ -57,64 +49,23 @@ public final class BluetoothMap implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - 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, + "BluetoothMap", IBluetoothMap.class.getName()) { + @Override + public IBluetoothMap getServiceInterface(IBinder service) { + return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener l) { + /*package*/ BluetoothMap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMap 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(IBluetoothMap.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 Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -132,26 +83,11 @@ public final class BluetoothMap implements BluetoothProfile { * are ok. */ public synchronized 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 IBluetoothMap getService() { + return mProfileConnector.getService(); } /** @@ -162,7 +98,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getState(); @@ -184,7 +120,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getClient(); @@ -205,7 +141,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -237,7 +173,7 @@ public final class BluetoothMap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -278,7 +214,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -298,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -318,7 +254,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -344,7 +280,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -373,7 +309,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -386,24 +322,6 @@ public final class BluetoothMap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 559a59b68b4..ec0180c5add 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 58be7329602..fb78789ba8a 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -19,10 +19,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -121,108 +118,42 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothPan mPanService; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PAN, + "BluetoothPan", IBluetoothPan.class.getName()) { + @Override + public IBluetoothPan getServiceInterface(IBinder service) { + return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - try { - mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); - doBind(); - } - - @UnsupportedAppUsage - boolean doBind() { - Intent intent = new Intent(IBluetoothPan.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 Pan Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mPanService != null) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPan getService() { + return mProfileConnector.getService(); } + protected void finalize() { close(); } - private final IBluetoothStateChangeCallback mStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - @Override - public void onBluetoothStateChange(boolean on) { - // Handle enable request to bind again. - Log.d(TAG, "onBluetoothStateChange on: " + on); - if (on) { - try { - if (mPanService == null) { - if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); - doBind(); - } - - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - } - } else { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - } - }; - /** * Initiate connection to a profile of the remote bluetooth device. * @@ -243,7 +174,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -284,7 +215,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -303,7 +234,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -322,7 +253,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -341,7 +272,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -357,7 +288,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { service.setBluetoothTethering(value); @@ -370,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.isTetheringOn(); @@ -381,25 +312,6 @@ public final class BluetoothPan implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PAN, - BluetoothPan.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); - mPanService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); - } - } - }; - @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index b303c34ee0a..d94c65742e6 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -117,28 +118,9 @@ public class BluetoothPbap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { log("onBluetoothStateChange: up=" + up); if (!up) { - log("Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - log("Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -154,25 +136,51 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } doBind(); } boolean doBind() { - Intent intent = new Intent(IBluetoothPbap.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 Pbap Service with " + intent); - return false; + synchronized (mConnection) { + try { + if (mService == null) { + log("Binding service..."); + Intent intent = new Intent(IBluetoothPbap.class.getName()); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); + return false; + } + } + } catch (SecurityException se) { + Log.e(TAG, "", se); + return false; + } } return true; } + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + log("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + Log.e(TAG, "", ie); + } finally { + mService = null; + } + } + } + } + protected void finalize() throws Throwable { try { close(); @@ -192,21 +200,11 @@ public class BluetoothPbap implements BluetoothProfile { 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); - } + } catch (RemoteException re) { + Log.e(TAG, "", re); } } + doUnbind(); mServiceListener = null; } @@ -312,7 +310,7 @@ public class BluetoothPbap implements BluetoothProfile { public void onServiceDisconnected(ComponentName className) { log("Proxy object disconnected"); - mService = null; + doUnbind(); if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 1446adc8b9c..d70e1e7f3f5 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,10 +16,7 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -42,11 +39,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothPbapClient 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; @@ -55,72 +47,25 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) { - Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT 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.PBAP_CLIENT, + "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { + @Override + public IBluetoothPbapClient getServiceInterface(IBinder service) { + return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener l) { + BluetoothPbapClient(Context context, ServiceListener listener) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient 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(); - } - - private boolean doBind() { - Intent intent = new Intent(IBluetoothPbapClient.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 PBAP Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -138,26 +83,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * are ok. */ public synchronized 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 IBluetoothPbapClient getService() { + return mProfileConnector.getService(); } /** @@ -173,7 +103,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -198,7 +128,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception()); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { service.disconnect(device); @@ -225,7 +155,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -250,7 +180,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -275,7 +205,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -290,29 +220,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) { - log("Proxy object connected"); - } - mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, - BluetoothPbapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) { - log("Proxy object disconnected"); - } - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } @@ -345,7 +252,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -378,7 +285,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java new file mode 100644 index 00000000000..d9987249a6e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -0,0 +1,166 @@ +/* + * Copyright 2019 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * Connector for Bluetooth profile proxies to bind manager service and + * profile services + * @param The Bluetooth profile interface for this connection. + * @hide + */ +public abstract class BluetoothProfileConnector { + private int mProfileId; + private BluetoothProfile.ServiceListener mServiceListener; + private BluetoothProfile mProfileProxy; + private Context mContext; + private String mProfileName; + private String mServiceName; + private volatile T mService; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (up) { + doBind(); + } else { + doUnbind(); + } + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + logDebug("Proxy object connected"); + mService = getServiceInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(mProfileId, mProfileProxy); + } + } + + public void onServiceDisconnected(ComponentName className) { + logDebug("Proxy object disconnected"); + doUnbind(); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + } + } + }; + + BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, + String serviceName) { + mProfileId = profileId; + mProfileProxy = profile; + mProfileName = profileName; + mServiceName = serviceName; + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + logDebug("Binding service..."); + try { + Intent intent = new Intent(mServiceName); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + logError("Could not bind to Bluetooth Service with " + intent); + return false; + } + } catch (SecurityException se) { + logError("Failed to bind service. " + se); + return false; + } + } + } + return true; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; + } + } + } + } + + void connect(Context context, BluetoothProfile.ServiceListener listener) { + mContext = context; + mServiceListener = listener; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to register state change callback. " + re); + } + } + doBind(); + } + + void disconnect() { + mServiceListener = null; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to unregister state change callback" + re); + } + } + doUnbind(); + } + + T getService() { + return mService; + } + + /** + * This abstract function is used to implement method to get the + * connected Bluetooth service interface. + * @param service the connected binder service. + * @return T the binder interface of {@code service}. + * @hide + */ + public abstract T getServiceInterface(IBinder service); + + private void logDebug(String log) { + Log.d(mProfileName, log); + } + + private void logError(String log) { + Log.e(mProfileName, log); + } +} diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 1b732062f61..e0610c89055 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -17,10 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -69,11 +66,6 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothSap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** * There was an error trying to obtain the state. * @@ -95,64 +87,23 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - 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.SAP, + "BluetoothSap", IBluetoothSap.class.getName()) { + @Override + public IBluetoothSap getServiceInterface(IBinder service) { + return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener l) { + /*package*/ BluetoothSap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothSap 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(IBluetoothSap.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 SAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -172,26 +123,11 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ public synchronized 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 IBluetoothSap getService() { + return mProfileConnector.getService(); } /** @@ -203,7 +139,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getState(); @@ -226,7 +162,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getClient(); @@ -249,7 +185,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -284,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -305,7 +241,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -326,7 +262,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -347,7 +283,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -372,7 +308,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -398,7 +334,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -411,24 +347,6 @@ public final class BluetoothSap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b60dd0f52c7..223eb552f83 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1094,11 +1094,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { synchronized (mProfileServices) { - ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + Integer profile = new Integer(bluetoothProfile); + ProfileServiceConnections psc = mProfileServices.get(profile); if (psc == null) { return; } psc.removeProxy(proxy); + if (psc.isEmpty()) { + // All prxoies are disconnected, unbind with the service. + try { + mContext.unbindService(psc); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + } + mProfileServices.remove(profile); + } } } @@ -1255,6 +1265,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mProxies.kill(); } + private boolean isEmpty() { + return mProxies.getRegisteredCallbackCount() == 0; + } + @Override public void onServiceConnected(ComponentName className, IBinder service) { // remove timeout message -- GitLab From 70d76596569ec6c17761bdc4099015e756e7b61b Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 26 Mar 2019 21:38:08 +0800 Subject: [PATCH 1035/1408] 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 Change-Id: Id0d85866d386962b94d2d966f0a864b1da165d13 --- .../java/android/bluetooth/BluetoothA2dp.java | 313 +++++------------- .../android/bluetooth/BluetoothA2dpSink.java | 129 ++------ .../bluetooth/BluetoothAvrcpController.java | 130 ++------ .../android/bluetooth/BluetoothHeadset.java | 40 ++- .../bluetooth/BluetoothHeadsetClient.java | 196 ++++------- .../bluetooth/BluetoothHearingAid.java | 274 ++++----------- .../android/bluetooth/BluetoothHidDevice.java | 139 ++------ .../android/bluetooth/BluetoothHidHost.java | 145 ++------ .../java/android/bluetooth/BluetoothMap.java | 129 ++------ .../android/bluetooth/BluetoothMapClient.java | 138 ++------ .../java/android/bluetooth/BluetoothPan.java | 135 ++------ .../java/android/bluetooth/BluetoothPbap.java | 85 +++-- .../bluetooth/BluetoothPbapClient.java | 136 ++------ .../bluetooth/BluetoothProfileConnector.java | 166 ++++++++++ .../java/android/bluetooth/BluetoothSap.java | 129 ++------ .../bluetooth/BluetoothManagerService.java | 16 +- 16 files changed, 680 insertions(+), 1620 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothProfileConnector.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 442b23938cc..1fe1b10005c 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -22,23 +22,16 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -209,101 +202,32 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; - - 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..."); - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", + IBluetoothA2dp.class.getName()) { + @Override + public IBluetoothA2dp getServiceInterface(IBinder service) { + return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { 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(IBluetoothA2dp.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothA2dp getService() { + return mProfileConnector.getService(); } @Override @@ -333,17 +257,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -376,17 +298,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -397,17 +317,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -418,17 +336,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -439,18 +355,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - 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; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -480,18 +394,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return mService.setActiveDevice(device); + return service.setActiveDevice(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -511,17 +423,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevice(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getActiveDevice(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -543,22 +453,20 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -578,18 +486,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -602,17 +508,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.isAvrcpAbsoluteVolumeSupported(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.isAvrcpAbsoluteVolumeSupported(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -625,15 +529,13 @@ public final class BluetoothA2dp implements BluetoothProfile { public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setAvrcpAbsoluteVolume(volume); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setAvrcpAbsoluteVolume(volume); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); } } @@ -646,18 +548,16 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isA2dpPlaying(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -694,19 +594,17 @@ public final class BluetoothA2dp implements BluetoothProfile { public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getCodecStatus(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getCodecStatus(device); } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -723,17 +621,15 @@ public final class BluetoothA2dp implements BluetoothProfile { BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(device, codecConfig); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setCodecConfigPreference(device, codecConfig); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -772,21 +668,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(device); + service.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(device); + service.disableOptionalCodecs(device); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -801,17 +695,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.supportsOptionalCodecs(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.supportsOptionalCodecs(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -826,17 +718,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.getOptionalCodecsEnabled(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getOptionalCodecsEnabled(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_PREF_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -858,18 +748,16 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); return; } - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - mService.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -900,35 +788,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index cb996f3381b..5a8055a29cf 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -125,93 +121,31 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String EXTRA_AUDIO_CONFIG = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; - - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, + "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { + @Override + public IBluetoothA2dpSink getServiceInterface(IBinder service) { + return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { 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(IBluetoothA2dpSink.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - 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); - } - } - } + private IBluetoothA2dpSink getService() { + return mProfileConnector.getService(); } @Override @@ -242,7 +176,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -283,7 +217,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -302,7 +236,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -321,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -340,7 +274,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -366,7 +300,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getAudioConfig(device); @@ -396,7 +330,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -428,7 +362,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -449,7 +383,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.isA2dpPlaying(device); @@ -488,25 +422,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, - BluetoothA2dpSink.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index c447868d6f0..4e7e4415c54 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -80,93 +76,32 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; - - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, + "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { + @Override + public IBluetoothAvrcpController getServiceInterface(IBinder service) { + return IBluetoothAvrcpController.Stub.asInterface( + Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { 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(IBluetoothAvrcpController.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - 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); - } - } - } + private IBluetoothAvrcpController getService() { + return mProfileConnector.getService(); } @Override @@ -180,7 +115,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -199,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -218,7 +155,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -239,7 +177,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { settings = service.getPlayerSettings(device); @@ -257,7 +196,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.setPlayerApplicationSetting(plAppSetting); @@ -277,7 +217,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { service.sendGroupNavigationCmd(device, keyCode, keyState); @@ -290,25 +231,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, - BluetoothAvrcpController.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 8d9d340ee68..9862a63ef23 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -337,19 +337,9 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -374,24 +364,32 @@ public final class BluetoothHeadset implements BluetoothProfile { doBind(); } - boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager().bindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind HeadsetService", e); + } + } } return false; } - void doUnbind() { + private void doUnbind() { synchronized (mConnection) { if (mService != null) { + if (VDBG) Log.d(TAG, "Unbinding service..."); try { mAdapter.getBluetoothManager().unbindBluetoothProfileService( BluetoothProfile.HEADSET, mConnection); } catch (RemoteException e) { Log.e(TAG, "Unable to unbind HeadsetService", e); + } finally { + mService = null; } } } @@ -411,8 +409,8 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } mServiceListener = null; @@ -1169,7 +1167,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_DISCONNECTED)); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 549c1faddd9..05833b5f571 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,15 +17,11 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -367,73 +363,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, + "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override - 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..."); - Intent intent = new Intent( - IBluetoothHeadsetClient.class.getName()); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + public IBluetoothHeadsetClient getServiceInterface(IBinder service) { + return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { 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(IBluetoothHeadsetClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /** @@ -444,27 +389,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - 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; + private IBluetoothHeadsetClient getService() { + return mProfileConnector.getService(); } /** @@ -481,7 +410,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -504,7 +434,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -525,7 +456,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -548,7 +480,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -570,7 +503,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -590,7 +524,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -612,7 +547,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -638,7 +574,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.startVoiceRecognition(device); @@ -663,7 +600,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.stopVoiceRecognition(device); @@ -683,7 +621,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentCalls(device); @@ -703,7 +642,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentAgEvents(device); @@ -727,7 +667,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.acceptCall(device, flag); @@ -748,7 +689,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.holdCall(device); @@ -774,7 +716,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.rejectCall(device); @@ -804,7 +747,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.terminateCall(device, call); @@ -832,7 +776,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.enterPrivateMode(device, index); @@ -859,7 +804,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.explicitCallTransfer(device); @@ -882,7 +828,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.dial(device, number); @@ -906,7 +853,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendDTMF(device, code); @@ -932,7 +880,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getLastVoiceTagNumber(device); @@ -952,7 +901,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioState(device); @@ -975,7 +925,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { service.setAudioRouteAllowed(device, allowed); @@ -997,7 +948,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioRouteAllowed(device); @@ -1021,7 +973,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.connectAudio(device); @@ -1045,7 +998,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.disconnectAudio(device); @@ -1066,7 +1020,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getCurrentAgFeatures(device); @@ -1080,29 +1035,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return null; } - - private final ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, - BluetoothHeadsetClient.this); - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index d6edb90ca27..60fb6fb122e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,21 +22,14 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This class provides the public APIs to control the Hearing Aid profile. @@ -129,97 +122,31 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothHearingAid mService; private BluetoothAdapter mAdapter; - - 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..."); - try { - mServiceLock.writeLock().lock(); - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, + "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { + @Override + public IBluetoothHearingAid getServiceInterface(IBinder service) { + return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - void doBind() { - Intent intent = new Intent(IBluetoothHearingAid.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); - return; - } + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothHearingAid getService() { + return mProfileConnector.getService(); } /** @@ -241,18 +168,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -283,18 +208,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -304,18 +227,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @Override public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -326,18 +247,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - 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(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -348,19 +267,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - 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; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -388,20 +305,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - mService.setActiveDevice(device); + service.setActiveDevice(device); return true; } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -419,18 +334,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevices(); + if (service != null && isEnabled()) { + return service.getActiveDevices(); } - 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<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -451,23 +364,21 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -485,19 +396,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -536,18 +445,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getVolume()"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getVolume(); + if (service != null && isEnabled()) { + return service.getVolume(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return 0; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return 0; - } finally { - mServiceLock.readLock().unlock(); } } @@ -567,21 +474,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void adjustVolume(int direction) { if (DBG) log("adjustVolume(" + direction + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.adjustVolume(direction); + service.adjustVolume(direction); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -594,20 +498,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.setVolume(volume); + service.setVolume(volume); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -623,21 +525,19 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getCustomerId(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return HI_SYNC_ID_INVALID; } if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return mService.getHiSyncId(device); + return service.getHiSyncId(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; - } finally { - mServiceLock.readLock().unlock(); } } @@ -653,19 +553,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceSide(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceSide(device); + return service.getDeviceSide(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return SIDE_LEFT; - } finally { - mServiceLock.readLock().unlock(); } } @@ -681,52 +579,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceMode(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceMode(device); + return service.getDeviceMode(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return MODE_MONAURAL; - } finally { - mServiceLock.readLock().unlock(); } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, - BluetoothHearingAid.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index e44f36e90c7..e9b0be2c4cd 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -18,13 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -328,11 +325,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHidDevice mService; - private BluetoothAdapter mAdapter; - private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { private final Executor mExecutor; @@ -386,114 +378,33 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - public void onBluetoothStateChange(boolean up) { - Log.d(TAG, "onBluetoothStateChange: up=" + up); - synchronized (mConnection) { - if (up) { - try { - if (mService == null) { - Log.d(TAG, "Binding HID Device service..."); - doBind(); - } - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } - } else { - Log.d(TAG, "Unbinding service..."); - doUnbind(); - } - } - } - }; - - private final ServiceConnection mConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected( - BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, + "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { + @Override + public IBluetoothHidDevice getServiceInterface(IBinder service) { + return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; BluetoothHidDevice(Context context, ServiceListener listener) { - mContext = context; - mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidDevice.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); - return false; - } - Log.d(TAG, "Bound to HID Device Service"); - return true; - } - - void doUnbind() { - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to unbind HidDevService", e); - } - } + mProfileConnector.connect(context, listener); } void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - doUnbind(); - } - mServiceListener = null; + private IBluetoothHidDevice getService() { + return mProfileConnector.getService(); } /** {@inheritDoc} */ @Override public List getConnectedDevices() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectedDevices(); @@ -510,7 +421,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -527,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectionState(device); @@ -584,7 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { throw new IllegalArgumentException("callback parameter cannot be null"); } - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); @@ -612,7 +523,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean unregisterApp() { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.unregisterApp(); @@ -637,7 +548,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.sendReport(device, id, data); @@ -663,7 +574,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -687,7 +598,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.reportError(device, error); @@ -708,7 +619,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public String getUserAppName() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { @@ -734,7 +645,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean connect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.connect(device); @@ -758,7 +669,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.disconnect(device); diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 7c925a1cb5e..4afb382c066 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -18,14 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -220,97 +216,32 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothHidHost mService; - - 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 { - if (mService != null) { - 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 final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, + "BluetoothHidHost", IBluetoothHidHost.class.getName()) { + @Override + public IBluetoothHidHost getServiceInterface(IBinder service) { + return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { 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(IBluetoothHidHost.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { if (VDBG) log("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 IBluetoothHidHost getService() { + return mProfileConnector.getService(); } /** @@ -334,7 +265,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -374,7 +305,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -393,7 +324,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -412,7 +343,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -431,7 +362,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -461,7 +392,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -493,7 +424,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,26 +437,6 @@ public final class BluetoothHidHost implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, - BluetoothHidHost.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } @@ -545,7 +456,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -571,7 +482,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -595,7 +506,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -626,7 +537,7 @@ public final class BluetoothHidHost implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -652,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -677,7 +588,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -701,7 +612,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -726,7 +637,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index fc5f830a894..dd2f150ad4e 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -45,11 +41,6 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothMap 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; @@ -58,64 +49,23 @@ public final class BluetoothMap implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - 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, + "BluetoothMap", IBluetoothMap.class.getName()) { + @Override + public IBluetoothMap getServiceInterface(IBinder service) { + return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener l) { + /*package*/ BluetoothMap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMap 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(IBluetoothMap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -133,26 +83,11 @@ public final class BluetoothMap implements BluetoothProfile { * are ok. */ public synchronized 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 IBluetoothMap getService() { + return mProfileConnector.getService(); } /** @@ -163,7 +98,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getState(); @@ -185,7 +120,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getClient(); @@ -206,7 +141,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -238,7 +173,7 @@ public final class BluetoothMap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -279,7 +214,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -299,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -319,7 +254,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -345,7 +280,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -374,7 +309,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -387,24 +322,6 @@ public final class BluetoothMap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 1c82e1984b6..ec0180c5add 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -18,14 +18,11 @@ 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.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -61,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; @@ -76,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, - UserHandle.CURRENT_OR_SELF)) { - 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 { @@ -151,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(); } /** @@ -180,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); @@ -200,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); @@ -222,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); @@ -242,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(); @@ -263,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); @@ -284,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); @@ -308,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) { @@ -337,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); @@ -366,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); @@ -386,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); @@ -406,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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 8923d734c84..fb78789ba8a 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -19,14 +19,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -122,108 +118,42 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothPan mPanService; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PAN, + "BluetoothPan", IBluetoothPan.class.getName()) { + @Override + public IBluetoothPan getServiceInterface(IBinder service) { + return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - try { - mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); - doBind(); - } - - @UnsupportedAppUsage - boolean doBind() { - Intent intent = new Intent(IBluetoothPan.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mPanService != null) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPan getService() { + return mProfileConnector.getService(); } + protected void finalize() { close(); } - private final IBluetoothStateChangeCallback mStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - @Override - public void onBluetoothStateChange(boolean on) { - // Handle enable request to bind again. - Log.d(TAG, "onBluetoothStateChange on: " + on); - if (on) { - try { - if (mPanService == null) { - if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); - doBind(); - } - - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - } - } else { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - } - }; - /** * Initiate connection to a profile of the remote bluetooth device. * @@ -244,7 +174,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -285,7 +215,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -304,7 +234,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -323,7 +253,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -342,7 +272,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -358,7 +288,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { service.setBluetoothTethering(value); @@ -371,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.isTetheringOn(); @@ -382,25 +312,6 @@ public final class BluetoothPan implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PAN, - BluetoothPan.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); - mPanService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); - } - } - }; - @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 359bec686a8..d94c65742e6 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -118,28 +118,9 @@ public class BluetoothPbap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { log("onBluetoothStateChange: up=" + up); if (!up) { - log("Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - log("Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -155,25 +136,51 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } doBind(); } boolean doBind() { - Intent intent = new Intent(IBluetoothPbap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); - return false; + synchronized (mConnection) { + try { + if (mService == null) { + log("Binding service..."); + Intent intent = new Intent(IBluetoothPbap.class.getName()); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); + return false; + } + } + } catch (SecurityException se) { + Log.e(TAG, "", se); + return false; + } } return true; } + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + log("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + Log.e(TAG, "", ie); + } finally { + mService = null; + } + } + } + } + protected void finalize() throws Throwable { try { close(); @@ -193,21 +200,11 @@ public class BluetoothPbap implements BluetoothProfile { 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); - } + } catch (RemoteException re) { + Log.e(TAG, "", re); } } + doUnbind(); mServiceListener = null; } @@ -313,7 +310,7 @@ public class BluetoothPbap implements BluetoothProfile { public void onServiceDisconnected(ComponentName className) { log("Proxy object disconnected"); - mService = null; + doUnbind(); if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index cbc96c07333..d70e1e7f3f5 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -43,11 +39,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothPbapClient 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; @@ -56,72 +47,25 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) { - Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT 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.PBAP_CLIENT, + "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { + @Override + public IBluetoothPbapClient getServiceInterface(IBinder service) { + return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener l) { + BluetoothPbapClient(Context context, ServiceListener listener) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient 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(); - } - - private boolean doBind() { - Intent intent = new Intent(IBluetoothPbapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -139,26 +83,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * are ok. */ public synchronized 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 IBluetoothPbapClient getService() { + return mProfileConnector.getService(); } /** @@ -174,7 +103,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -199,7 +128,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception()); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { service.disconnect(device); @@ -226,7 +155,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -251,7 +180,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -276,7 +205,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -291,29 +220,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) { - log("Proxy object connected"); - } - mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, - BluetoothPbapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) { - log("Proxy object disconnected"); - } - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } @@ -346,7 +252,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -379,7 +285,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java new file mode 100644 index 00000000000..d9987249a6e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -0,0 +1,166 @@ +/* + * Copyright 2019 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * Connector for Bluetooth profile proxies to bind manager service and + * profile services + * @param The Bluetooth profile interface for this connection. + * @hide + */ +public abstract class BluetoothProfileConnector { + private int mProfileId; + private BluetoothProfile.ServiceListener mServiceListener; + private BluetoothProfile mProfileProxy; + private Context mContext; + private String mProfileName; + private String mServiceName; + private volatile T mService; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (up) { + doBind(); + } else { + doUnbind(); + } + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + logDebug("Proxy object connected"); + mService = getServiceInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(mProfileId, mProfileProxy); + } + } + + public void onServiceDisconnected(ComponentName className) { + logDebug("Proxy object disconnected"); + doUnbind(); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + } + } + }; + + BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, + String serviceName) { + mProfileId = profileId; + mProfileProxy = profile; + mProfileName = profileName; + mServiceName = serviceName; + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + logDebug("Binding service..."); + try { + Intent intent = new Intent(mServiceName); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + logError("Could not bind to Bluetooth Service with " + intent); + return false; + } + } catch (SecurityException se) { + logError("Failed to bind service. " + se); + return false; + } + } + } + return true; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; + } + } + } + } + + void connect(Context context, BluetoothProfile.ServiceListener listener) { + mContext = context; + mServiceListener = listener; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to register state change callback. " + re); + } + } + doBind(); + } + + void disconnect() { + mServiceListener = null; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to unregister state change callback" + re); + } + } + doUnbind(); + } + + T getService() { + return mService; + } + + /** + * This abstract function is used to implement method to get the + * connected Bluetooth service interface. + * @param service the connected binder service. + * @return T the binder interface of {@code service}. + * @hide + */ + public abstract T getServiceInterface(IBinder service); + + private void logDebug(String log) { + Log.d(mProfileName, log); + } + + private void logError(String log) { + Log.e(mProfileName, log); + } +} diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index ebf6bed5447..e0610c89055 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -70,11 +66,6 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothSap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** * There was an error trying to obtain the state. * @@ -96,64 +87,23 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - 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.SAP, + "BluetoothSap", IBluetoothSap.class.getName()) { + @Override + public IBluetoothSap getServiceInterface(IBinder service) { + return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener l) { + /*package*/ BluetoothSap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothSap 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(IBluetoothSap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -173,26 +123,11 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ public synchronized 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 IBluetoothSap getService() { + return mProfileConnector.getService(); } /** @@ -204,7 +139,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getState(); @@ -227,7 +162,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getClient(); @@ -250,7 +185,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -285,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -306,7 +241,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -327,7 +262,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -348,7 +283,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -373,7 +308,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -399,7 +334,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -412,24 +347,6 @@ public final class BluetoothSap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c4bc52c6f4d..9405ed26e51 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1091,11 +1091,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { synchronized (mProfileServices) { - ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + Integer profile = new Integer(bluetoothProfile); + ProfileServiceConnections psc = mProfileServices.get(profile); if (psc == null) { return; } psc.removeProxy(proxy); + if (psc.isEmpty()) { + // All prxoies are disconnected, unbind with the service. + try { + mContext.unbindService(psc); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + } + mProfileServices.remove(profile); + } } } @@ -1252,6 +1262,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mProxies.kill(); } + private boolean isEmpty() { + return mProxies.getRegisteredCallbackCount() == 0; + } + @Override public void onServiceConnected(ComponentName className, IBinder service) { // remove timeout message -- GitLab From 8b55c83b7eeff37ad2661c550ff694eaf94cfd94 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Tue, 14 May 2019 11:09:19 -0700 Subject: [PATCH 1036/1408] 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 --- framework/java/android/bluetooth/BluetoothMapClient.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ec0180c5add..69682c6ab5d 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 = -- GitLab From 1fd7ddfb8581f383c9bb45e510bedd2b5e397113 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Tue, 14 May 2019 11:09:19 -0700 Subject: [PATCH 1037/1408] 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(+) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ec0180c5add..69682c6ab5d 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 = -- GitLab From 608187427edcf4d21864362b303334b353346174 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Tue, 21 May 2019 17:15:44 -0700 Subject: [PATCH 1038/1408] ScanFilter: Fix argument sanitization logic. Bug: 132888204 Test: Manual Change-Id: Id8cadbbfac29aca97ccb83815795948b1bbf85a5 --- framework/java/android/bluetooth/le/ScanFilter.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 07ba2c6680a..038994fb553 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -590,6 +590,9 @@ public final class ScanFilter implements Parcelable { public @NonNull Builder setServiceSolicitationUuid( @Nullable ParcelUuid serviceSolicitationUuid) { mServiceSolicitationUuid = serviceSolicitationUuid; + if (serviceSolicitationUuid == null) { + mServiceSolicitationUuidMask = null; + } return this; } @@ -600,13 +603,16 @@ public final class ScanFilter implements Parcelable { * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to * ignore that bit. * + * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null. + * @param solicitationUuidMask can be null or a mask with no restriction. + * * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but * {@code serviceSolicitationUuidMask} is not {@code null}. */ public @NonNull Builder setServiceSolicitationUuid( @Nullable ParcelUuid serviceSolicitationUuid, @Nullable ParcelUuid solicitationUuidMask) { - if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) { + if (solicitationUuidMask != null && serviceSolicitationUuid == null) { throw new IllegalArgumentException( "SolicitationUuid is null while SolicitationUuidMask is not null!"); } -- GitLab From 9fae71bd63b5e5a48afb3ad254d0c87960234bf4 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Tue, 21 May 2019 17:15:44 -0700 Subject: [PATCH 1039/1408] DO NOT MERGE: ScanFilter: Fix argument sanitization logic. Bug: 132888204 Test: Manual Change-Id: Id8cadbbfac29aca97ccb83815795948b1bbf85a5 --- framework/java/android/bluetooth/le/ScanFilter.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 07ba2c6680a..038994fb553 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -590,6 +590,9 @@ public final class ScanFilter implements Parcelable { public @NonNull Builder setServiceSolicitationUuid( @Nullable ParcelUuid serviceSolicitationUuid) { mServiceSolicitationUuid = serviceSolicitationUuid; + if (serviceSolicitationUuid == null) { + mServiceSolicitationUuidMask = null; + } return this; } @@ -600,13 +603,16 @@ public final class ScanFilter implements Parcelable { * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to * ignore that bit. * + * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null. + * @param solicitationUuidMask can be null or a mask with no restriction. + * * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but * {@code serviceSolicitationUuidMask} is not {@code null}. */ public @NonNull Builder setServiceSolicitationUuid( @Nullable ParcelUuid serviceSolicitationUuid, @Nullable ParcelUuid solicitationUuidMask) { - if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) { + if (solicitationUuidMask != null && serviceSolicitationUuid == null) { throw new IllegalArgumentException( "SolicitationUuid is null while SolicitationUuidMask is not null!"); } -- GitLab From eef5c81a33a1fdf7a133329852d03e32d35d9b75 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Fri, 24 May 2019 14:35:13 -0700 Subject: [PATCH 1040/1408] Update BluetoothProfileConnector to invoke service disconnections with correct profile ID Bug: b/133517229 Test: Build + Manual on automotive hardware Change-Id: Id8dbeaf844a65020f610505f81eea20b96ec002a --- .../android/bluetooth/BluetoothProfileConnector.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index d9987249a6e..863fd3698cb 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -32,12 +32,12 @@ import android.util.Log; * @hide */ public abstract class BluetoothProfileConnector { - private int mProfileId; + private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; - private BluetoothProfile mProfileProxy; + private final BluetoothProfile mProfileProxy; private Context mContext; - private String mProfileName; - private String mServiceName; + private final String mProfileName; + private final String mServiceName; private volatile T mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -65,7 +65,7 @@ public abstract class BluetoothProfileConnector { logDebug("Proxy object disconnected"); doUnbind(); if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + mServiceListener.onServiceDisconnected(mProfileId); } } }; -- GitLab From 99e2f8c9a166f9d9174214d8529529ce45e4ee80 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Fri, 24 May 2019 14:35:13 -0700 Subject: [PATCH 1041/1408] BluetoothProfileConnector: Invoke service disconnections with the correct profile ID Bug: b/133517229 Test: Build + Manual on automotive hardware Change-Id: Id8dbeaf844a65020f610505f81eea20b96ec002a Merged-In: Id8dbeaf844a65020f610505f81eea20b96ec002a --- .../android/bluetooth/BluetoothProfileConnector.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index d9987249a6e..863fd3698cb 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -32,12 +32,12 @@ import android.util.Log; * @hide */ public abstract class BluetoothProfileConnector { - private int mProfileId; + private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; - private BluetoothProfile mProfileProxy; + private final BluetoothProfile mProfileProxy; private Context mContext; - private String mProfileName; - private String mServiceName; + private final String mProfileName; + private final String mServiceName; private volatile T mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -65,7 +65,7 @@ public abstract class BluetoothProfileConnector { logDebug("Proxy object disconnected"); doUnbind(); if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + mServiceListener.onServiceDisconnected(mProfileId); } } }; -- GitLab From 0d3bada213ed4d6d8a5ddb8ee33ac5390014072d Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Wed, 29 May 2019 10:29:30 +0800 Subject: [PATCH 1042/1408] Declare STATE_AUDIO_CONNECTED in right place Test: build pass Change-Id: Ia3834207756afdcc06ccfd7bdc5c91c3e651bebe --- framework/java/android/bluetooth/BluetoothHeadset.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 9862a63ef23..672174f8f76 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -280,6 +280,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ + public static final int STATE_AUDIO_CONNECTED = 12; /** * Intent used to broadcast the headset's indicator status @@ -322,8 +323,6 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String EXTRA_HF_INDICATORS_IND_VALUE = "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; - public static final int STATE_AUDIO_CONNECTED = 12; - private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; -- GitLab From 147146fb21165bdf7812fa9745ca797b7d12b437 Mon Sep 17 00:00:00 2001 From: Andrei Onea Date: Mon, 17 Jun 2019 11:26:14 +0100 Subject: [PATCH 1043/1408] Document public alternatives to greylisted APIs Add known public alternatives or recommendations for greylisted APIs in Bluetooth. Bug: 135171386 Test: m Change-Id: I86e708be37eb7d1b0fafa2d64283b7f81bc02e51 --- framework/java/android/bluetooth/BluetoothAdapter.java | 6 ++++-- framework/java/android/bluetooth/BluetoothDevice.java | 7 ++++--- .../java/android/bluetooth/BluetoothServerSocket.java | 3 ++- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 31bbd16497c..39d63de87da 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1028,7 +1028,8 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + + "whether you can use BLE & BT classic.") public int getLeState() { int state = BluetoothAdapter.STATE_OFF; @@ -1484,7 +1485,8 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + + "shows UI that confirms the user wants to go into discoverable mode.") public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) { return false; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 34c7372202e..ee33103ebb2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1051,7 +1051,7 @@ public final class BluetoothDevice implements Parcelable { * @return the Bluetooth alias, or null if no alias or there was a problem * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -1100,7 +1100,7 @@ public final class BluetoothDevice implements Parcelable { * @see #getAlias() * @see #getName() */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") public String getAliasName() { String name = getAlias(); if (name == null) { @@ -1975,7 +1975,8 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use " + + "{@link #createInsecureRfcommSocketToServiceRecord} instead.") public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index c06b837a9ee..3a23808f361 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -77,7 +77,8 @@ public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; private static final boolean DBG = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API " + + "instead.") /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 3a1e2f58c99..a6e3153d6af 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -131,7 +131,7 @@ public final class BluetoothSocket implements Closeable { private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.") private ParcelFileDescriptor mPfd; @UnsupportedAppUsage private LocalSocket mSocket; -- GitLab From 69b4b5f1f98cb511a7e1760a913134e4e0c22317 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Thu, 27 Jun 2019 12:47:38 +0100 Subject: [PATCH 1044/1408] Restore some greylist entries. In Q, these APIs were either: - removed from the greylist entirely without good reason - Moved to the restricted greylist without any public alternative information added So they are being moved back to the greylist for Q. Test: Treehugger Bug: 136102585 Change-Id: I5ac8b8b9b23c3789d80239cf456072cc7dfa1203 --- framework/java/android/bluetooth/BluetoothHearingAid.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 60fb6fb122e..a812c32ae86 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -83,6 +84,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ + @UnsupportedAppUsage @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -303,6 +305,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -331,6 +334,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); -- GitLab From 2171493cb2789a05157e2086d39c53dbc581459e Mon Sep 17 00:00:00 2001 From: Deqiang Chen Date: Tue, 25 Jun 2019 14:30:55 -0700 Subject: [PATCH 1045/1408] Define intent for receive vendor specific response and define a function to send vendor AT command from Hfp client Bug: 132813146 Test: Local test with Kitchensink: able to send vendor command and receive response. Change-Id: I9a6debbacb1a5ad6445416c067d3d35849906685 --- .../bluetooth/BluetoothHeadsetClient.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 05833b5f571..5d00f09501b 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -125,6 +125,17 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final String ACTION_RESULT = "android.bluetooth.headsetclient.profile.action.RESULT"; + /** + * Intent that notifies about vendor specific event arrival. Events not defined in + * HFP spec will be matched with supported vendor event list and this intent will + * be broadcasted upon a match. Supported vendor events are of format of + * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx". + * Vendor event can be a response to an vendor specific command or unsolicited. + * + */ + public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = + "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; + /** * Intent that notifies about the number attached to the last voice tag * recorded on AG. @@ -243,6 +254,28 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final String EXTRA_CME_CODE = "android.bluetooth.headsetclient.extra.CME_CODE"; + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor ID. + */ + public static final String EXTRA_VENDOR_ID = + "android.bluetooth.headsetclient.extra.VENDOR_ID"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor event code. + */ + public static final String EXTRA_VENDOR_EVENT_CODE = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * contains full vendor event including event code and full arguments. + */ + public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; + + /* Extras for AG_FEATURES, extras type is boolean */ // TODO verify if all of those are actually useful /** @@ -587,6 +620,31 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } + /** + * Send vendor specific AT command. + * + * @param device remote device + * @param vendorId vendor number by Bluetooth SIG + * @param atCommand command to be sent. It start with + prefix and only one command at one time. + * @return true if command has been issued successfully; false + * otherwise. + */ + public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, + String atCommand) { + if (DBG) log("sendVendorSpecificCommand()"); + final IBluetoothHeadsetClient service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendVendorAtCommand(device, vendorId, atCommand); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** * Stops voice recognition. * -- GitLab From 126dc8bbaeed8bc25251b688273e21a315d108e9 Mon Sep 17 00:00:00 2001 From: Zongheng Wang Date: Mon, 8 Jul 2019 15:22:04 -0700 Subject: [PATCH 1046/1408] Do not turn on Bluetooth when booting into Safe Mode Bug: 122691743 Test: Boot into Safe Mode and Bluetooth is not turned on. Change-Id: I653b2f58c462631e9c4cc868c4ec1932ff907999 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 223eb552f83..89b59cf4a73 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1142,7 +1142,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBluetoothDisallowed) { return; } - if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { + final boolean isSafeMode = mContext.getPackageManager().isSafeMode(); + if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) { if (DBG) { Slog.d(TAG, "Auto-enabling Bluetooth."); } -- GitLab From f1a5389eee79c37cc8dd9061bdf59e08382ffd7b Mon Sep 17 00:00:00 2001 From: Eric Jeong Date: Thu, 18 Jul 2019 09:37:08 -0700 Subject: [PATCH 1047/1408] Add isHeadlessSystemUserMode() to UserManager - isHeadlessSystemUserMode() returns whether the device is running with headless system user (go/multi-user-headless-user0) - RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER is replaced by isHeadlessSystemUserMode() Bug: 137755681 Test: Bluetooth service and Network policy manager should work fine with headless system user. A profile user should be created with headless system user. Change-Id: Ic48d98426f7a5ea47dde2008ae51f78855f622e6 --- .../java/com/android/server/bluetooth/BluetoothService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 5c5b477352b..112cf085372 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -18,8 +18,7 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; - -import com.android.internal.os.RoSystemProperties; +import android.os.UserManager; class BluetoothService extends SystemService { private BluetoothManagerService mBluetoothManagerService; @@ -47,7 +46,7 @@ class BluetoothService extends SystemService { publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && - !RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER) { + !UserManager.isHeadlessSystemUserMode()) { initialize(); } } -- GitLab From 38a8716ef8fc33e5c8915311b932d945c864ae85 Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Thu, 18 Jul 2019 21:45:10 +0800 Subject: [PATCH 1048/1408] Add helpers to check whether the BluetoothCodecConfig instance is selectable There will be a helper in the BluetoothCodecStatus to check whether the codec config is matched the selectable array. It uses 3 smaller helpers to confirm the codec config has none (wildcard matching), or one and only one value for the audio feeding parameters. Besides, this CL also adds a helper to compare whether two codec configs are similar or not, and uses NONE values as wildcard. Bug: 131147224 Bug: 133719424 Test: atest -t BluetoothInstrumentationTests:com.android.bluetooth.a2dp Change-Id: I7d8f1a16b8358c440841801d95471b2d010739ec --- .../bluetooth/BluetoothCodecConfig.java | 91 +++++++++++++++++++ .../bluetooth/BluetoothCodecStatus.java | 37 ++++++++ 2 files changed, 128 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 79c0a3a207c..c79df174344 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -427,6 +427,43 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecSpecific4; } + /** + * Checks whether a value set presented by a bitmask has zero or single bit + * + * @param valueSet the value set presented by a bitmask + * @return true if the valueSet contains zero or single bit, otherwise false. + */ + private static boolean hasSingleBit(int valueSet) { + return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); + } + + /** + * Checks whether the object contains none or single sample rate. + * + * @return true if the object contains none or single sample rate, otherwise false. + */ + public boolean hasSingleSampleRate() { + return hasSingleBit(mSampleRate); + } + + /** + * Checks whether the object contains none or single bits per sample. + * + * @return true if the object contains none or single bits per sample, otherwise false. + */ + public boolean hasSingleBitsPerSample() { + return hasSingleBit(mBitsPerSample); + } + + /** + * Checks whether the object contains none or single channel mode. + * + * @return true if the object contains none or single channel mode, otherwise false. + */ + public boolean hasSingleChannelMode() { + return hasSingleBit(mChannelMode); + } + /** * Checks whether the audio feeding parameters are same. * @@ -438,4 +475,58 @@ public final class BluetoothCodecConfig implements Parcelable { && other.mBitsPerSample == mBitsPerSample && other.mChannelMode == mChannelMode); } + + /** + * Checks whether another codec config has the similar feeding parameters. + * Any parameters with NONE value will be considered to be a wildcard matching. + * + * @param other the codec config to compare against + * @return true if the audio feeding parameters are similar, otherwise false. + */ + public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { + if (other == null || mCodecType != other.mCodecType) { + return false; + } + int sampleRate = other.mSampleRate; + if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE + || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + sampleRate = mSampleRate; + } + int bitsPerSample = other.mBitsPerSample; + if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE + || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + bitsPerSample = mBitsPerSample; + } + int channelMode = other.mChannelMode; + if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE + || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + channelMode = mChannelMode; + } + return sameAudioFeedingParameters(new BluetoothCodecConfig( + mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode, + /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0, + /* specific4 */ 0)); + } + + /** + * Checks whether the codec specific parameters are the same. + * + * @param other the codec config to compare against + * @return true if the codec specific parameters are the same, otherwise false. + */ + public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { + if (other == null && mCodecType != other.mCodecType) { + return false; + } + // Currently we only care about the LDAC Playback Quality at CodecSpecific1 + switch (mCodecType) { + case SOURCE_CODEC_TYPE_LDAC: + if (mCodecSpecific1 != other.mCodecSpecific1) { + return false; + } + // fall through + default: + return true; + } + } } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 32bb681f2e8..8237d6a73c7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -88,6 +88,43 @@ public final class BluetoothCodecStatus implements Parcelable { return Arrays.asList(c1).containsAll(Arrays.asList(c2)); } + /** + * Checks whether the codec config matches the selectable capabilities. + * Any parameters of the codec config with NONE value will be considered a wildcard matching. + * + * @param codecConfig the codec config to compare against + * @return true if the codec config matches, otherwise false + */ + public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + if (codecConfig == null || !codecConfig.hasSingleSampleRate() + || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { + return false; + } + for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) { + if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { + continue; + } + int sampleRate = codecConfig.getSampleRate(); + if ((sampleRate & selectableConfig.getSampleRate()) == 0 + && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) { + continue; + } + int bitsPerSample = codecConfig.getBitsPerSample(); + if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0 + && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + continue; + } + int channelMode = codecConfig.getChannelMode(); + if ((channelMode & selectableConfig.getChannelMode()) == 0 + && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) { + continue; + } + return true; + } + return false; + } + + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, -- GitLab From bacd869510eb18b9a04651cd9537418622a4b935 Mon Sep 17 00:00:00 2001 From: Ram Periathiruvadi Date: Wed, 31 Jul 2019 15:47:47 -0700 Subject: [PATCH 1049/1408] Fix Handling of user switching for headless user 0 model. For headless user 0 models we don't start Bluetooth Service in user 0. We wait for the first user switch to start - call BluetoothManagerService.handleBootPhase(). However, we also call BluetoothManagerService.handleOnUserSwitch() which ends up restarting the service that was started in handleBootPhase(). This leads to the Bluetooth service not being available due to the extra restart. Changing the loigc to 1. Handle boot phase on User switch if not done before. 2. Handle on switch user otherwise. This saves 6-7 seconds of the time it takes for something like BLE to be available in the automotive builds. Bug: 137289703 Test: Make sure Bluetooth comes up only once with no restarts on boot up and also ensure Bluetooth is restarted when manually switching to another user. Change-Id: I30179f4c2a3f81940258c70045a6ba8b32f315e1 --- .../com/android/server/bluetooth/BluetoothService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 112cf085372..0bcd9373c66 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -53,8 +53,11 @@ class BluetoothService extends SystemService { @Override public void onSwitchUser(int userHandle) { - initialize(); - mBluetoothManagerService.handleOnSwitchUser(userHandle); + if (!mInitialized) { + initialize(); + } else { + mBluetoothManagerService.handleOnSwitchUser(userHandle); + } } @Override -- GitLab From 80b52652df3f3cb2f2d334b9e135ec9830d7bdc5 Mon Sep 17 00:00:00 2001 From: Dan Harms Date: Tue, 6 Aug 2019 09:18:02 -0700 Subject: [PATCH 1050/1408] Include BLE<->SPP transition states in isLeEnabled Fixes: 138997297 Test: Start scan while in transition and observe no exceptions Change-Id: Ib8b05c30c3db8e398194572a179028647e703a9f --- framework/java/android/bluetooth/BluetoothAdapter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 39d63de87da..e7ba85ad5d9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -858,7 +858,10 @@ public final class BluetoothAdapter { if (DBG) { Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); } - return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON); + return (state == BluetoothAdapter.STATE_ON + || state == BluetoothAdapter.STATE_BLE_ON + || state == BluetoothAdapter.STATE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_OFF); } /** -- GitLab From 22d54f6777f64a8f2256aabea420d1e3e9bfff61 Mon Sep 17 00:00:00 2001 From: Ram Periathiruvadi Date: Wed, 31 Jul 2019 15:47:47 -0700 Subject: [PATCH 1051/1408] Fix Handling of user switching for headless user 0 model. For headless user 0 models we don't start Bluetooth Service in user 0. We wait for the first user switch to start - call BluetoothManagerService.handleBootPhase(). However, we also call BluetoothManagerService.handleOnUserSwitch() which ends up restarting the service that was started in handleBootPhase(). This leads to the Bluetooth service not being available due to the extra restart. Changing the loigc to 1. Handle boot phase on User switch if not done before. 2. Handle on switch user otherwise. This saves 6-7 seconds of the time it takes for something like BLE to be available in the automotive builds. Bug: 137289703 Test: Make sure Bluetooth comes up only once with no restarts on boot up and also ensure Bluetooth is restarted when manually switching to another user. Change-Id: I30179f4c2a3f81940258c70045a6ba8b32f315e1 (cherry picked from commit bacd869510eb18b9a04651cd9537418622a4b935) --- .../com/android/server/bluetooth/BluetoothService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 5c5b477352b..6a6ddc84be4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -54,8 +54,11 @@ class BluetoothService extends SystemService { @Override public void onSwitchUser(int userHandle) { - initialize(); - mBluetoothManagerService.handleOnSwitchUser(userHandle); + if (!mInitialized) { + initialize(); + } else { + mBluetoothManagerService.handleOnSwitchUser(userHandle); + } } @Override -- GitLab From b6c698909a604c9d99fbce0fc539480fed7c270c Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 16 Aug 2019 15:33:28 +0800 Subject: [PATCH 1052/1408] Passing caller package name to setBluetoothTethering This is necessary to examine caller's permission. If caller's uid is not same as passing package name, SecurityException would be throwed. This change also clear the identity before calling BluetoothPan#setBluetoothTethering() in Tethering#setBluetoothTethering. This is fine because caller already pass permission check before in ConnectivityService. See the flow below: ConnectivityManager#startTethering -> ConnectivityService#startTethering -> Tethering#startTethering -> Tethering#setBluetoothTethering -> BluetoothPan#setBluetoothTethering Bug: 134649258 Test: -build, flash, boot -atest FrameworkNetTests -manual test with bluetooth OFF/ON Change-Id: I2140398ad3bbc8076f729c843f0515c654553aaf --- framework/java/android/bluetooth/BluetoothPan.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index fb78789ba8a..cfb363a0834 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -118,6 +118,8 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; + private final Context mContext; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PAN, @@ -136,6 +138,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; mProfileConnector.connect(context, listener); } @@ -287,11 +290,12 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { - if (DBG) log("setBluetoothTethering(" + value + ")"); + String pkgName = mContext.getOpPackageName(); + if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value); + service.setBluetoothTethering(value, pkgName); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } -- GitLab From 0388ad1f72c70ca96cabc8b1a4a107877752b187 Mon Sep 17 00:00:00 2001 From: Chris Wailes Date: Wed, 21 Aug 2019 17:22:54 -0700 Subject: [PATCH 1053/1408] Remove a misleading "flush" function. This patch removes LocalSocketImpl.flush(). In practice this function was simply a wrapper around `Thread.sleep(10)`. All direct calls to this function have been removed. The `flush()` function is still called on several objects that wrap a SocketOutputStream. This will make booting a device 20ms faster than it currently is. Bug: 139192244 Test: Build -> flash -> boot -> launch app Change-Id: I0a96f4bc72461670370f61e847349f32af5ac774 --- .../android/bluetooth/BluetoothOutputStream.java | 12 ------------ .../java/android/bluetooth/BluetoothSocket.java | 14 -------------- 2 files changed, 26 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index dfec4e102fd..a0aa2dee9d3 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -75,16 +75,4 @@ import java.io.OutputStream; } mSocket.write(b, offset, count); } - - /** - * Wait until the data in sending queue is emptied. A polling version - * for flush implementation. Use it to ensure the writing data afterwards will - * be packed in the new RFCOMM frame. - * - * @throws IOException if an i/o error occurs. - * @since Android 4.2.3 - */ - public void flush() throws IOException { - mSocket.flush(); - } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index a6e3153d6af..760166bfcc5 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -515,20 +515,6 @@ public final class BluetoothSocket implements Closeable { return mSocketIS.available(); } - /** - * Wait until the data in sending queue is emptied. A polling version - * for flush implementation. Used to ensure the writing data afterwards will - * be packed in new RFCOMM frame. - * - * @throws IOException if an i/o error occurs. - */ - @UnsupportedAppUsage - /*package*/ void flush() throws IOException { - if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); - if (VDBG) Log.d(TAG, "flush: " + mSocketOS); - mSocketOS.flush(); - } - /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); -- GitLab From 7968c27cd011a3ef0b6be3b6954e22662ddd72f5 Mon Sep 17 00:00:00 2001 From: markchien Date: Fri, 16 Aug 2019 15:33:28 +0800 Subject: [PATCH 1054/1408] Passing caller package name to setBluetoothTethering This is necessary to examine caller's permission. If caller's uid is not same as passing package name, SecurityException would be throwed. This change also clear the identity before calling BluetoothPan#setBluetoothTethering() in Tethering#setBluetoothTethering. This is fine because caller already pass permission check before in ConnectivityService. See the flow below: ConnectivityManager#startTethering -> ConnectivityService#startTethering -> Tethering#startTethering -> Tethering#setBluetoothTethering -> BluetoothPan#setBluetoothTethering Bug: 134649258 Test: -build, flash, boot -atest FrameworkNetTests -manual test with bluetooth OFF/ON Change-Id: I2140398ad3bbc8076f729c843f0515c654553aaf Merged-In: I2140398ad3bbc8076f729c843f0515c654553aaf --- framework/java/android/bluetooth/BluetoothPan.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index fb78789ba8a..cfb363a0834 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -118,6 +118,8 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; + private final Context mContext; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PAN, @@ -136,6 +138,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; mProfileConnector.connect(context, listener); } @@ -287,11 +290,12 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { - if (DBG) log("setBluetoothTethering(" + value + ")"); + String pkgName = mContext.getOpPackageName(); + if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value); + service.setBluetoothTethering(value, pkgName); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } -- GitLab From 3d30e625e338d452c2a9e91dc2ad477e8500e5eb Mon Sep 17 00:00:00 2001 From: Ian Kasprzak Date: Sat, 31 Aug 2019 21:28:50 +0000 Subject: [PATCH 1055/1408] Revert "Remove a misleading "flush" function." This reverts commit 0388ad1f72c70ca96cabc8b1a4a107877752b187. Reason for revert: Driodcop: aosp-master test-mapping showing multiple failures (b/140336855). Change-Id: If44e273dd111802db8b44db1e5a67a4628c72e3c --- .../android/bluetooth/BluetoothOutputStream.java | 12 ++++++++++++ .../java/android/bluetooth/BluetoothSocket.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index a0aa2dee9d3..dfec4e102fd 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -75,4 +75,16 @@ import java.io.OutputStream; } mSocket.write(b, offset, count); } + + /** + * Wait until the data in sending queue is emptied. A polling version + * for flush implementation. Use it to ensure the writing data afterwards will + * be packed in the new RFCOMM frame. + * + * @throws IOException if an i/o error occurs. + * @since Android 4.2.3 + */ + public void flush() throws IOException { + mSocket.flush(); + } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 760166bfcc5..a6e3153d6af 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -515,6 +515,20 @@ public final class BluetoothSocket implements Closeable { return mSocketIS.available(); } + /** + * Wait until the data in sending queue is emptied. A polling version + * for flush implementation. Used to ensure the writing data afterwards will + * be packed in new RFCOMM frame. + * + * @throws IOException if an i/o error occurs. + */ + @UnsupportedAppUsage + /*package*/ void flush() throws IOException { + if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); + if (VDBG) Log.d(TAG, "flush: " + mSocketOS); + mSocketOS.flush(); + } + /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); -- GitLab From 4fd500310f8738adfb2d7dd6e32b576ab7b00ab8 Mon Sep 17 00:00:00 2001 From: Christian Wailes Date: Wed, 4 Sep 2019 23:35:54 +0000 Subject: [PATCH 1056/1408] Revert "Revert "Remove a misleading "flush" function."" This reverts commit 3d30e625e338d452c2a9e91dc2ad477e8500e5eb. Reason for revert: Fixed the test broken by the original commit Bug: 139192244 Bug: 140336855 Test: m -> flash -> boot Test: atest CtsJvmtiAttachingHostTestCases Change-Id: I4c67ad8709652c4710ef24564e0240f74f817f8c --- .../android/bluetooth/BluetoothOutputStream.java | 12 ------------ .../java/android/bluetooth/BluetoothSocket.java | 14 -------------- 2 files changed, 26 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index dfec4e102fd..a0aa2dee9d3 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -75,16 +75,4 @@ import java.io.OutputStream; } mSocket.write(b, offset, count); } - - /** - * Wait until the data in sending queue is emptied. A polling version - * for flush implementation. Use it to ensure the writing data afterwards will - * be packed in the new RFCOMM frame. - * - * @throws IOException if an i/o error occurs. - * @since Android 4.2.3 - */ - public void flush() throws IOException { - mSocket.flush(); - } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index a6e3153d6af..760166bfcc5 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -515,20 +515,6 @@ public final class BluetoothSocket implements Closeable { return mSocketIS.available(); } - /** - * Wait until the data in sending queue is emptied. A polling version - * for flush implementation. Used to ensure the writing data afterwards will - * be packed in new RFCOMM frame. - * - * @throws IOException if an i/o error occurs. - */ - @UnsupportedAppUsage - /*package*/ void flush() throws IOException { - if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); - if (VDBG) Log.d(TAG, "flush: " + mSocketOS); - mSocketOS.flush(); - } - /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); -- GitLab From d3238fab19414ee8a286154f62e67b6e252ce6ae Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Thu, 18 Jul 2019 21:45:10 +0800 Subject: [PATCH 1057/1408] Add helpers to check whether the BluetoothCodecConfig instance is selectable There will be a helper in the BluetoothCodecStatus to check whether the codec config is matched the selectable array. It uses 3 smaller helpers to confirm the codec config has none (wildcard matching), or one and only one value for the audio feeding parameters. Besides, this CL also adds a helper to compare whether two codec configs are similar or not, and uses NONE values as wildcard. Bug: 131147224 Bug: 133719424 Test: atest -t BluetoothInstrumentationTests:com.android.bluetooth.a2dp Change-Id: I7d8f1a16b8358c440841801d95471b2d010739ec Merged-In: I7d8f1a16b8358c440841801d95471b2d010739ec (cherry picked from commit 38a8716ef8fc33e5c8915311b932d945c864ae85) --- .../bluetooth/BluetoothCodecConfig.java | 91 +++++++++++++++++++ .../bluetooth/BluetoothCodecStatus.java | 37 ++++++++ 2 files changed, 128 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 591c418c936..36f3a1eeba7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -440,6 +440,43 @@ public final class BluetoothCodecConfig implements Parcelable { return mCodecSpecific4; } + /** + * Checks whether a value set presented by a bitmask has zero or single bit + * + * @param valueSet the value set presented by a bitmask + * @return true if the valueSet contains zero or single bit, otherwise false. + */ + private static boolean hasSingleBit(int valueSet) { + return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); + } + + /** + * Checks whether the object contains none or single sample rate. + * + * @return true if the object contains none or single sample rate, otherwise false. + */ + public boolean hasSingleSampleRate() { + return hasSingleBit(mSampleRate); + } + + /** + * Checks whether the object contains none or single bits per sample. + * + * @return true if the object contains none or single bits per sample, otherwise false. + */ + public boolean hasSingleBitsPerSample() { + return hasSingleBit(mBitsPerSample); + } + + /** + * Checks whether the object contains none or single channel mode. + * + * @return true if the object contains none or single channel mode, otherwise false. + */ + public boolean hasSingleChannelMode() { + return hasSingleBit(mChannelMode); + } + /** * Checks whether the audio feeding parameters are same. * @@ -451,4 +488,58 @@ public final class BluetoothCodecConfig implements Parcelable { && other.mBitsPerSample == mBitsPerSample && other.mChannelMode == mChannelMode); } + + /** + * Checks whether another codec config has the similar feeding parameters. + * Any parameters with NONE value will be considered to be a wildcard matching. + * + * @param other the codec config to compare against + * @return true if the audio feeding parameters are similar, otherwise false. + */ + public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { + if (other == null || mCodecType != other.mCodecType) { + return false; + } + int sampleRate = other.mSampleRate; + if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE + || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + sampleRate = mSampleRate; + } + int bitsPerSample = other.mBitsPerSample; + if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE + || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + bitsPerSample = mBitsPerSample; + } + int channelMode = other.mChannelMode; + if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE + || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + channelMode = mChannelMode; + } + return sameAudioFeedingParameters(new BluetoothCodecConfig( + mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode, + /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0, + /* specific4 */ 0)); + } + + /** + * Checks whether the codec specific parameters are the same. + * + * @param other the codec config to compare against + * @return true if the codec specific parameters are the same, otherwise false. + */ + public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { + if (other == null && mCodecType != other.mCodecType) { + return false; + } + // Currently we only care about the LDAC Playback Quality at CodecSpecific1 + switch (mCodecType) { + case SOURCE_CODEC_TYPE_LDAC: + if (mCodecSpecific1 != other.mCodecSpecific1) { + return false; + } + // fall through + default: + return true; + } + } } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 58b6aeae639..58a764a85be 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -89,6 +89,43 @@ public final class BluetoothCodecStatus implements Parcelable { return Arrays.asList(c1).containsAll(Arrays.asList(c2)); } + /** + * Checks whether the codec config matches the selectable capabilities. + * Any parameters of the codec config with NONE value will be considered a wildcard matching. + * + * @param codecConfig the codec config to compare against + * @return true if the codec config matches, otherwise false + */ + public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + if (codecConfig == null || !codecConfig.hasSingleSampleRate() + || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { + return false; + } + for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) { + if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { + continue; + } + int sampleRate = codecConfig.getSampleRate(); + if ((sampleRate & selectableConfig.getSampleRate()) == 0 + && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) { + continue; + } + int bitsPerSample = codecConfig.getBitsPerSample(); + if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0 + && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + continue; + } + int channelMode = codecConfig.getChannelMode(); + if ((channelMode & selectableConfig.getChannelMode()) == 0 + && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) { + continue; + } + return true; + } + return false; + } + + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, -- GitLab From 600f7365d8ebc80f610634b68aefa2f8648c5cba Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 11 Oct 2019 15:49:37 -0700 Subject: [PATCH 1058/1408] Bluetooth doc: The is a -> This is a Fixes: 142364826 Test: build Change-Id: I9bde35140bdfc4d76df7e8c427ac6e8baceee611 --- framework/java/android/bluetooth/le/ScanRecord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 30868bfeac6..97e3f5221c7 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -154,7 +154,7 @@ public final class ScanRecord { } /** - * Returns the local name of the BLE device. The is a UTF-8 encoded string. + * Returns the local name of the BLE device. This is a UTF-8 encoded string. */ @Nullable public String getDeviceName() { -- GitLab From 5eb1894128ce092c434706dcb6412419db6baeed Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 25 Oct 2019 13:15:12 -0700 Subject: [PATCH 1059/1408] Refactor BluetoothAdapter APIs used by Settings Bug: 143238544 Test: Manual Change-Id: Ia831d2b4b628b746493602857bcb54b585f13e12 --- .../android/bluetooth/BluetoothAdapter.java | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e7ba85ad5d9..566b38738dc 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1189,13 +1189,11 @@ public final class BluetoothAdapter { /** * Factory reset bluetooth settings. * - *

          Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} - * permission - * * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { mServiceLock.readLock().lock(); @@ -1214,13 +1212,12 @@ public final class BluetoothAdapter { /** * Get the UUIDs supported by the local Bluetooth adapter. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * * @return the UUIDs supported by the local Bluetooth Adapter. * @hide */ @UnsupportedAppUsage - public ParcelUuid[] getUuids() { + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @NonNull ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; } @@ -1476,7 +1473,6 @@ public final class BluetoothAdapter { * will return false. After turning on Bluetooth, * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} * to get the updated value. - *

          Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} *

          Applications cannot set the scan mode. They should use * startActivityForResult( * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) @@ -1488,8 +1484,8 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " - + "shows UI that confirms the user wants to go into discoverable mode.") + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) { return false; @@ -1507,9 +1503,34 @@ public final class BluetoothAdapter { return false; } - /** @hide */ - @UnsupportedAppUsage - public boolean setScanMode(int mode) { + /** + * Set the Bluetooth scan mode of the local Bluetooth adapter. + *

          The Bluetooth scan mode determines if the local adapter is + * connectable and/or discoverable from remote Bluetooth devices. + *

          For privacy reasons, discoverable mode is automatically turned off + * after duration seconds. For example, 120 seconds should be + * enough for a remote device to initiate and complete its discovery + * process. + *

          Valid scan mode values are: + * {@link #SCAN_MODE_NONE}, + * {@link #SCAN_MODE_CONNECTABLE}, + * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + *

          If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. + *

          Applications cannot set the scan mode. They should use + * startActivityForResult( + * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) + * instead. + * + * @param mode valid scan mode + * @return true if the scan mode was set, false otherwise + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; } @@ -1562,6 +1583,8 @@ public final class BluetoothAdapter { * been called recently. * @hide */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); @@ -2060,7 +2083,7 @@ public final class BluetoothAdapter { * BluetoothProfile}. * @hide */ - public List getSupportedProfiles() { + public @NonNull List getSupportedProfiles() { final ArrayList supportedProfiles = new ArrayList(); try { -- GitLab From e521618ea3669e1a0d03363e4eb1fd8db2384f97 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 22 Jul 2019 15:56:23 +0800 Subject: [PATCH 1060/1408] Add method to convert profile ID into human readable string Bug: 136068566 Test: adb shell dumpsys bluetooth_manager Change-Id: I9f354ecbe20caf058c9de843180b7a07de7af569 --- .../android/bluetooth/BluetoothProfile.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index dabe0fdac39..f5aa0145848 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -324,4 +324,54 @@ public interface BluetoothProfile { return "STATE_UNKNOWN"; } } + + /** + * Convert an integer value of profile ID into human readable string + * + * @param profile profile ID + * @return profile name as String, UNKOWN_PROFILE if the profile ID is not defined. + * @hide + */ + static String getProfileName(int profile) { + switch(profile) { + case HEADSET: + return "HEADSET"; + case A2DP: + return "A2DP"; + case HID_HOST: + return "HID_HOST"; + case PAN: + return "PAN"; + case PBAP: + return "PBAP"; + case GATT: + return "GATT"; + case GATT_SERVER: + return "GATT_SERVER"; + case MAP: + return "MAP"; + case SAP: + return "SAP"; + case A2DP_SINK: + return "A2DP_SINK"; + case AVRCP_CONTROLLER: + return "AVRCP_CONTROLLER"; + case AVRCP: + return "AVRCP"; + case HEADSET_CLIENT: + return "HEADSET_CLIENT"; + case PBAP_CLIENT: + return "PBAP_CLIENT"; + case MAP_CLIENT: + return "MAP_CLIENT"; + case HID_DEVICE: + return "HID_DEVICE"; + case OPP: + return "OPP"; + case HEARING_AID: + return "HEARING_AID"; + default: + return "UNKNOWN_PROFILE"; + } + } } -- GitLab From d00637913fa8db7e49655d1bdf236ad8fd90ebb8 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 22 Oct 2019 13:37:29 -0700 Subject: [PATCH 1061/1408] Refactor methods to access/modify BluetoothDevice alias and name Bug: 142352567 Test: Manual Change-Id: I86bb69faed1c6d46ac668816e6081bd1673fbf85 --- .../android/bluetooth/BluetoothDevice.java | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c6160446c79..0be3eca8239 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -173,13 +173,10 @@ public final class BluetoothDevice implements Parcelable { * changed. *

          Always contains the extra field {@link #EXTRA_DEVICE}. *

          Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage public static final String ACTION_ALIAS_CHANGED = - "android.bluetooth.device.action.ALIAS_CHANGED"; + "android.bluetooth.action.ALIAS_CHANGED"; /** * Broadcast Action: Indicates a change in the bond state of a remote @@ -1048,10 +1045,11 @@ public final class BluetoothDevice implements Parcelable { * Get the Bluetooth alias of the remote device. *

          Alias is the locally modified name of a remote device. * - * @return the Bluetooth alias, or null if no alias or there was a problem - * @hide + * @return the Bluetooth alias, the friendly device name if no alias, or + * null if there was a problem */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") + @Nullable + @RequiresPermission(Manifest.permission.BLUETOOTH) public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -1059,7 +1057,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteAlias(this); + String alias = service.getRemoteAlias(this); + if (alias == null) { + return getName(); + } + return alias; } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1076,8 +1078,9 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage - public boolean setAlias(String alias) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); @@ -1091,24 +1094,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** - * Get the Bluetooth alias of the remote device. - * If Alias is null, get the Bluetooth name instead. - * - * @return the Bluetooth alias, or null if no alias or there was a problem - * @hide - * @see #getAlias() - * @see #getName() - */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") - public String getAliasName() { - String name = getAlias(); - if (name == null) { - name = getName(); - } - return name; - } - /** * Get the most recent identified battery level of this Bluetooth device *

          Requires {@link android.Manifest.permission#BLUETOOTH} -- GitLab From 634b8b303b1c8d3e56a9fff79a7528a7856ab2b7 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 13 Nov 2019 16:21:12 -0800 Subject: [PATCH 1062/1408] Create systemapis to connect/disconnect all bt profiles Bug: 143495377 Test: Manual Change-Id: I80d816083ef568df319afddfd4557ef74d37d16f --- .../android/bluetooth/BluetoothAdapter.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 566b38738dc..9d152a7faf4 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1733,6 +1733,56 @@ public final class BluetoothAdapter { return false; } + /** + * Connects all enabled and supported bluetooth profiles between the local and remote device + * + * @param device is the remote device with which to connect these profiles + * @return true if all profiles successfully connected, false if an error occurred + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.connectAllEnabledProfiles(device); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** + * Disconnects all enabled and supported bluetooth profiles between the local and remote device + * + * @param device is the remote device with which to disconnect these profiles + * @return true if all profiles successfully disconnected, false if an error occurred + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.disconnectAllEnabledProfiles(device); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + /** * Return true if the multi advertisement is supported by the chipset * -- GitLab From 4d42daa9c2f6303729e55a97179c4ebce16dc9ee Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Sat, 16 Nov 2019 11:52:13 -0800 Subject: [PATCH 1063/1408] Re-add getAliasName to fix build breakage Bug: 144619887 Test: Manual Change-Id: Ib4f3582a1c9cb8cbcb14fece6a3ab463b3400cea --- .../android/bluetooth/BluetoothDevice.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0be3eca8239..19f42b6a4c9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1094,6 +1094,24 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Get the Bluetooth alias of the remote device. + * If Alias is null, get the Bluetooth name instead. + * + * @return the Bluetooth alias, or null if no alias or there was a problem + * @hide + * @see #getAlias() + * @see #getName() + */ + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") + public String getAliasName() { + String name = getAlias(); + if (name == null) { + name = getName(); + } + return name; + } + /** * Get the most recent identified battery level of this Bluetooth device *

          Requires {@link android.Manifest.permission#BLUETOOTH} -- GitLab From c924e95831fed3bc4e7a92c653f008f7a3884ec4 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 18 Nov 2019 18:59:55 +0000 Subject: [PATCH 1064/1408] Revert "Re-add getAliasName to fix build breakage" This reverts commit 4d42daa9c2f6303729e55a97179c4ebce16dc9ee. Reason for revert: Removing method again and will make changes on internal to prevent breakage Change-Id: I41cb918df90a203fd6a03f4b51464300de9428f8 --- .../android/bluetooth/BluetoothDevice.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 19f42b6a4c9..0be3eca8239 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1094,24 +1094,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** - * Get the Bluetooth alias of the remote device. - * If Alias is null, get the Bluetooth name instead. - * - * @return the Bluetooth alias, or null if no alias or there was a problem - * @hide - * @see #getAlias() - * @see #getName() - */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") - public String getAliasName() { - String name = getAlias(); - if (name == null) { - name = getName(); - } - return name; - } - /** * Get the most recent identified battery level of this Bluetooth device *

          Requires {@link android.Manifest.permission#BLUETOOTH} -- GitLab From 4ce73d71c1607f519f7ac7ca32052df5ab1e9587 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 19 Nov 2019 18:26:48 -0800 Subject: [PATCH 1065/1408] DO NOT MERGE Revert "Merge "Revert "Re-add getAliasName to fix build breakage"" am: 2ad71dd2fc" This reverts commit 99734c8977ce1128e431cf75907575fab9a68c1a, reversing changes made to d26ea8807f7337a7e540944921932db9277b46a7. Bug: 144619932 Test: Manual Change-Id: Ie9a46c9527004759a250dc71d2a1065ff3bb7328 --- .../android/bluetooth/BluetoothDevice.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0be3eca8239..19f42b6a4c9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1094,6 +1094,24 @@ public final class BluetoothDevice implements Parcelable { return false; } + /** + * Get the Bluetooth alias of the remote device. + * If Alias is null, get the Bluetooth name instead. + * + * @return the Bluetooth alias, or null if no alias or there was a problem + * @hide + * @see #getAlias() + * @see #getName() + */ + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") + public String getAliasName() { + String name = getAlias(); + if (name == null) { + name = getName(); + } + return name; + } + /** * Get the most recent identified battery level of this Bluetooth device *

          Requires {@link android.Manifest.permission#BLUETOOTH} -- GitLab From b9bbad77ce5755b54502a6189c12ff97baf88547 Mon Sep 17 00:00:00 2001 From: David Castro Date: Fri, 15 Nov 2019 23:51:03 +0000 Subject: [PATCH 1066/1408] Removed the confusing note as per guidance from mylesgw@ in BUG:117309989. Change-Id: I07680c04798642d21420bbea5c6b592c1c741f43 --- framework/java/android/bluetooth/le/ScanFilter.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 038994fb553..7511fd051e4 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -671,8 +671,6 @@ public final class ScanFilter implements Parcelable { /** * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. - *

          - * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. * * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. */ -- GitLab From eeccce5a2e6aede9f5eeb7e3612c45e9452e71f0 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 19 Nov 2019 14:54:25 -0800 Subject: [PATCH 1067/1408] Resolve API dependencies on BluetoothPan Bug: 143244283 Test: Manual Change-Id: Ie419b2f83358d06d094dcf4921c4595fc0e72857 --- .../java/android/bluetooth/BluetoothPan.java | 43 ++++++++++++++++--- .../android/bluetooth/BluetoothProfile.java | 6 ++- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index cfb363a0834..4e9762737c0 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -16,8 +16,13 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -25,6 +30,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -40,6 +47,7 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; @@ -67,6 +75,7 @@ public final class BluetoothPan implements BluetoothProfile { *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */ + @SuppressLint("ActionValue") @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; @@ -76,19 +85,32 @@ public final class BluetoothPan implements BluetoothProfile { * The local role of the PAN profile that the remote device is bound to. * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}. */ + @SuppressLint("ActionValue") public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + /** @hide */ + @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface LocalPanRole {} + public static final int PAN_ROLE_NONE = 0; /** * The local device is acting as a Network Access Point. */ public static final int LOCAL_NAP_ROLE = 1; - public static final int REMOTE_NAP_ROLE = 1; /** * The local device is acting as a PAN User. */ public static final int LOCAL_PANU_ROLE = 2; + + /** @hide */ + @IntDef({PAN_ROLE_NONE, REMOTE_NAP_ROLE, REMOTE_PANU_ROLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface RemotePanRole {} + + public static final int REMOTE_NAP_ROLE = 1; + public static final int REMOTE_PANU_ROLE = 2; /** @@ -134,6 +156,8 @@ public final class BluetoothPan implements BluetoothProfile { /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile + * + * @hide */ @UnsupportedAppUsage /*package*/ BluetoothPan(Context context, ServiceListener listener) { @@ -235,7 +259,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ @Override - public List getConnectedDevices() { + public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothPan service = getService(); if (service != null && isEnabled()) { @@ -252,6 +276,7 @@ public final class BluetoothPan implements BluetoothProfile { /** * {@inheritDoc} + * @hide */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -273,7 +298,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(BluetoothDevice device) { + public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -288,7 +313,11 @@ public final class BluetoothPan implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - @UnsupportedAppUsage + /** + * Turns on/off bluetooth tethering + * + * @param value is whether to enable or disable bluetooth tethering + */ public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -302,7 +331,11 @@ public final class BluetoothPan implements BluetoothProfile { } } - @UnsupportedAppUsage + /** + * Determines whether tethering is enabled + * + * @return true if tethering is on, false if not or some error occurred + */ public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f5aa0145848..f1ac7652748 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -20,9 +20,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; -import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -43,6 +43,7 @@ public interface BluetoothProfile { * This extra represents the current connection state of the profile of the * Bluetooth device. */ + @SuppressLint("ActionValue") String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; /** @@ -51,6 +52,7 @@ public interface BluetoothProfile { * This extra represents the previous connection state of the profile of the * Bluetooth device. */ + @SuppressLint("ActionValue") String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE"; @@ -106,7 +108,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @SystemApi int PAN = 5; /** -- GitLab From 96067532ad3ca7966ea5cfd5ebf966675f9a9334 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Wed, 20 Nov 2019 17:59:11 +0800 Subject: [PATCH 1068/1408] Limit the retry attemps on restarting Bluetooth * Back off the delay time of resarting Bluetooth when there's continous turn on failures. * Stop to restart Bluetooth if we are seeing back to back Bluetooth crashes, so we won't see endless diaglogs popping out. Bug: 144572641 Test: Manual Change-Id: Ifeffa214256eab61901c123fc88b037fc6d7dd50 --- .../bluetooth/BluetoothManagerService.java | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 208b63888ef..798a4c663c5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -98,7 +98,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind //Maximum msec to wait for service restart - private static final int SERVICE_RESTART_TIME_MS = 200; + private static final int SERVICE_RESTART_TIME_MS = 400; //Maximum msec to wait for restart due to error private static final int ERROR_RESTART_TIME_MS = 3000; //Maximum msec to delay MESSAGE_USER_SWITCHED @@ -1635,13 +1635,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // ActivityManager detects it. // The waiting for (b) and (c) is accomplished by // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE - // message. On slower devices, that delay needs to be - // on the order of (2 * SERVICE_RESTART_TIME_MS). + // message. The delay time is backed off if Bluetooth + // continuously failed to turn on itself. // waitForOnOff(false, true); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); } break; @@ -1820,7 +1820,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { waitForOnOff(false, true); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); } } if (newState == BluetoothAdapter.STATE_ON @@ -1863,7 +1863,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Send a Bluetooth Restart message Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, SERVICE_RESTART_TIME_MS); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); } sendBluetoothServiceDownCallback(); @@ -1886,14 +1886,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; } case MESSAGE_RESTART_BLUETOOTH_SERVICE: { - Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE"); - /* Enable without persisting the setting as - it doesnt change when IBluetooth - service restarts */ - mEnable = true; - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED, - mContext.getPackageName(), true); - handleEnable(mQuietEnable); + mErrorRecoveryRetryCounter++; + Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count=" + + mErrorRecoveryRetryCounter); + if (mErrorRecoveryRetryCounter < MAX_ERROR_RESTART_RETRIES) { + /* Enable without persisting the setting as + it doesnt change when IBluetooth + service restarts */ + mEnable = true; + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED, + mContext.getPackageName(), true); + handleEnable(mQuietEnable); + } else { + Slog.e(TAG, "Reach maximum retry to restart Bluetooth!"); + } break; } case MESSAGE_TIMEOUT_BIND: { @@ -2332,13 +2338,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnable = false; - if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) { - // Send a Bluetooth Restart message to reenable bluetooth - Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); - } else { - // todo: notify user to power down and power up phone to make bluetooth work. - } + // Send a Bluetooth Restart message to reenable bluetooth + Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); } private boolean isBluetoothDisallowed() { @@ -2374,6 +2376,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private int getServiceRestartMs() { + return (mErrorRecoveryRetryCounter + 1) * SERVICE_RESTART_TIME_MS; + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) { -- GitLab From b997c1f1fa16b9658adfb10a770dc9b1baedb018 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Tue, 26 Nov 2019 21:30:07 -0800 Subject: [PATCH 1069/1408] Implement isEnabled in terms of getState Bug: 145171640 Test: compile & check everything works normally Change-Id: I169e220ab83546d76317e518652c66c9bd2229a2 --- .../android/bluetooth/BluetoothAdapter.java | 13 +------------ .../bluetooth/BluetoothManagerService.java | 17 +---------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9d152a7faf4..6ec942c07fa 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -830,18 +830,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isEnabled(); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; + return getState() == BluetoothAdapter.STATE_ON; } /** diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 208b63888ef..2827bd32611 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -613,22 +613,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean isEnabled() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "isEnabled(): not allowed for non-active and non system user"); - return false; - } - - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - return mBluetooth.isEnabled(); - } - } catch (RemoteException e) { - Slog.e(TAG, "isEnabled()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return false; + return getState() == BluetoothAdapter.STATE_ON; } public int getState() { -- GitLab From e76a4104da52fa6a2edfda86ddb5e3d124efb734 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Tue, 26 Nov 2019 21:46:08 -0800 Subject: [PATCH 1070/1408] Call parameterized version of IBluetooth.enable Bug: 145171640 Test: compile & verify starts Change-Id: I725d1b81b78455b7febe250161d9eb9859434d2a --- .../bluetooth/BluetoothManagerService.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2827bd32611..87149d37f1b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1751,14 +1751,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (!mQuietEnable) { - if (!mBluetooth.enable()) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } else { - if (!mBluetooth.enableNoAutoConnect()) { - Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false"); - } + if (!mBluetooth.enable(mQuietEnable)) { + Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { Slog.e(TAG, "Unable to call enable()", e); @@ -2027,14 +2021,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (mBluetooth != null) { //Enable bluetooth try { - if (!mQuietEnable) { - if (!mBluetooth.enable()) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } else { - if (!mBluetooth.enableNoAutoConnect()) { - Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false"); - } + if (!mBluetooth.enable(mQuietEnable)) { + Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { Slog.e(TAG, "Unable to call enable()", e); -- GitLab From fd32c4bfb97f7ebf6b1755d1f5c65b2a6ac6a35c Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Tue, 26 Nov 2019 21:55:15 -0800 Subject: [PATCH 1071/1408] Collapse the implementations of BluetoothDevice.createBond The backend is now a single function. Remove duplicate code/ Bug: 145171640 Test: compile, and try pairing Change-Id: I04e8f95430d0de245926e849ea71eff030294bc1 --- .../android/bluetooth/BluetoothDevice.java | 35 ++----------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0be3eca8239..49187dcde34 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1131,20 +1131,7 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); - return false; - } - try { - Log.i(TAG, "createBond() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.createBond(this, TRANSPORT_AUTO); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; + return createBond(TRANSPORT_AUTO); } /** @@ -1165,23 +1152,7 @@ public final class BluetoothDevice implements Parcelable { */ @UnsupportedAppUsage public boolean createBond(int transport) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); - return false; - } - if (TRANSPORT_AUTO > transport || transport > TRANSPORT_LE) { - throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); - } - try { - Log.i(TAG, "createBond() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.createBond(this, transport); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; + return createBondOutOfBand(transport, null); } /** @@ -1209,7 +1180,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.createBondOutOfBand(this, transport, oobData); + return service.createBond(this, transport, oobData); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From e8bac9b871a87aa38e99956c380069c3c377eb79 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 27 Nov 2019 18:09:33 -0800 Subject: [PATCH 1072/1408] Rename priority to connection policy in bluetooth apis Bug: 145005327 Test: Manual Change-Id: I43ad57feb7dd70f39005ad7a01bc7dac6fb7b639 --- .../java/android/bluetooth/BluetoothA2dp.java | 62 +++++++++++++---- .../android/bluetooth/BluetoothA2dpSink.java | 65 +++++++++++++---- .../android/bluetooth/BluetoothAdapter.java | 45 ++++++++++++ .../android/bluetooth/BluetoothHeadset.java | 59 +++++++++++++--- .../bluetooth/BluetoothHeadsetClient.java | 69 +++++++++++++++++-- .../bluetooth/BluetoothHearingAid.java | 61 ++++++++++++---- .../android/bluetooth/BluetoothHidHost.java | 65 +++++++++++++---- .../java/android/bluetooth/BluetoothMap.java | 62 ++++++++++++++--- .../android/bluetooth/BluetoothMapClient.java | 64 ++++++++++++++--- .../bluetooth/BluetoothPbapClient.java | 68 ++++++++++++++---- .../android/bluetooth/BluetoothProfile.java | 36 ++++++++++ .../java/android/bluetooth/BluetoothSap.java | 58 ++++++++++++++-- 12 files changed, 601 insertions(+), 113 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1fe1b10005c..accdd8dc64b 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -17,10 +17,12 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -439,28 +441,45 @@ public final class BluetoothA2dp implements BluetoothProfile { * Set priority of the profile * *

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

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. + * 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("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(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); try { final IBluetoothA2dp 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; } - return service.setPriority(device, priority); + return service.setConnectionPolicy(device, connectionPolicy); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -474,8 +493,7 @@ public final class BluetoothA2dp 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 @@ -485,17 +503,35 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("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 @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.getPriority(device); + return service.getConnectionPolicy(device); } if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } } diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 5a8055a29cf..c17834aa8e5 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.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.content.Context; import android.os.Binder; @@ -317,27 +320,43 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Set priority of the profile * *

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

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. + * 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("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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink 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, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -351,28 +370,44 @@ public final class BluetoothA2dpSink 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} - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * {@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("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("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getPriority(device); + return service.getConnectionPolicy(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } } if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } /** diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9d152a7faf4..9d6be568a28 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -27,6 +27,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; @@ -3381,4 +3382,48 @@ public final class BluetoothAdapter { void onMetadataChanged(@NonNull BluetoothDevice device, int key, @Nullable byte[] value); } + + /** + * Converts old constant of priority to the new for connection policy + * + * @param priority is the priority to convert to connection policy + * @return the equivalent connection policy constant to the priority + * + * @hide + */ + public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) { + switch(priority) { + case BluetoothProfile.PRIORITY_AUTO_CONNECT: + return BluetoothProfile.CONNECTION_POLICY_ALLOWED; + case BluetoothProfile.PRIORITY_ON: + return BluetoothProfile.CONNECTION_POLICY_ALLOWED; + case BluetoothProfile.PRIORITY_OFF: + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + case BluetoothProfile.PRIORITY_UNDEFINED: + return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; + default: + Log.e(TAG, "setPriority: Invalid priority: " + priority); + return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; + } + } + + /** + * Converts new constant of connection policy to the old for priority + * + * @param connectionPolicy is the connection policy to convert to priority + * @return the equivalent priority constant to the connectionPolicy + * + * @hide + */ + public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) { + switch(connectionPolicy) { + case BluetoothProfile.CONNECTION_POLICY_ALLOWED: + return BluetoothProfile.PRIORITY_ON; + case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN: + return BluetoothProfile.PRIORITY_OFF; + case BluetoothProfile.CONNECTION_POLICY_UNKNOWN: + return BluetoothProfile.PRIORITY_UNDEFINED; + } + return BluetoothProfile.PRIORITY_UNDEFINED; + } } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 672174f8f76..ea3831a869f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -560,26 +561,45 @@ public final class BluetoothHeadset implements BluetoothProfile { * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or * {@link BluetoothProfile#PRIORITY_OFF}, * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Paired bluetooth device * @param priority * @return true if priority is set, false on error * @hide + * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)} */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("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(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadset service = mService; 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; @@ -596,26 +616,43 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth device * @return priority of the device * @hide */ @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("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 @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; 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; } /** diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 5d00f09501b..a8e1fd26ad4 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.content.Context; import android.os.Binder; @@ -553,19 +556,45 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Set priority of the profile * - * The device should already be paired. + *

          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("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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadsetClient 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; @@ -577,21 +606,47 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Get the priority of the profile. + * + *

          The priority can be any of: + * {@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("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("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient 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; } /** diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index a812c32ae86..ead8429e4a7 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -355,28 +356,45 @@ public final class BluetoothHearingAid implements BluetoothProfile { * Set priority of the profile * *

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

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. + * 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("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(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHearingAid service = getService(); try { 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; } - return service.setPriority(device, priority); + return service.setConnectionPolicy(device, connectionPolicy); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -390,8 +408,7 @@ public final class BluetoothHearingAid 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 @@ -400,17 +417,35 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("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 @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getPriority(device); + return service.getConnectionPolicy(device); } if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 4afb382c066..8f5cdf01f57 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -16,8 +16,11 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -379,27 +382,43 @@ public final class BluetoothHidHost 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}, - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. + * 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("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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHidHost 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, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -413,28 +432,44 @@ public final class BluetoothHidHost 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} - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * {@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("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("getConnectionPolicy(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getPriority(device); + return service.getConnectionPolicy(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } } if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index dd2f150ad4e..979dfd4e3ba 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.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.content.Context; import android.os.Binder; @@ -271,23 +274,43 @@ public final class BluetoothMap 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}, + * 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("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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap 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; @@ -301,25 +324,44 @@ public final class BluetoothMap 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("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("getConnectionPolicy(" + device + ")"); final IBluetoothMap 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; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 69682c6ab5d..0ec473c85ad 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; } /** diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d70e1e7f3f5..9618ba01572 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -241,25 +244,45 @@ public final class BluetoothPbapClient 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}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, * * @param device Paired bluetooth device - * @param priority Priority of this profile + * @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("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("setPriority(" + device + ", " + priority + ")"); + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); } final IBluetoothPbapClient 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; @@ -275,28 +298,47 @@ public final class BluetoothPbapClient 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("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("getPriority(" + device + ")"); + log("getConnectionPolicy(" + device + ")"); } final IBluetoothPbapClient 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; } } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f1ac7652748..097a3677ac4 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -225,7 +225,9 @@ public interface BluetoothProfile { * and outgoing connections for the profile * * @hide + * @deprecated Replaced with {@link #CONNECTION_POLICY_ALLOWED} **/ + @Deprecated @SystemApi int PRIORITY_ON = 100; @@ -234,7 +236,9 @@ public interface BluetoothProfile { * connections and outgoing connections for the profile. * * @hide + * @deprecated Replaced with {@link #CONNECTION_POLICY_FORBIDDEN} **/ + @Deprecated @SystemApi int PRIORITY_OFF = 0; @@ -246,6 +250,38 @@ public interface BluetoothProfile { @UnsupportedAppUsage int PRIORITY_UNDEFINED = -1; + /** @hide */ + @IntDef(prefix = "CONNECTION_POLICY_", value = {CONNECTION_POLICY_ALLOWED, + CONNECTION_POLICY_FORBIDDEN, CONNECTION_POLICY_UNKNOWN}) + @Retention(RetentionPolicy.SOURCE) + public @interface ConnectionPolicy{} + + /** + * Default connection policy for devices that allow incoming and outgoing connections + * for the profile + * + * @hide + **/ + @SystemApi + int CONNECTION_POLICY_ALLOWED = 100; + + /** + * Default connection policy for devices that do not allow incoming or outgoing connections + * for the profile. + * + * @hide + **/ + @SystemApi + int CONNECTION_POLICY_FORBIDDEN = 0; + + /** + * Default connection policy when not set or when the device is unpaired + * + * @hide + */ + @SystemApi + int CONNECTION_POLICY_UNKNOWN = -1; + /** * Get connected devices for this specific profile. * diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index e0610c89055..9b4dabcdcfd 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.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.content.Context; import android.os.Binder; @@ -300,22 +303,43 @@ public final class BluetoothSap 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}, * * @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("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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothSap 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; @@ -328,23 +352,45 @@ public final class BluetoothSap implements BluetoothProfile { /** * Get the priority of the profile. * + *

          The priority can be any of: + * {@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("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("getConnectionPolicy(" + device + ")"); final IBluetoothSap 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; } private static void log(String msg) { -- GitLab From 4a9070a30ac81d1cfdcd4162ad55e48f7b10d582 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 25 Nov 2019 11:16:10 -0800 Subject: [PATCH 1073/1408] Add SystemApi to setActiveDevice in BluetoothAdapter Bug: 145004683 Test: Manual Change-Id: I70f46bb0153a8a0f3755dc2b3e26e556bd092daa --- .../android/bluetooth/BluetoothAdapter.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9d152a7faf4..89f9cbc0488 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -456,6 +456,37 @@ public final class BluetoothAdapter { @Retention(RetentionPolicy.SOURCE) public @interface IoCapability {} + /** @hide */ + @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO, + ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL}) + @Retention(RetentionPolicy.SOURCE) + public @interface ActiveDeviceUse {} + + /** + * Use the specified device for audio (a2dp and hearing aid profile) + * + * @hide + */ + @SystemApi + public static final int ACTIVE_DEVICE_AUDIO = 0; + + /** + * Use the specified device for phone calls (headset profile and hearing + * aid profile) + * + * @hide + */ + @SystemApi + public static final int ACTIVE_DEVICE_PHONE_CALL = 1; + + /** + * Use the specified device for a2dp, hearing aid profile, and headset profile + * + * @hide + */ + @SystemApi + public static final int ACTIVE_DEVICE_ALL = 2; + /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -1733,6 +1764,41 @@ public final class BluetoothAdapter { return false; } + /** + * + * @param device is the remote bluetooth device + * @param profiles represents the purpose for which we are setting this as the active device. + * Possible values are: + * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, + * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, + * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} + * @return false on immediate error, true otherwise + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setActiveDevice(@Nullable BluetoothDevice device, + @ActiveDeviceUse int profiles) { + if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL + && profiles != ACTIVE_DEVICE_ALL) { + Log.e(TAG, "Invalid profiles param value in setActiveDevice"); + return false; + } + + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.setActiveDevice(device, profiles); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + /** * Connects all enabled and supported bluetooth profiles between the local and remote device * -- GitLab From 028e5cae047509a536304398c92e87f369a296bb Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 29 Nov 2019 22:11:11 +0100 Subject: [PATCH 1074/1408] Do not call SystemConfig.getInstance() from Bluetooth (1/2) Move call to SystemConfig.getInstance into BluetoothManagerService Bug: 145297991 Change-Id: I6edd91b831a240117757cb6683d8a373e861db99 --- .../bluetooth/BluetoothManagerService.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 798a4c663c5..a3188443591 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -74,6 +74,7 @@ import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; +import java.util.ArrayList; import java.util.LinkedList; import java.util.Locale; import java.util.Map; @@ -695,6 +696,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mIsHearingAidProfileSupported; } + @Override + /** @hide */ + public java.util.List getSystemConfigEnabledProfilesForPackage(String packageName) { + if (Binder.getCallingUid() != Process.BLUETOOTH_UID) { + Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth"); + return null; + } + + SystemConfig systemConfig = SystemConfig.getInstance(); + if (systemConfig == null) { + return null; + } + + android.util.ArrayMap componentEnabledStates = + systemConfig.getComponentsEnabledStates(packageName); + if (componentEnabledStates == null) { + return null; + } + + ArrayList enabledProfiles = new ArrayList(); + for (Map.Entry entry : componentEnabledStates.entrySet()) { + if (entry.getValue()) { + enabledProfiles.add(entry.getKey()); + } + } + + return enabledProfiles; + } + // Monitor change of BLE scan only mode settings. private void registerForBleScanModeChange() { ContentObserver contentObserver = new ContentObserver(null) { -- GitLab From bc9f48f906fd310c20ffecd603d4d8105d500e16 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 3 Dec 2019 11:48:05 -0800 Subject: [PATCH 1075/1408] Make BluetoothUuid constants and some functions System Apis Bug: 143245929 Test: Manual Change-Id: I5519a379a2bb5d56fe25745d8bdeaa5cf52d9e92 --- .../java/android/bluetooth/BluetoothA2dp.java | 2 +- .../java/android/bluetooth/BluetoothUuid.java | 258 ++++++++---------- 2 files changed, 121 insertions(+), 139 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index accdd8dc64b..8ed61b6c87e 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -610,7 +610,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (uuids == null) return false; for (ParcelUuid uuid : uuids) { - if (BluetoothUuid.isAvrcpTarget(uuid)) { + if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) { return true; } } diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index bc3c9a9ebf4..7e96c23af4b 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -31,6 +33,7 @@ import java.util.UUID; * * @hide */ +@SystemApi public final class BluetoothUuid { /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs @@ -39,167 +42,157 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid AudioSink = + + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid A2DP_SINK = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AudioSource = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid A2DP_SOURCE = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid AdvAudioDist = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid ADV_AUDIO_DIST = ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid HSP = ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid HSP_AG = ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final ParcelUuid Handsfree = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HFP = ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid Handsfree_AG = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HFP_AG = ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AvrcpController = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid AVRCP_CONTROLLER = ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid AvrcpTarget = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid AVRCP_TARGET = ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage - public static final ParcelUuid ObexObjectPush = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid OBEX_OBJECT_PUSH = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); - public static final ParcelUuid Hid = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HID = ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); - @UnsupportedAppUsage - public static final ParcelUuid Hogp = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HOGP = ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PANU = ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid NAP = ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BNEP = ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PBAP_PCE = ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); - @UnsupportedAppUsage + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid PBAP_PSE = ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MAP = ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MNS = ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid SAP = ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); - public static final ParcelUuid HearingAid = + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid HEARING_AID = ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); - /** Length of bytes for 16 bit UUID */ + /** + * Length of bytes for 16 bit UUID + * + * @hide + */ + @SystemApi public static final int UUID_BYTES_16_BIT = 2; - /** Length of bytes for 32 bit UUID */ + /** + * Length of bytes for 32 bit UUID + * + * @hide + */ + @SystemApi public static final int UUID_BYTES_32_BIT = 4; - /** Length of bytes for 128 bit UUID */ - public static final int UUID_BYTES_128_BIT = 16; - - @UnsupportedAppUsage - public static final ParcelUuid[] RESERVED_UUIDS = { - AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP}; - - @UnsupportedAppUsage - public static boolean isAudioSource(ParcelUuid uuid) { - return uuid.equals(AudioSource); - } - - public static boolean isAudioSink(ParcelUuid uuid) { - return uuid.equals(AudioSink); - } - - @UnsupportedAppUsage - public static boolean isAdvAudioDist(ParcelUuid uuid) { - return uuid.equals(AdvAudioDist); - } - - public static boolean isHandsfree(ParcelUuid uuid) { - return uuid.equals(Handsfree); - } - - public static boolean isHeadset(ParcelUuid uuid) { - return uuid.equals(HSP); - } - - public static boolean isAvrcpController(ParcelUuid uuid) { - return uuid.equals(AvrcpController); - } - - @UnsupportedAppUsage - public static boolean isAvrcpTarget(ParcelUuid uuid) { - return uuid.equals(AvrcpTarget); - } - - public static boolean isInputDevice(ParcelUuid uuid) { - return uuid.equals(Hid); - } - - public static boolean isPanu(ParcelUuid uuid) { - return uuid.equals(PANU); - } - - public static boolean isNap(ParcelUuid uuid) { - return uuid.equals(NAP); - } - - public static boolean isBnep(ParcelUuid uuid) { - return uuid.equals(BNEP); - } - - public static boolean isMap(ParcelUuid uuid) { - return uuid.equals(MAP); - } - - public static boolean isMns(ParcelUuid uuid) { - return uuid.equals(MNS); - } - - public static boolean isMas(ParcelUuid uuid) { - return uuid.equals(MAS); - } - - public static boolean isSap(ParcelUuid uuid) { - return uuid.equals(SAP); - } - /** - * Returns true if ParcelUuid is present in uuidArray + * Length of bytes for 128 bit UUID * - * @param uuidArray - Array of ParcelUuids - * @param uuid + * @hide */ - @UnsupportedAppUsage - public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { - if ((uuidArray == null || uuidArray.length == 0) && uuid == null) { - return true; - } - - if (uuidArray == null) { - return false; - } - - for (ParcelUuid element : uuidArray) { - if (element.equals(uuid)) return true; - } - return false; - } + @SystemApi + public static final int UUID_BYTES_128_BIT = 16; /** * Returns true if there any common ParcelUuids in uuidA and uuidB. * * @param uuidA - List of ParcelUuids * @param uuidB - List of ParcelUuids + * + * @hide */ - @UnsupportedAppUsage - public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + @SystemApi + public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA, + @Nullable ParcelUuid[] uuidB) { if (uuidA == null && uuidB == null) return true; if (uuidA == null) { @@ -217,29 +210,6 @@ public final class BluetoothUuid { return false; } - /** - * Returns true if all the ParcelUuids in ParcelUuidB are present in - * ParcelUuidA - * - * @param uuidA - Array of ParcelUuidsA - * @param uuidB - Array of ParcelUuidsB - */ - public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { - if (uuidA == null && uuidB == null) return true; - - if (uuidA == null) { - return uuidB.length == 0; - } - - if (uuidB == null) return true; - - HashSet uuidSet = new HashSet(Arrays.asList(uuidA)); - for (ParcelUuid uuid : uuidB) { - if (!uuidSet.contains(uuid)) return false; - } - return true; - } - /** * Extract the Service Identifier or the actual uuid from the Parcel Uuid. * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, @@ -248,7 +218,7 @@ public final class BluetoothUuid { * @param parcelUuid * @return the service identifier. */ - public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { + private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { UUID uuid = parcelUuid.getUuid(); long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32; return (int) value; @@ -262,8 +232,12 @@ public final class BluetoothUuid { * @param uuidBytes Byte representation of uuid. * @return {@link ParcelUuid} parsed from bytes. * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. + * + * @hide */ - public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { + @NonNull + @SystemApi + public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) { if (uuidBytes == null) { throw new IllegalArgumentException("uuidBytes cannot be null"); } @@ -305,6 +279,8 @@ public final class BluetoothUuid { * @param uuid uuid to parse. * @return shortest representation of {@code uuid} as bytes. * @throws IllegalArgumentException If the {@code uuid} is null. + * + * @hide */ public static byte[] uuidToBytes(ParcelUuid uuid) { if (uuid == null) { @@ -345,6 +321,8 @@ public final class BluetoothUuid { * * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + * + * @hide */ @UnsupportedAppUsage public static boolean is16BitUuid(ParcelUuid parcelUuid) { @@ -361,6 +339,8 @@ public final class BluetoothUuid { * * @param parcelUuid * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. + * + * @hide */ @UnsupportedAppUsage public static boolean is32BitUuid(ParcelUuid parcelUuid) { @@ -373,4 +353,6 @@ public final class BluetoothUuid { } return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L); } + + private BluetoothUuid() {} } -- GitLab From d9798613fd3241058e21aad6bc7461151b187003 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 4 Dec 2019 14:21:10 -0800 Subject: [PATCH 1076/1408] Refactor BluetoothHeadset APIs used by Settings Bug: 143244793 Test: Manual Change-Id: I399a1e0f93cb01c44817604edfcaa8fe5061142f --- framework/java/android/bluetooth/BluetoothHeadset.java | 5 +++-- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index ea3831a869f..0955b103a8e 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -559,7 +559,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

          The device should already be paired. * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or - * {@link BluetoothProfile#PRIORITY_OFF}, + * {@link BluetoothProfile#PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority @@ -1133,8 +1133,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ + @SystemApi + @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index a8e1fd26ad4..7ee29ff1c50 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -557,7 +557,7 @@ public final class BluetoothHeadsetClient 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}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority -- GitLab From 4f8a2b36d14cf7beb64a51132d2352a28636eb93 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 15 Nov 2019 16:08:54 -0800 Subject: [PATCH 1077/1408] Resolve BluetoothA2dp hidden APIs to resolve dependencies Bug: 143240341 Test: Manual Change-Id: Ib55e0fb106fa7b91ef4d3559da12ea2c048f1ae5 --- .../java/android/bluetooth/BluetoothA2dp.java | 101 ++++++---- .../android/bluetooth/BluetoothA2dpSink.java | 2 +- .../bluetooth/BluetoothCodecConfig.java | 179 +++++++++++++----- .../bluetooth/BluetoothCodecStatus.java | 42 ++-- 4 files changed, 226 insertions(+), 98 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 8ed61b6c87e..64df0e84f6d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -32,6 +33,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -154,13 +157,22 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public static final int STATE_NOT_PLAYING = 11; + /** @hide */ + @IntDef(prefix = "OPTIONAL_CODECS_", value = { + OPTIONAL_CODECS_SUPPORT_UNKNOWN, + OPTIONAL_CODECS_NOT_SUPPORTED, + OPTIONAL_CODECS_SUPPORTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OptionalCodecsSupportStatus {} + /** * We don't have a stored preference for whether or not the given A2DP sink device supports * optional codecs. * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; /** @@ -168,7 +180,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; /** @@ -176,16 +188,25 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_SUPPORTED = 1; + /** @hide */ + @IntDef(prefix = "OPTIONAL_CODECS_PREF_", value = { + OPTIONAL_CODECS_PREF_UNKNOWN, + OPTIONAL_CODECS_PREF_DISABLED, + OPTIONAL_CODECS_PREF_ENABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface OptionalCodecsPreferenceStatus {} + /** - * We don't have a stored preference for whether optional codecs should be enabled or disabled - * for the given A2DP device. + * We don't have a stored preference for whether optional codecs should be enabled or + * disabled for the given A2DP device. * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; /** @@ -193,7 +214,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; /** @@ -201,7 +222,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; private BluetoothAdapter mAdapter; @@ -248,13 +269,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -289,13 +309,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -384,14 +403,12 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted * with the active device. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device the remote Bluetooth device. Could be null to clear * the active device and stop streaming audio to a Bluetooth device. * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); @@ -412,16 +429,13 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Get the connected device that is active. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the connected device that is active or null if no device * is active * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @SystemApi @Nullable - @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { @@ -441,7 +455,7 @@ public final class BluetoothA2dp 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}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority @@ -626,8 +640,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { + @SystemApi + @Nullable + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { final IBluetoothA2dp service = getService(); @@ -652,9 +668,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage - public void setCodecConfigPreference(BluetoothDevice device, - BluetoothCodecConfig codecConfig) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void setCodecConfigPreference(@Nullable BluetoothDevice device, + @Nullable BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { final IBluetoothA2dp service = getService(); @@ -676,8 +693,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage - public void enableOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void enableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, true); } @@ -689,8 +707,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage - public void disableOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void disableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); enableDisableOptionalCodecs(device, false); } @@ -728,8 +747,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage - public int supportsOptionalCodecs(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @OptionalCodecsSupportStatus + public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -738,7 +759,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } } @@ -751,8 +772,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage - public int getOptionalCodecsEnabled(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @OptionalCodecsPreferenceStatus + public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -761,7 +784,7 @@ public final class BluetoothA2dp implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); + Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e); return OPTIONAL_CODECS_PREF_UNKNOWN; } } @@ -775,8 +798,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage - public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + @OptionalCodecsPreferenceStatus int value) { try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index c17834aa8e5..cf3367602aa 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -320,7 +320,7 @@ public final class BluetoothA2dpSink 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}, + * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} * * @param device Paired bluetooth device * @param priority diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 36f3a1eeba7..08d0797997b 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -16,10 +16,15 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** @@ -29,78 +34,131 @@ import java.util.Objects; * * {@hide} */ +@SystemApi public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { + SOURCE_CODEC_TYPE_SBC, + SOURCE_CODEC_TYPE_AAC, + SOURCE_CODEC_TYPE_APTX, + SOURCE_CODEC_TYPE_APTX_HD, + SOURCE_CODEC_TYPE_LDAC, + SOURCE_CODEC_TYPE_MAX, + SOURCE_CODEC_TYPE_INVALID + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SourceCodecType {} + public static final int SOURCE_CODEC_TYPE_SBC = 0; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_AAC = 1; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_APTX = 2; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_LDAC = 4; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_MAX = 5; - @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; - @UnsupportedAppUsage + /** @hide */ + @IntDef(prefix = "CODEC_PRIORITY_", value = { + CODEC_PRIORITY_DISABLED, + CODEC_PRIORITY_DEFAULT, + CODEC_PRIORITY_HIGHEST + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CodecPriority {} + public static final int CODEC_PRIORITY_DISABLED = -1; - @UnsupportedAppUsage + public static final int CODEC_PRIORITY_DEFAULT = 0; - @UnsupportedAppUsage + public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "SAMPLE_RATE_", value = { + SAMPLE_RATE_NONE, + SAMPLE_RATE_44100, + SAMPLE_RATE_48000, + SAMPLE_RATE_88200, + SAMPLE_RATE_96000, + SAMPLE_RATE_176400, + SAMPLE_RATE_192000 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SampleRate {} + public static final int SAMPLE_RATE_NONE = 0; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_44100 = 0x1 << 0; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_48000 = 0x1 << 1; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_88200 = 0x1 << 2; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_96000 = 0x1 << 3; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_176400 = 0x1 << 4; - @UnsupportedAppUsage + public static final int SAMPLE_RATE_192000 = 0x1 << 5; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "BITS_PER_SAMPLE_", value = { + BITS_PER_SAMPLE_NONE, + BITS_PER_SAMPLE_16, + BITS_PER_SAMPLE_24, + BITS_PER_SAMPLE_32 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BitsPerSample {} + public static final int BITS_PER_SAMPLE_NONE = 0; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - @UnsupportedAppUsage + public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - @UnsupportedAppUsage + + /** @hide */ + @IntDef(prefix = "CHANNEL_MODE_", value = { + CHANNEL_MODE_NONE, + CHANNEL_MODE_MONO, + CHANNEL_MODE_STEREO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChannelMode {} + public static final int CHANNEL_MODE_NONE = 0; - @UnsupportedAppUsage + public static final int CHANNEL_MODE_MONO = 0x1 << 0; - @UnsupportedAppUsage + public static final int CHANNEL_MODE_STEREO = 0x1 << 1; - private final int mCodecType; - private int mCodecPriority; - private final int mSampleRate; - private final int mBitsPerSample; - private final int mChannelMode; + private final @SourceCodecType int mCodecType; + private @CodecPriority int mCodecPriority; + private final @SampleRate int mSampleRate; + private final @BitsPerSample int mBitsPerSample; + private final @ChannelMode int mChannelMode; private final long mCodecSpecific1; private final long mCodecSpecific2; private final long mCodecSpecific3; private final long mCodecSpecific4; - @UnsupportedAppUsage - public BluetoothCodecConfig(int codecType, int codecPriority, - int sampleRate, int bitsPerSample, - int channelMode, long codecSpecific1, + public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, + @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, + @ChannelMode int channelMode, long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { mCodecType = codecType; @@ -114,8 +172,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } - @UnsupportedAppUsage - public BluetoothCodecConfig(int codecType) { + public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; @@ -144,6 +201,12 @@ public final class BluetoothCodecConfig implements Parcelable { return false; } + /** + * Returns a hash based on the config values + * + * @return a hash based on the config values + * @hide + */ @Override public int hashCode() { return Objects.hash(mCodecType, mCodecPriority, mSampleRate, @@ -155,6 +218,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains valid codec configuration. * * @return true if the object contains valid codec configuration, otherwise false. + * @hide */ public boolean isValid() { return (mSampleRate != SAMPLE_RATE_NONE) @@ -242,6 +306,12 @@ public final class BluetoothCodecConfig implements Parcelable { + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; } + /** + * Always returns 0 + * + * @return 0 + * @hide + */ @Override public int describeContents() { return 0; @@ -271,6 +341,14 @@ public final class BluetoothCodecConfig implements Parcelable { } }; + /** + * Flattens the object to a parcel + * + * @param out The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * + * @hide + */ @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mCodecType); @@ -289,7 +367,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec name */ - public String getCodecName() { + public @NonNull String getCodecName() { switch (mCodecType) { case SOURCE_CODEC_TYPE_SBC: return "SBC"; @@ -315,8 +393,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ - @UnsupportedAppUsage - public int getCodecType() { + public @SourceCodecType int getCodecType() { return mCodecType; } @@ -336,8 +413,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ - @UnsupportedAppUsage - public int getCodecPriority() { + public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -347,9 +423,10 @@ public final class BluetoothCodecConfig implements Parcelable { * means higher priority. If 0, reset to default. * * @param codecPriority the codec priority + * @hide */ @UnsupportedAppUsage - public void setCodecPriority(int codecPriority) { + public void setCodecPriority(@CodecPriority int codecPriority) { mCodecPriority = codecPriority; } @@ -366,8 +443,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ - @UnsupportedAppUsage - public int getSampleRate() { + public @SampleRate int getSampleRate() { return mSampleRate; } @@ -381,8 +457,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ - @UnsupportedAppUsage - public int getBitsPerSample() { + public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -394,9 +469,10 @@ public final class BluetoothCodecConfig implements Parcelable { * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO} * * @return the codec channel mode + * @hide */ @UnsupportedAppUsage - public int getChannelMode() { + public @ChannelMode int getChannelMode() { return mChannelMode; } @@ -405,7 +481,6 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ - @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } @@ -414,6 +489,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value2. * * @return a codec specific value2 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific2() { @@ -424,6 +500,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value3. * * @return a codec specific value3 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific3() { @@ -434,6 +511,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Gets a codec specific value4. * * @return a codec specific value4 + * @hide */ @UnsupportedAppUsage public long getCodecSpecific4() { @@ -445,6 +523,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param valueSet the value set presented by a bitmask * @return true if the valueSet contains zero or single bit, otherwise false. + * @hide */ private static boolean hasSingleBit(int valueSet) { return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); @@ -454,6 +533,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single sample rate. * * @return true if the object contains none or single sample rate, otherwise false. + * @hide */ public boolean hasSingleSampleRate() { return hasSingleBit(mSampleRate); @@ -463,6 +543,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single bits per sample. * * @return true if the object contains none or single bits per sample, otherwise false. + * @hide */ public boolean hasSingleBitsPerSample() { return hasSingleBit(mBitsPerSample); @@ -472,6 +553,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether the object contains none or single channel mode. * * @return true if the object contains none or single channel mode, otherwise false. + * @hide */ public boolean hasSingleChannelMode() { return hasSingleBit(mChannelMode); @@ -482,6 +564,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the audio feeding parameters are same, otherwise false + * @hide */ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { return (other != null && other.mSampleRate == mSampleRate @@ -495,6 +578,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the audio feeding parameters are similar, otherwise false. + * @hide */ public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { if (other == null || mCodecType != other.mCodecType) { @@ -526,6 +610,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @param other the codec config to compare against * @return true if the codec specific parameters are the same, otherwise false. + * @hide */ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { if (other == null && mCodecType != other.mCodecType) { diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 58a764a85be..b6e77391da5 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,6 +32,7 @@ import java.util.Objects; * * {@hide} */ +@SystemApi public final class BluetoothCodecStatus implements Parcelable { /** * Extra for the codec configuration intents of the individual profiles. @@ -39,17 +40,16 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = - "android.bluetooth.codec.extra.CODEC_STATUS"; + "android.bluetooth.extra.CODEC_STATUS"; private final @Nullable BluetoothCodecConfig mCodecConfig; private final BluetoothCodecConfig[] mCodecsLocalCapabilities; private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; - public BluetoothCodecStatus(BluetoothCodecConfig codecConfig, - BluetoothCodecConfig[] codecsLocalCapabilities, - BluetoothCodecConfig[] codecsSelectableCapabilities) { + public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig, + @Nullable BluetoothCodecConfig[] codecsLocalCapabilities, + @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities) { mCodecConfig = codecConfig; mCodecsLocalCapabilities = codecsLocalCapabilities; mCodecsSelectableCapabilities = codecsSelectableCapabilities; @@ -74,6 +74,7 @@ public final class BluetoothCodecStatus implements Parcelable { * @param c1 the first array of capabilities to compare * @param c2 the second array of capabilities to compare * @return true if both arrays contain same capabilities + * @hide */ public static boolean sameCapabilities(BluetoothCodecConfig[] c1, BluetoothCodecConfig[] c2) { @@ -95,6 +96,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @param codecConfig the codec config to compare against * @return true if the codec config matches, otherwise false + * @hide */ public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { if (codecConfig == null || !codecConfig.hasSingleSampleRate() @@ -125,7 +127,12 @@ public final class BluetoothCodecStatus implements Parcelable { return false; } - + /** + * Returns a hash based on the codec config and local capabilities + * + * @return a hash based on the config values + * @hide + */ @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, @@ -140,6 +147,12 @@ public final class BluetoothCodecStatus implements Parcelable { + "}"; } + /** + * Always returns 0 + * + * @return 0 + * @hide + */ @Override public int describeContents() { return 0; @@ -165,6 +178,14 @@ public final class BluetoothCodecStatus implements Parcelable { } }; + /** + * Flattens the object to a parcel + * + * @param out The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * + * @hide + */ @Override public void writeToParcel(Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); @@ -177,7 +198,6 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -187,8 +207,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage - public BluetoothCodecConfig[] getCodecsLocalCapabilities() { + public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -197,8 +216,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage - public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { + public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } } -- GitLab From d7205b6a1fc84b8b27c28461f9f3be1e626e8b67 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 9 Dec 2019 13:59:13 -0800 Subject: [PATCH 1078/1408] Resolve BluetoothHearingAid API usages by Settings Bug: 143244535 Test: Manual Change-Id: Ide243e05166f03d465a50a7f2bdb82dbfd6815d0 --- framework/java/android/bluetooth/BluetoothHearingAid.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index ead8429e4a7..b4521c623ec 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -335,9 +335,9 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public List getActiveDevices() { + public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); final IBluetoothHearingAid service = getService(); try { @@ -559,8 +559,9 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return the CustomerId of the device * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public long getHiSyncId(BluetoothDevice device) { + public long getHiSyncId(@Nullable BluetoothDevice device) { if (VDBG) { log("getCustomerId(" + device + ")"); } -- GitLab From 7feb0b43d519611f80fd373c85384894c9227a6e Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 10 Dec 2019 11:34:37 -0800 Subject: [PATCH 1079/1408] Refactor BluetoothPbap APIs used by Settings Bug: 145943634 Test: Manual Change-Id: I23b2b20f97968696f9f00fee55094db6e551835f --- .../java/android/bluetooth/BluetoothPbap.java | 95 ++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d94c65742e6..df0289643ee 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -16,7 +16,10 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -54,6 +57,7 @@ import java.util.List; * * @hide */ +@SystemApi public class BluetoothPbap implements BluetoothProfile { private static final String TAG = "BluetoothPbap"; @@ -75,7 +79,11 @@ public class BluetoothPbap implements BluetoothProfile { * {@link BluetoothProfile#STATE_DISCONNECTING}. *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. + * + * @hide */ + @SuppressLint("ActionValue") + @SystemApi @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; @@ -85,33 +93,16 @@ public class BluetoothPbap implements BluetoothProfile { private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; + /** @hide */ public static final int RESULT_FAILURE = 0; + /** @hide */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ - public static final int RESULT_CANCELED = 2; - /** - * An interface for notifying Bluetooth PCE IPC clients when they have - * been connected to the BluetoothPbap service. + * Connection canceled before completion. + * + * @hide */ - public interface ServiceListener { - /** - * Called to notify the client when this proxy object has been - * connected to the BluetoothPbap service. Clients must wait for - * this callback before making IPC calls on the BluetoothPbap - * service. - */ - public void onServiceConnected(BluetoothPbap proxy); - - /** - * Called to notify the client that this proxy object has been - * disconnected from the BluetoothPbap service. Clients must not - * make IPC calls on the BluetoothPbap service after this callback. - * This callback will currently only occur if the application hosting - * the BluetoothPbap service, but may be called more often in future. - */ - public void onServiceDisconnected(); - } + public static final int RESULT_CANCELED = 2; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { @@ -127,6 +118,8 @@ public class BluetoothPbap implements BluetoothProfile { /** * Create a BluetoothPbap proxy object. + * + * @hide */ public BluetoothPbap(Context context, ServiceListener l) { mContext = context; @@ -181,6 +174,7 @@ public class BluetoothPbap implements BluetoothProfile { } } + /** @hide */ protected void finalize() throws Throwable { try { close(); @@ -194,6 +188,8 @@ public class BluetoothPbap implements BluetoothProfile { * Other public functions of BluetoothPbap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * + * @hide */ public synchronized void close() { IBluetoothManager mgr = mAdapter.getBluetoothManager(); @@ -210,6 +206,8 @@ public class BluetoothPbap implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List getConnectedDevices() { @@ -229,17 +227,22 @@ public class BluetoothPbap implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ + @SystemApi @Override - public int getConnectionState(BluetoothDevice device) { + public int getConnectionState(@Nullable BluetoothDevice device) { log("getConnectionState: device=" + device); - final IBluetoothPbap service = mService; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.STATE_DISCONNECTED; - } try { - return service.getConnectionState(device); + final IBluetoothPbap service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getConnectionState(device); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -248,6 +251,8 @@ public class BluetoothPbap implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -265,23 +270,13 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } - /** - * Returns true if the specified Bluetooth device is connected (does not - * include connecting). Returns false if not connected, or if this proxy - * object is not currently connected to the Pbap service. - */ - // TODO: This is currently being used by SettingsLib and internal app. - public boolean isConnected(BluetoothDevice device) { - return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED; - } - /** * Disconnects the current Pbap client (PCE). Currently this call blocks, * it may soon be made asynchronous. Returns false if this proxy object is * not currently connected to the Pbap service. + * + * @hide */ - // TODO: This is currently being used by SettingsLib and will be used in the future. - // TODO: Must specify target device. Implement this in the service. @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { log("disconnect()"); @@ -304,7 +299,7 @@ public class BluetoothPbap implements BluetoothProfile { log("Proxy object connected"); mService = IBluetoothPbap.Stub.asInterface(service); if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothPbap.this); + mServiceListener.onServiceConnected(BluetoothProfile.PBAP, BluetoothPbap.this); } } @@ -312,11 +307,23 @@ public class BluetoothPbap implements BluetoothProfile { log("Proxy object disconnected"); doUnbind(); if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(); + mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP); } } }; + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + private static void log(String msg) { if (DBG) { Log.d(TAG, msg); -- GitLab From fccb8d42581e8523296dac72cb4be67bd6ae8c35 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 4 Dec 2019 16:09:44 -0800 Subject: [PATCH 1080/1408] Resolve BluetoothDevice hidden API usages by Settings Bug: 143245666 Test: Manual Change-Id: Id34eb275f604e61330aeb91401dc31aeca666b8f --- .../android/bluetooth/BluetoothDevice.java | 127 ++++++++++-------- .../android/bluetooth/BluetoothTestUtils.java | 2 - 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 49187dcde34..323c7d17203 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -33,8 +34,12 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.UUID; /** @@ -771,6 +776,13 @@ public final class BluetoothDevice implements Parcelable { @UnsupportedAppUsage public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; + + /** @hide */ + @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN, + ACCESS_ALLOWED, ACCESS_REJECTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface AccessPermission{} + /** * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. @@ -1096,15 +1108,14 @@ public final class BluetoothDevice implements Parcelable { /** * Get the most recent identified battery level of this Bluetooth device - *

          Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting * service, or return value is invalid * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - @UnsupportedAppUsage public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1187,8 +1198,15 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - @UnsupportedAppUsage + /** + * Gets whether bonding was initiated locally + * + * @return true if bonding is initiated locally, false otherwise + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1480,15 +1498,20 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - @UnsupportedAppUsage - public boolean setPasskey(int passkey) { - //TODO(BT) - /* - try { - return sService.setPasskey(this, true, 4, passkey); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return false; + /** + * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} + * + * @return true pin has been set false for error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setPin(@Nullable String pin) { + byte[] pinBytes = convertPinToBytes(pin); + if (pinBytes == null) { + return false; + } + return setPin(pinBytes); } /** @@ -1511,22 +1534,18 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - public boolean setRemoteOutOfBandData() { - // TODO(BT) - /* - try { - return sService.setRemoteOutOfBandData(this); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return false; - } - - /** @hide */ - @UnsupportedAppUsage - public boolean cancelPairingUserInput() { + /** + * Cancels pairing to this device + * + * @return true if pairing cancelled successfully, false otherwise + * + * @hide + */ + @SystemApi + public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { - Log.e(TAG, "BT not enabled. Cannot create pairing user input"); + Log.e(TAG, "BT not enabled. Cannot cancel pairing"); return false; } try { @@ -1537,17 +1556,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ - @UnsupportedAppUsage - public boolean isBluetoothDock() { - // TODO(BT) - /* - try { - return sService.isBluetoothDock(this); - } catch (RemoteException e) {Log.e(TAG, "", e);}*/ - return false; - } - boolean isBluetoothEnabled() { boolean ret = false; BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1558,13 +1566,14 @@ public final class BluetoothDevice implements Parcelable { } /** - * Requires {@link android.Manifest.permission#BLUETOOTH}. + * Gets whether the phonebook access is allowed for this bluetooth device * * @return Whether the phonebook access is allowed to this device. Can be {@link * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1667,14 +1676,14 @@ public final class BluetoothDevice implements Parcelable { } /** - * Requires {@link android.Manifest.permission#BLUETOOTH}. + * Gets whether message access is allowed to this bluetooth device * - * @return Whether the message access is allowed to this device. Can be {@link #ACCESS_UNKNOWN}, - * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @return Whether the message access is allowed to this device. * @hide */ - @UnsupportedAppUsage - public int getMessageAccessPermission() { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { return ACCESS_UNKNOWN; @@ -1689,15 +1698,18 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the message access is allowed to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link - * #ACCESS_REJECTED}. + * @param value is the value we are setting the message access permission to * @return Whether the value has been successfully set. * @hide */ - @UnsupportedAppUsage - public boolean setMessageAccessPermission(int value) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setMessageAccessPermission(@AccessPermission int value) { + // Validates param value is one of the accepted constants + if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) { + throw new IllegalArgumentException(value + "is not a valid AccessPermission value"); + } final IBluetooth service = sService; if (service == null) { return false; @@ -1711,13 +1723,14 @@ public final class BluetoothDevice implements Parcelable { } /** - * Requires {@link android.Manifest.permission#BLUETOOTH}. + * Gets whether sim access is allowed for this bluetooth device * - * @return Whether the Sim access is allowed to this device. Can be {@link #ACCESS_UNKNOWN}, - * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. + * @return Whether the Sim access is allowed to this device. * @hide */ - public int getSimAccessPermission() { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { return ACCESS_UNKNOWN; @@ -1732,14 +1745,14 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the Sim access is allowed to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link * #ACCESS_REJECTED}. * @return Whether the value has been successfully set. * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -1970,7 +1983,7 @@ public final class BluetoothDevice implements Parcelable { * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ - @UnsupportedAppUsage + @VisibleForTesting public static byte[] convertPinToBytes(String pin) { if (pin == null) { return null; diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index b906d84adf5..ed613c36b89 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -176,14 +176,12 @@ public class BluetoothTestUtils extends Assert { mDevice.setPin(mPin); break; case BluetoothDevice.PAIRING_VARIANT_PASSKEY: - mDevice.setPasskey(mPasskey); break; case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: case BluetoothDevice.PAIRING_VARIANT_CONSENT: mDevice.setPairingConfirmation(true); break; case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT: - mDevice.setRemoteOutOfBandData(); break; } } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { -- GitLab From 658617b40f1526657c0ad59a406068af78598ccb Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Fri, 15 Nov 2019 12:41:09 -0800 Subject: [PATCH 1081/1408] Pipe featureId from app to noteOp in BT code FeatureIds are not yet available, hence in BTManager we assume always a "null" featureId. The effect of this change is that for apps that opt into using featureIds, they will have one BluetoothAdapter per feature, not one per process as before. In my testing this caused no problem. Most apps won't use featureIds, hence for most apps there is no change in behavior. Test: used bluetooth Bug: 136595429 Change-Id: Ic40326ea331c60f764f213bb2673cb4c49a81604 --- .../android/bluetooth/BluetoothAdapter.java | 14 +++++++-- .../android/bluetooth/BluetoothManager.java | 30 ++++++++++++++----- .../bluetooth/le/BluetoothLeScanner.java | 20 ++++++++----- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3f8cb627e38..291d1d99fdc 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -847,7 +847,8 @@ public final class BluetoothAdapter { } synchronized (mLock) { if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = new BluetoothLeScanner(mManagerService); + sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), + getFeatureId()); } } return sBluetoothLeScanner; @@ -1637,6 +1638,15 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } + private String getFeatureId() { + // Workaround for legacy API for getting a BluetoothAdapter not + // passing a context + if (mContext != null) { + return null; + } + return null; + } + /** * Start the remote device discovery process. *

          The discovery process usually involves an inquiry scan of about 12 @@ -1674,7 +1684,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName()); + return mService.startDiscovery(getOpPackageName(), getFeatureId()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index adedff3e938..cff4c2d75db 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -22,7 +22,9 @@ import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.content.pm.PackageManager; +import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; @@ -60,22 +62,34 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - context = context.getApplicationContext(); - if (context == null) { - throw new IllegalArgumentException( - "context not associated with any application (using a mock context?)"); + if (null == null) { + context = context.getApplicationContext(); + if (context == null) { + throw new IllegalArgumentException( + "context not associated with any application (using a mock context?)"); + } + + mAdapter = BluetoothAdapter.getDefaultAdapter(); + } else { + IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); + if (b != null) { + mAdapter = new BluetoothAdapter(IBluetoothManager.Stub.asInterface(b)); + } else { + Log.e(TAG, "Bluetooth binder is null"); + mAdapter = null; + } } - // Legacy api - getDefaultAdapter does not take in the context - mAdapter = BluetoothAdapter.getDefaultAdapter(); + + // Context is not initialized in constructor if (mAdapter != null) { mAdapter.setContext(context); } } /** - * Get the default BLUETOOTH Adapter for this device. + * Get the BLUETOOTH Adapter for this device. * - * @return the default BLUETOOTH Adapter + * @return the BLUETOOTH Adapter */ public BluetoothAdapter getAdapter() { return mAdapter; diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index ac126ae0969..9a17346334d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.app.ActivityThread; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; @@ -84,17 +83,25 @@ public final class BluetoothLeScanner { private BluetoothAdapter mBluetoothAdapter; private final Map mLeScanClients; + private final String mOpPackageName; + private final String mFeatureId; + /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. * * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. + * @param opPackageName The opPackageName of the context this object was created from + * @param featureId The featureId of the context this object was created from * @hide */ - public BluetoothLeScanner(IBluetoothManager bluetoothManager) { + public BluetoothLeScanner(IBluetoothManager bluetoothManager, + @NonNull String opPackageName, @Nullable String featureId) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap(); + mOpPackageName = opPackageName; + mFeatureId = featureId; } /** @@ -246,8 +253,8 @@ public final class BluetoothLeScanner { wrapper.startRegistration(); } else { try { - gatt.startScanForIntent(callbackIntent, settings, filters, - ActivityThread.currentOpPackageName()); + gatt.startScanForIntent(callbackIntent, settings, filters, mOpPackageName, + mFeatureId); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } @@ -288,7 +295,7 @@ public final class BluetoothLeScanner { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName()); + gatt.stopScanForIntent(callbackIntent, mOpPackageName); } catch (RemoteException e) { } } @@ -448,8 +455,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mResultStorages, - ActivityThread.currentOpPackageName()); + mResultStorages, mOpPackageName, mFeatureId); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); -- GitLab From 492c97c1c8552980aec761670ee7893ab5867713 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 6 Nov 2019 15:00:48 -0800 Subject: [PATCH 1082/1408] Remove a few hard coded references to SystemUI in fw/base/services Bug: 143775123 Test: atest ScreenshotHelperTest Test: atest AlarmManagerTest Change-Id: I2fefe2968e23bc2d06aa52266c2eeeb90f4733ee --- .../bluetooth/BluetoothManagerService.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 119b987f7ae..470300e6485 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -16,6 +16,9 @@ package com.android.server; +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.os.UserHandle.USER_SYSTEM; + import android.Manifest; import android.app.ActivityManager; import android.app.AppGlobals; @@ -42,6 +45,7 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; @@ -242,10 +246,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. - if (userId == UserHandle.USER_SYSTEM + if (userId == USER_SYSTEM && UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { - if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean( + if (userId == USER_SYSTEM && newRestrictions.getBoolean( UserManager.DISALLOW_BLUETOOTH)) { updateOppLauncherComponentState(userId, true); // Sharing disallowed sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED, @@ -437,18 +441,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } int systemUiUid = -1; - try { - // Check if device is configured with no home screen, which implies no SystemUI. - boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen); - if (!noHome) { - systemUiUid = mContext.getPackageManager() - .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, - UserHandle.USER_SYSTEM); - } + // Check if device is configured with no home screen, which implies no SystemUI. + boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen); + if (!noHome) { + PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(), + MATCH_SYSTEM_ONLY, USER_SYSTEM); + } + if (systemUiUid >= 0) { Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid)); - } catch (PackageManager.NameNotFoundException e) { + } else { // Some platforms, such as wearables do not have a system ui. - Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); + Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; } -- GitLab From 64c9d2eae343ec9dbdb4ac02a1af9e6126bc5ac3 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 12 Dec 2019 13:29:19 -0800 Subject: [PATCH 1083/1408] Resolve BluetoothA2dpSink APIs used by Settings Bug: 146082017 Test: Manual Change-Id: Iebae62408c490cb9091b1f4e4a5a5e32321e2700 --- .../android/bluetooth/BluetoothA2dpSink.java | 103 +++++------------- 1 file changed, 28 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index cf3367602aa..c6957e14116 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,7 +17,9 @@ package android.bluetooth; import android.Manifest; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.Context; @@ -39,6 +41,7 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothA2dpSink implements BluetoothProfile { private static final String TAG = "BluetoothA2dpSink"; private static final boolean DBG = true; @@ -59,71 +62,14 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. + * @hide */ + @SystemApi + @SuppressLint("ActionValue") + @RequiresPermission(Manifest.permission.BLUETOOTH) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; - /** - * Intent used to broadcast the change in the Playing state of the A2DP Sink - * profile. - * - *

          This intent will have 3 extras: - *

            - *
          • {@link #EXTRA_STATE} - The current state of the profile.
          • - *
          • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
          • - *
          • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
          • - *
          - * - *

          {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - */ - public static final String ACTION_PLAYING_STATE_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED"; - - /** - * A2DP sink device is streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. - */ - public static final int STATE_PLAYING = 10; - - /** - * A2DP sink device is NOT streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. - */ - public static final int STATE_NOT_PLAYING = 11; - - /** - * Intent used to broadcast the change in the Playing state of the A2DP Sink - * profile. - * - *

          This intent will have 3 extras: - *

            - *
          • {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device.
          • - *
          • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
          • - *
          - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - */ - public static final String ACTION_AUDIO_CONFIG_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED"; - - /** - * Extra for the {@link #ACTION_AUDIO_CONFIG_CHANGED} intent. - * - * This extra represents the current audio configuration of the A2DP source device. - * {@see BluetoothAudioConfig} - */ - public static final String EXTRA_AUDIO_CONFIG = - "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; - private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, @@ -170,13 +116,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -210,14 +154,12 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -235,6 +177,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List getConnectedDevices() { @@ -254,6 +198,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -273,6 +219,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public int getConnectionState(BluetoothDevice device) { @@ -300,6 +248,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return audio configuration for the device, or null * * {@see BluetoothAudioConfig} + * + * @hide */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); @@ -347,7 +297,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -395,7 +345,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -411,13 +361,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } /** - * Check if A2DP profile is streaming music. - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Check if audio is playing on the bluetooth device (A2DP profile is streaming music). * * @param device BluetoothDevice device + * @return true if audio is playing (A2dp is streaming music), false otherwise + * + * @hide */ - public boolean isA2dpPlaying(BluetoothDevice device) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isAudioPlaying(@Nullable BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { @@ -448,9 +401,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { return "connected"; case STATE_DISCONNECTING: return "disconnecting"; - case STATE_PLAYING: + case BluetoothA2dp.STATE_PLAYING: return "playing"; - case STATE_NOT_PLAYING: + case BluetoothA2dp.STATE_NOT_PLAYING: return "not playing"; default: return ""; -- GitLab From a188ac2ea6791550982fa6a3bf3636eb0d050b80 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Fri, 15 Nov 2019 12:41:09 -0800 Subject: [PATCH 1084/1408] Pipe featureId from app to noteOp in BT code Test: atest CtsAppOpsTestCases (Now with canary test for this code) Bug: 136595429 Change-Id: I90bb6b017da4f03038fce76760a860390b727f00 --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 291d1d99fdc..8404705633c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1642,7 +1642,7 @@ public final class BluetoothAdapter { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { - return null; + return mContext.getFeatureId(); } return null; } diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index cff4c2d75db..7ff64663349 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (null == null) { + if (context.getFeatureId() == null) { context = context.getApplicationContext(); if (context == null) { throw new IllegalArgumentException( -- GitLab From 9681e24d770aed5d01d931fc979066aee98717a3 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 16 Dec 2019 16:41:33 -0800 Subject: [PATCH 1085/1408] Remove unused BluetoothHearingAid APIs and usages Bug: 143244535 Test: Manual Change-Id: I8915ec755794b2c44412386de03c5fc0778d8ec7 --- .../bluetooth/BluetoothHearingAid.java | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index b4521c623ec..a65162ad5d8 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -471,63 +471,6 @@ public final class BluetoothHearingAid implements BluetoothProfile { } } - /** - * Get the volume of the device. - * - *

          The volume is between -128 dB (mute) to 0 dB. - * - * @return volume of the hearing aid device. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getVolume() { - if (VDBG) { - log("getVolume()"); - } - final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled()) { - return service.getVolume(); - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return 0; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return 0; - } - } - - /** - * Tells remote device to adjust volume. Uses the following values: - *

            - *
          • {@link AudioManager#ADJUST_LOWER}
          • - *
          • {@link AudioManager#ADJUST_RAISE}
          • - *
          • {@link AudioManager#ADJUST_MUTE}
          • - *
          • {@link AudioManager#ADJUST_UNMUTE}
          • - *
          - * - * @param direction One of the supported adjust values. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public void adjustVolume(int direction) { - if (DBG) log("adjustVolume(" + direction + ")"); - - final IBluetoothHearingAid service = getService(); - try { - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return; - } - - if (!isEnabled()) return; - - service.adjustVolume(direction); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } - } - /** * Tells remote device to set an absolute volume. * -- GitLab From cb33332a4259dc9e5cc3833cd3d38c1caa165c7b Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Tue, 10 Dec 2019 17:47:52 +0000 Subject: [PATCH 1086/1408] 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 --- framework/java/android/bluetooth/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothA2dpSink.java | 2 +- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 2 +- framework/java/android/bluetooth/BluetoothCodecConfig.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- framework/java/android/bluetooth/BluetoothGattDescriptor.java | 2 +- framework/java/android/bluetooth/BluetoothGattService.java | 2 +- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 2 +- framework/java/android/bluetooth/BluetoothHearingAid.java | 2 +- framework/java/android/bluetooth/BluetoothMap.java | 2 +- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- framework/java/android/bluetooth/BluetoothPan.java | 2 +- framework/java/android/bluetooth/BluetoothPbap.java | 2 +- framework/java/android/bluetooth/BluetoothProfile.java | 2 +- framework/java/android/bluetooth/BluetoothSap.java | 2 +- framework/java/android/bluetooth/BluetoothServerSocket.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- framework/java/android/bluetooth/le/ScanRecord.java | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 64df0e84f6d..d8c653c6d0d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Build; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index c6957e14116..8993de0939e 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8404705633c..b1b6f0d61f9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -25,7 +25,6 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; @@ -36,6 +35,7 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 260e2fb1b80..905b0ceec4e 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 08d0797997b..93e76fa5ba7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 323c7d17203..9fe4dd66b87 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Parcel; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index d616b8f92d3..f877f04626d 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index edacf3e0b70..7066f470aa9 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 0783cd2b73c..7cc2d6bc53f 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index c20faf9db69..13d6d7021ec 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0955b103a8e..1ba2bb5e31a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 7ee29ff1c50..6de1ffb6344 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Bundle; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 7165dd56a21..d1a096e605d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index b4521c623ec..83eaa728d54 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 979dfd4e3ba..917e7fa04e4 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0ec473c85ad..0aa5aac5d8f 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4e9762737c0..42f27f2a9c9 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -23,7 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index df0289643ee..c579fdf5048 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -20,7 +20,7 @@ import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 097a3677ac4..638e6de06be 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -22,7 +22,7 @@ import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 9b4dabcdcfd..8bf1b588c89 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 3a23808f361..88c186c88aa 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 760166bfcc5..f7743696553 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 7e96c23af4b..e274af1b5c5 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import java.nio.ByteBuffer; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 97e3f5221c7..c0c1aa1634f 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -18,8 +18,8 @@ package android.bluetooth.le; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; -- GitLab From 0f750b11afa11af76cdc9253a12ea4b2b63ddf3b Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 6 Jan 2020 15:22:55 -0800 Subject: [PATCH 1087/1408] Add setConnectionPolicy to BluetoothPbap Bug: 145943634 Test: Manual Change-Id: I72fc624d5c6578ca0374a1e943810d97b6361250 --- .../java/android/bluetooth/BluetoothPbap.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index df0289643ee..29101507038 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -16,7 +16,10 @@ package android.bluetooth; +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; @@ -270,6 +273,42 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } + /** + * Pbap does not store connection policy, so this function only disconnects Pbap if + * connectionPolicy is CONNECTION_POLICY_FORBIDDEN. + * + *

          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 pbap is successfully disconnected, false otherwise + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothPbap service = mService; + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + /** * Disconnects the current Pbap client (PCE). Currently this call blocks, * it may soon be made asynchronous. Returns false if this proxy object is -- GitLab From 1fc4e5c82ff1447df1a8dad20414458f33b8eac4 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 7 Jan 2020 17:02:22 -0800 Subject: [PATCH 1088/1408] Make the BluetoothHidHost class @SystemApi Bug: 146462547 Test: Manual Change-Id: I2044e850583701173b51ca89e30ff8aa79bb255c --- .../java/android/bluetooth/BluetoothHidHost.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 8f5cdf01f57..c1233b800a5 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -17,9 +17,12 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; @@ -43,6 +46,7 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothHidHost implements BluetoothProfile { private static final String TAG = "BluetoothHidHost"; private static final boolean DBG = true; @@ -66,6 +70,7 @@ public final class BluetoothHidHost implements BluetoothProfile { *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */ + @SuppressLint("ActionValue") @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; @@ -325,7 +330,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public List getConnectedDevices() { + public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { @@ -342,6 +347,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List getDevicesMatchingConnectionStates(int[] states) { @@ -363,7 +370,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(BluetoothDevice device) { + public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -409,7 +416,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -457,7 +464,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- GitLab From 5cd5a06efa7942b232f2a5c934150d635080ef18 Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Wed, 8 Jan 2020 17:50:08 +0800 Subject: [PATCH 1089/1408] Support PBAP for get/close profile proxy Bug: 147078847 Test: manual Change-Id: Iabcf9ab81b052be8f6f7388843681450a1cd7da4 --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 291d1d99fdc..220b77b299a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2670,6 +2670,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; + } else if (profile == BluetoothProfile.PBAP) { + BluetoothPbap pbap = new BluetoothPbap(context, listener); + return true; } else if (profile == BluetoothProfile.HEALTH) { Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); return false; @@ -2742,6 +2745,10 @@ public final class BluetoothAdapter { BluetoothPan pan = (BluetoothPan) proxy; pan.close(); break; + case BluetoothProfile.PBAP: + BluetoothPbap pbap = (BluetoothPbap) proxy; + pbap.close(); + break; case BluetoothProfile.GATT: BluetoothGatt gatt = (BluetoothGatt) proxy; gatt.close(); -- GitLab From d8fe38cc98bb4f49cd0c4ab9d7855f98ee1209f3 Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Tue, 10 Dec 2019 17:47:52 +0000 Subject: [PATCH 1090/1408] 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/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothA2dpSink.java | 2 +- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 2 +- framework/java/android/bluetooth/BluetoothCodecConfig.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- framework/java/android/bluetooth/BluetoothGattDescriptor.java | 2 +- framework/java/android/bluetooth/BluetoothGattService.java | 2 +- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 2 +- framework/java/android/bluetooth/BluetoothHearingAid.java | 2 +- framework/java/android/bluetooth/BluetoothMap.java | 2 +- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- framework/java/android/bluetooth/BluetoothPan.java | 2 +- framework/java/android/bluetooth/BluetoothPbap.java | 2 +- framework/java/android/bluetooth/BluetoothProfile.java | 2 +- framework/java/android/bluetooth/BluetoothSap.java | 2 +- framework/java/android/bluetooth/BluetoothServerSocket.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- framework/java/android/bluetooth/le/ScanRecord.java | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 64df0e84f6d..d8c653c6d0d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Build; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index c6957e14116..8993de0939e 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 291d1d99fdc..e0aabffb100 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -25,7 +25,6 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; @@ -36,6 +35,7 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 260e2fb1b80..905b0ceec4e 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 08d0797997b..93e76fa5ba7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 7396a116193..61b23b67519 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Parcel; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index d616b8f92d3..f877f04626d 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index edacf3e0b70..7066f470aa9 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 0783cd2b73c..7cc2d6bc53f 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index c20faf9db69..13d6d7021ec 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0955b103a8e..1ba2bb5e31a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 7ee29ff1c50..6de1ffb6344 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Bundle; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 7165dd56a21..d1a096e605d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index a65162ad5d8..38498bc147b 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 979dfd4e3ba..917e7fa04e4 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0ec473c85ad..0aa5aac5d8f 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4e9762737c0..42f27f2a9c9 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -23,7 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 29101507038..948885ed501 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 097a3677ac4..638e6de06be 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -22,7 +22,7 @@ import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 9b4dabcdcfd..8bf1b588c89 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 3a23808f361..88c186c88aa 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 760166bfcc5..f7743696553 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 7e96c23af4b..e274af1b5c5 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import java.nio.ByteBuffer; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 97e3f5221c7..c0c1aa1634f 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -18,8 +18,8 @@ package android.bluetooth.le; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; -- GitLab From bf9ddc5eafe0666f8b6a8b3784cb25a9205474b9 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 9 Jan 2020 13:57:12 -0800 Subject: [PATCH 1091/1408] Add setConnectionPolicy to HidDevice and Pan. Clean up documentation for the method in Pbap. Bug: 147444905 Test: Manual Change-Id: I308b29d8ce96f9e410a66d59379a731ae12e0478 --- .../android/bluetooth/BluetoothHidDevice.java | 63 +++++++++++++++++++ .../java/android/bluetooth/BluetoothPan.java | 37 +++++++++++ .../java/android/bluetooth/BluetoothPbap.java | 6 +- 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index e9b0be2c4cd..a923be62fbc 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -16,8 +16,12 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -36,6 +40,7 @@ import java.util.concurrent.Executor; */ public final class BluetoothHidDevice implements BluetoothProfile { private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + private static final boolean DBG = false; /** * Intent used to broadcast the change in connection state of the Input Host profile. @@ -682,4 +687,62 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } + + /** + * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} + * and disconnects Hid device if connectionPolicy is + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}. + * + *

          The device should already be paired. + * Connection policy can be one of: + * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, + * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy determines whether hid device should be connected or disconnected + * @return true if hid device is connected or disconnected, false otherwise + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothHidDevice service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + if (DBG) { + Log.d(TAG, msg); + } + } } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4e9762737c0..f5de4addb07 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -16,9 +16,11 @@ package android.bluetooth; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; @@ -255,6 +257,41 @@ public final class BluetoothPan implements BluetoothProfile { return false; } + /** + * 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(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothPan service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + /** * {@inheritDoc} */ diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 29101507038..74ee2c3fba0 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -274,15 +274,15 @@ public class BluetoothPbap implements BluetoothProfile { } /** - * Pbap does not store connection policy, so this function only disconnects Pbap if - * connectionPolicy is CONNECTION_POLICY_FORBIDDEN. + * Pbap does not store connection policy, so this function only disconnects pbap if + * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}. * *

          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 + * @param connectionPolicy determines whether to disconnect the device * @return true if pbap is successfully disconnected, false otherwise * @hide */ -- GitLab From 1429e81689c92c60f556aab1f387f8d2a10b8eb2 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 8 Jan 2020 11:08:42 -0800 Subject: [PATCH 1092/1408] Make the BluetoothMap class @SystemApi Bug: 146462547 Test: Manual Change-Id: I2bd3fbb058019bbb32472eac4fcaa65677cbf950 --- .../java/android/bluetooth/BluetoothMap.java | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 979dfd4e3ba..f2ceabcc7d7 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -17,7 +17,10 @@ package android.bluetooth; 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.annotation.UnsupportedAppUsage; import android.content.Context; @@ -35,21 +38,35 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothMap implements BluetoothProfile { private static final String TAG = "BluetoothMap"; private static final boolean DBG = true; private static final boolean VDBG = false; + /** @hide */ + @SuppressLint("ActionValue") + @SystemApi public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - /** 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; private BluetoothAdapter mAdapter; @@ -71,6 +88,7 @@ public final class BluetoothMap implements BluetoothProfile { mProfileConnector.connect(context, listener); } + @SuppressLint("GenericException") protected void finalize() throws Throwable { try { close(); @@ -84,6 +102,8 @@ public final class BluetoothMap implements BluetoothProfile { * Other public functions of BluetoothMap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * + * @hide */ public synchronized void close() { mProfileConnector.disconnect(); @@ -98,6 +118,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not * connected to the Map service. + * + * @hide */ public int getState() { if (VDBG) log("getState()"); @@ -120,6 +142,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @return The remote Bluetooth device, or null if not in connected or connecting state, or if * this proxy object is not connected to the Map service. + * + * @hide */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); @@ -141,6 +165,8 @@ public final class BluetoothMap 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("isConnected(" + device + ")"); @@ -161,6 +187,8 @@ public final class BluetoothMap implements BluetoothProfile { /** * Initiate connection. Initiation of outgoing connections is not * supported for MAP server. + * + * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")" + "not supported for MAPS"); @@ -172,6 +200,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @param device Remote Bluetooth Device * @return false on error, true otherwise + * + * @hide */ @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { @@ -196,6 +226,8 @@ public final class BluetoothMap implements BluetoothProfile { * devices. It tries to err on the side of false positives. * * @return True if this device might support Map. + * + * @hide */ public static boolean doesClassMatchSink(BluetoothClass btClass) { // TODO optimize the rule @@ -214,8 +246,11 @@ public final class BluetoothMap implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * + * @hide */ - public List getConnectedDevices() { + @SystemApi + public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap service = getService(); if (service != null && isEnabled()) { @@ -234,6 +269,8 @@ public final class BluetoothMap implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * + * @hide */ public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); @@ -254,6 +291,8 @@ public final class BluetoothMap implements BluetoothProfile { * Get connection state of device * * @return device connection state + * + * @hide */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); @@ -301,7 +340,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -349,7 +388,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- GitLab From 3625be4eb7d52fe10c659b649d945a4cea0d0beb Mon Sep 17 00:00:00 2001 From: Artur Satayev Date: Tue, 10 Dec 2019 17:47:52 +0000 Subject: [PATCH 1093/1408] 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/BluetoothA2dp.java | 2 +- framework/java/android/bluetooth/BluetoothA2dpSink.java | 2 +- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothClass.java | 2 +- framework/java/android/bluetooth/BluetoothCodecConfig.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- .../java/android/bluetooth/BluetoothGattCharacteristic.java | 2 +- framework/java/android/bluetooth/BluetoothGattDescriptor.java | 2 +- framework/java/android/bluetooth/BluetoothGattService.java | 2 +- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- .../java/android/bluetooth/BluetoothHeadsetClientCall.java | 2 +- framework/java/android/bluetooth/BluetoothHearingAid.java | 2 +- framework/java/android/bluetooth/BluetoothMap.java | 2 +- framework/java/android/bluetooth/BluetoothMapClient.java | 2 +- framework/java/android/bluetooth/BluetoothPan.java | 2 +- framework/java/android/bluetooth/BluetoothPbap.java | 2 +- framework/java/android/bluetooth/BluetoothProfile.java | 2 +- framework/java/android/bluetooth/BluetoothSap.java | 2 +- framework/java/android/bluetooth/BluetoothServerSocket.java | 2 +- framework/java/android/bluetooth/BluetoothSocket.java | 2 +- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- framework/java/android/bluetooth/le/ScanRecord.java | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 64df0e84f6d..d8c653c6d0d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Build; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index c6957e14116..8993de0939e 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 220b77b299a..1869c6fa762 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -25,7 +25,6 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; @@ -36,6 +35,7 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 260e2fb1b80..905b0ceec4e 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 08d0797997b..93e76fa5ba7 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 323c7d17203..9fe4dd66b87 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -24,7 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Parcel; diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index d616b8f92d3..f877f04626d 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index edacf3e0b70..7066f470aa9 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 0783cd2b73c..7cc2d6bc53f 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index c20faf9db69..13d6d7021ec 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,7 +15,7 @@ */ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 0955b103a8e..1ba2bb5e31a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 7ee29ff1c50..6de1ffb6344 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Bundle; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 7165dd56a21..d1a096e605d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index a65162ad5d8..38498bc147b 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 979dfd4e3ba..917e7fa04e4 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0ec473c85ad..0aa5aac5d8f 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4e9762737c0..42f27f2a9c9 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -23,7 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 29101507038..948885ed501 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -23,7 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 097a3677ac4..638e6de06be 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -22,7 +22,7 @@ import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 9b4dabcdcfd..8bf1b588c89 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.IBinder; diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 3a23808f361..88c186c88aa 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 760166bfcc5..f7743696553 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,7 +16,7 @@ package android.bluetooth; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 7e96c23af4b..e274af1b5c5 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -19,7 +19,7 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import java.nio.ByteBuffer; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 97e3f5221c7..c0c1aa1634f 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -18,8 +18,8 @@ package android.bluetooth.le; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothUuid; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; -- GitLab From 5d71da5f6837ab2c582559d429dcc67354e9911f Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Fri, 10 Jan 2020 02:32:54 +0800 Subject: [PATCH 1094/1408] A2DP: Check AAC bitrate mode by its CodecSpecific1 Bug: 112325138 Test: manual Change-Id: Ib00dd08df98f16a5d52df52d395bf1cb4aaf914c --- framework/java/android/bluetooth/BluetoothCodecConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 08d0797997b..33cb75ed076 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -616,8 +616,9 @@ public final class BluetoothCodecConfig implements Parcelable { if (other == null && mCodecType != other.mCodecType) { return false; } - // Currently we only care about the LDAC Playback Quality at CodecSpecific1 + // Currently we only care about the AAC VBR and LDAC Playback Quality at CodecSpecific1 switch (mCodecType) { + case SOURCE_CODEC_TYPE_AAC: case SOURCE_CODEC_TYPE_LDAC: if (mCodecSpecific1 != other.mCodecSpecific1) { return false; -- GitLab From 7a0602e3d45617e7e51f93dec7d0c00aeab45b6f Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 17 Jan 2020 15:09:44 -0800 Subject: [PATCH 1095/1408] Add error log to BluetoothAdapter.factoryReset() and make BluetoothAdapter.getUuids() marked with @Nullable Bug: 144765076 Test: Manual Change-Id: I2a38fbb2256177ad29e2739ccbec8c13aae9df6a --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 220b77b299a..57ccaa83ae9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1222,6 +1222,7 @@ public final class BluetoothAdapter { if (mService != null) { return mService.factoryReset(); } + Log.e(TAG, "factoryReset(): IBluetooth Service is null"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1239,7 +1240,7 @@ public final class BluetoothAdapter { */ @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) - public @NonNull ParcelUuid[] getUuids() { + public @Nullable ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; } -- GitLab From 9a68067a4289606fbcc968b7eb096d23b5e6d244 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Wed, 16 Oct 2019 20:27:23 +0800 Subject: [PATCH 1096/1408] Context-aware Bluetooth airplane mode Do not automatically turn off Bluetooth when airplane mode is turned on and Bluetooth is in one of the following situations: 1. Bluetooth A2DP is connected. 2. Bluetooth Hearing Aid profile is connected. Bug: 142831248 Test: Manual Change-Id: I7f10b27102d91cc7a9803f74c88a4af80a294fb9 --- .../BluetoothAirplaneModeListener.java | 258 ++++++++++++++++++ .../bluetooth/BluetoothManagerService.java | 131 +++++---- .../BluetoothAirplaneModeListenerTest.java | 125 +++++++++ 3 files changed, 457 insertions(+), 57 deletions(-) create mode 100644 service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java create mode 100644 service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java new file mode 100644 index 00000000000..31cd5d519d8 --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -0,0 +1,258 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; +import android.widget.Toast; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +/** + * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks + * whether we need to inform BluetoothManagerService on this change. + * + * The information of airplane mode turns on would not be passed to the BluetoothManagerService + * when Bluetooth is on and Bluetooth is in one of the following situations: + * 1. Bluetooth A2DP is connected. + * 2. Bluetooth Hearing Aid profile is connected. + */ +class BluetoothAirplaneModeListener { + private static final String TAG = "BluetoothAirplaneModeListener"; + @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count"; + + private static final int MSG_AIRPLANE_MODE_CHANGED = 0; + + @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times + + private final BluetoothManagerService mBluetoothManager; + private final BluetoothAirplaneModeHandler mHandler; + private AirplaneModeHelper mAirplaneHelper; + + @VisibleForTesting int mToastCount = 0; + + BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) { + mBluetoothManager = service; + + mHandler = new BluetoothAirplaneModeHandler(looper); + context.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, + mAirplaneModeObserver); + } + + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { + @Override + public void onChange(boolean unused) { + // Post from system main thread to android_io thread. + Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED); + mHandler.sendMessage(msg); + } + }; + + private class BluetoothAirplaneModeHandler extends Handler { + BluetoothAirplaneModeHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_AIRPLANE_MODE_CHANGED: + handleAirplaneModeChange(); + break; + default: + Log.e(TAG, "Invalid message: " + msg.what); + break; + } + } + } + + /** + * Call after boot complete + */ + @VisibleForTesting + void start(AirplaneModeHelper helper) { + Log.i(TAG, "start"); + mAirplaneHelper = helper; + mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); + } + + @VisibleForTesting + boolean shouldPopToast() { + if (mToastCount >= MAX_TOAST_COUNT) { + return false; + } + mToastCount++; + mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount); + return true; + } + + @VisibleForTesting + void handleAirplaneModeChange() { + if (shouldSkipAirplaneModeChange()) { + Log.i(TAG, "Ignore airplane mode change"); + // We have to store Bluetooth state here, so if user turns off Bluetooth + // after airplane mode is turned on, we don't forget to turn on Bluetooth + // when airplane mode turns off. + mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + if (shouldPopToast()) { + mAirplaneHelper.showToastMessage(); + } + return; + } + mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + } + + @VisibleForTesting + boolean shouldSkipAirplaneModeChange() { + if (mAirplaneHelper == null) { + return false; + } + if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() + || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { + return false; + } + return true; + } + + /** + * Helper class that handles callout and callback methods without + * complex logic. + */ + @VisibleForTesting + public static class AirplaneModeHelper { + private volatile BluetoothA2dp mA2dp; + private volatile BluetoothHearingAid mHearingAid; + private final BluetoothAdapter mAdapter; + private final Context mContext; + + AirplaneModeHelper(Context context) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; + + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, + BluetoothProfile.HEARING_AID); + } + + private final ServiceListener mProfileServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + // Setup Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = (BluetoothHearingAid) proxy; + break; + default: + break; + } + } + + @Override + public void onServiceDisconnected(int profile) { + // Clear Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = null; + break; + default: + break; + } + } + }; + + @VisibleForTesting + public boolean isA2dpOrHearingAidConnected() { + return isA2dpConnected() || isHearingAidConnected(); + } + + @VisibleForTesting + public boolean isBluetoothOn() { + final BluetoothAdapter adapter = mAdapter; + if (adapter == null) { + return false; + } + return adapter.getLeState() == BluetoothAdapter.STATE_ON; + } + + @VisibleForTesting + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + @VisibleForTesting + public void onAirplaneModeChanged(BluetoothManagerService managerService) { + managerService.onAirplaneModeChanged(); + } + + @VisibleForTesting + public int getSettingsInt(String name) { + return Settings.Global.getInt(mContext.getContentResolver(), + name, 0); + } + + @VisibleForTesting + public void setSettingsInt(String name, int value) { + Settings.Global.putInt(mContext.getContentResolver(), + name, value); + } + + @VisibleForTesting + public void showToastMessage() { + Resources r = mContext.getResources(); + final CharSequence text = r.getString( + R.string.bluetooth_airplane_mode_toast, 0); + Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); + } + + private boolean isA2dpConnected() { + final BluetoothA2dp a2dp = mA2dp; + if (a2dp == null) { + return false; + } + return a2dp.getConnectedDevices().size() > 0; + } + + private boolean isHearingAidConnected() { + final BluetoothHearingAid hearingAid = mHearingAid; + if (hearingAid == null) { + return false; + } + return hearingAid.getConnectedDevices().size() > 0; + } + }; +} diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index cdfd310a85a..fa8eda54e53 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -68,6 +68,7 @@ import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.pm.UserRestrictionsUtils; @@ -138,7 +139,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Bluetooth persisted setting is on // but Airplane mode will affect Bluetooth state at start up // and Airplane mode will have higher priority. - private static final int BLUETOOTH_ON_AIRPLANE = 2; + @VisibleForTesting + static final int BLUETOOTH_ON_AIRPLANE = 2; private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; @@ -159,6 +161,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mBinding; private boolean mUnbinding; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; @@ -257,68 +261,65 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { - @Override - public void onChange(boolean unused) { - synchronized (this) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } + public void onAirplaneModeChanged() { + synchronized (this) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } + } - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - return; - } finally { - mBluetoothLock.readLock().unlock(); + int st = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + st = mBluetooth.getState(); } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return; + } finally { + mBluetoothLock.readLock().unlock(); + } - Slog.d(TAG, - "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( - st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); + Slog.d(TAG, + "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( + st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); + if (isAirplaneModeOn()) { + // Clear registered LE apps to force shut-off + clearBleApps(); - // If state is BLE_ON make sure we trigger disableBLE - if (st == BluetoothAdapter.STATE_BLE_ON) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - addActiveLog( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); + // If state is BLE_ON make sure we trigger disableBLE + if (st == BluetoothAdapter.STATE_BLE_ON) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + mEnable = false; + mEnableExternal = false; } - } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } - } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + } else if (st == BluetoothAdapter.STATE_ON) { + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, mContext.getPackageName()); } + } else if (mEnableExternal) { + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } - }; + } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -430,9 +431,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); if (airplaneModeRadios == null || airplaneModeRadios.contains( Settings.Global.RADIO_BLUETOOTH)) { - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, - mAirplaneModeObserver); + mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( + this, IoThread.get().getLooper(), context); } int systemUiUid = -1; @@ -478,6 +478,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return state != BLUETOOTH_OFF; } + private boolean isBluetoothPersistedStateOnAirplane() { + if (!supportBluetoothPersistedState()) { + return false; + } + int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); + if (DBG) { + Slog.d(TAG, "Bluetooth persisted state: " + state); + } + return state == BLUETOOTH_ON_AIRPLANE; + } + /** * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ @@ -954,10 +965,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } synchronized (mReceiver) { - if (persist) { - persistBluetoothSetting(BLUETOOTH_OFF); + if (!isBluetoothPersistedStateOnAirplane()) { + if (persist) { + persistBluetoothSetting(BLUETOOTH_OFF); + } + mEnableExternal = false; } - mEnableExternal = false; sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } @@ -1185,6 +1198,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } + if (mBluetoothAirplaneModeListener != null) { + mBluetoothAirplaneModeListener.start( + new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); + } } /** diff --git a/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java new file mode 100644 index 00000000000..968a402ff3b --- /dev/null +++ b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static org.mockito.Mockito.*; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.os.Looper; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@MediumTest +@RunWith(AndroidJUnit4.class) +public class BluetoothAirplaneModeListenerTest { + private Context mContext; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + private BluetoothAdapter mBluetoothAdapter; + private AirplaneModeHelper mHelper; + + @Mock BluetoothManagerService mBluetoothManagerService; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + + mHelper = mock(AirplaneModeHelper.class); + when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) + .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); + doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); + doNothing().when(mHelper).showToastMessage(); + doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class)); + + mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( + mBluetoothManagerService, Looper.getMainLooper(), mContext); + mBluetoothAirplaneModeListener.start(mHelper); + } + + @Test + public void testIgnoreOnAirplanModeChange() { + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isBluetoothOn()).thenReturn(true); + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isAirplaneModeOn()).thenReturn(true); + Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + } + + @Test + public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() { + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { + mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; + when(mHelper.isBluetoothOn()).thenReturn(true); + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isAirplaneModeOn()).thenReturn(true); + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + + verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + verify(mHelper, times(0)).showToastMessage(); + verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { + mBluetoothAirplaneModeListener.mToastCount = 0; + when(mHelper.isBluetoothOn()).thenReturn(true); + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isAirplaneModeOn()).thenReturn(true); + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + + verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + verify(mHelper).showToastMessage(); + verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testIsPopToast_PopToast() { + mBluetoothAirplaneModeListener.mToastCount = 0; + Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast()); + verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1); + } + + @Test + public void testIsPopToast_NotPopToast() { + mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast()); + verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt()); + } +} -- GitLab From 07f4803cb3f43a19607ffebc7e2e27cb03ae5270 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 21 Jan 2020 15:11:22 -0800 Subject: [PATCH 1097/1408] Make certain BluetoothProfile constants being used by CarBluetoothTest into @SystemApi Bug: 146663105 Test: Manual Change-Id: Ic474a85c17b69c71cf24b06d0262c13b3b8142fe --- framework/java/android/bluetooth/BluetoothProfile.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 638e6de06be..7538df8bbe5 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -146,7 +146,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int A2DP_SINK = 11; /** @@ -154,7 +154,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int AVRCP_CONTROLLER = 12; /** @@ -169,6 +169,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int HEADSET_CLIENT = 16; /** @@ -176,6 +177,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int PBAP_CLIENT = 17; /** -- GitLab From 69ab794b2eb773197ddf180f64da0d751aa9963d Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 22 Jan 2020 11:24:29 -0800 Subject: [PATCH 1098/1408] Add @RequiresPermission annotation to SystemApis in BluetoothPan, open up close method as a SystemApi, and BluetoothPan class now implements AutoCloseable and CloseGuard Bug: 146045934 Test: Manual Change-Id: Iafb2157d67ae4f8e3d7d97cf4198f2978e391a8c --- .../java/android/bluetooth/BluetoothPan.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 024bb06098a..ec63fd058b1 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -30,6 +30,7 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.util.CloseGuard; import android.util.Log; import java.lang.annotation.Retention; @@ -50,10 +51,11 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothPan implements BluetoothProfile { +public final class BluetoothPan implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; private static final boolean VDBG = false; + private CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the Pan @@ -166,10 +168,15 @@ public final class BluetoothPan implements BluetoothProfile { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); } - @UnsupportedAppUsage - /*package*/ void close() { + /** + * Closes the connection to the service and unregisters callbacks + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -178,8 +185,11 @@ public final class BluetoothPan implements BluetoothProfile { return mProfileConnector.getService(); } - + @RequiresPermission(Manifest.permission.BLUETOOTH) protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } close(); } @@ -316,6 +326,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothPan service = getService(); @@ -335,6 +346,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); @@ -355,6 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { * * @param value is whether to enable or disable bluetooth tethering */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -373,6 +386,7 @@ public final class BluetoothPan implements BluetoothProfile { * * @return true if tethering is on, false if not or some error occurred */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); -- GitLab From 745310e2c9cdf886b71ffa7ad1b2404f9d0252f7 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 22 Jan 2020 14:26:35 -0800 Subject: [PATCH 1099/1408] Use @ConnectionPolicy annotation more consistently in classes with methods setConnectionPolicy and getConnectionPolicy Bug: 147669289 Test: Manual Change-Id: I2b9b0391a02d01623c1cd253f2da12b2baaea599 --- framework/java/android/bluetooth/BluetoothA2dpSink.java | 5 +++-- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 5 +++-- framework/java/android/bluetooth/BluetoothHidHost.java | 5 +++-- framework/java/android/bluetooth/BluetoothMap.java | 5 +++-- framework/java/android/bluetooth/BluetoothMapClient.java | 5 +++-- framework/java/android/bluetooth/BluetoothPbapClient.java | 5 +++-- framework/java/android/bluetooth/BluetoothSap.java | 5 +++-- 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 8993de0939e..ee2cc6d1471 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -297,7 +297,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -345,7 +346,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 6de1ffb6344..fbda9e9d6dd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -584,7 +584,8 @@ public final class BluetoothHeadsetClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadsetClient service = getService(); @@ -633,7 +634,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index c1233b800a5..26e3e271bff 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -416,7 +416,8 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -464,7 +465,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 46747067428..1c62faa97ee 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -340,7 +340,8 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -388,7 +389,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0aa5aac5d8f..8d2aaddd38d 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)) { diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 9618ba01572..9563c68ce65 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -271,7 +271,8 @@ public final class BluetoothPbapClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); } @@ -323,7 +324,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) { log("getConnectionPolicy(" + device + ")"); } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 8bf1b588c89..bfc3a4d25c2 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -330,7 +330,8 @@ public final class BluetoothSap 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -378,7 +379,7 @@ public final class BluetoothSap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- GitLab From 2a9950384b458e76ee998559590fef7b7e14a17f Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 19 Dec 2019 16:09:20 -0800 Subject: [PATCH 1100/1408] Bluetooth LE COC: Delete unused testing API Some methods are for SL4A test only, and now they have been migrated. Test: compile Change-Id: I86e22814fc9a3fd296c359804a465b9ead4d926f --- .../android/bluetooth/BluetoothAdapter.java | 25 ------------------- .../android/bluetooth/BluetoothDevice.java | 22 ---------------- 2 files changed, 47 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index bd0a39c06b2..8415ecd38b7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3235,18 +3235,6 @@ public final class BluetoothAdapter { return socket; } - /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, listenUsingL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingL2capCoc(int transport) - throws IOException { - Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel"); - return listenUsingL2capChannel(); - } - /** * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The @@ -3293,19 +3281,6 @@ public final class BluetoothAdapter { return socket; } - /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, listenUsingInsecureL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) - throws IOException { - Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, " - + "listenUsingInsecureL2capChannel"); - return listenUsingInsecureL2capChannel(); - } - /** * Register a {@link #OnMetadataChangedListener} to receive update about metadata * changes for this {@link BluetoothDevice}. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 9fe4dd66b87..12dc814c341 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -2171,17 +2171,6 @@ public final class BluetoothDevice implements Parcelable { null); } - /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, createL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { - Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel"); - return createL2capChannel(psm); - } - /** * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can * be used to start a secure outgoing connection to the remote device with the same dynamic @@ -2212,17 +2201,6 @@ public final class BluetoothDevice implements Parcelable { null); } - /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, createInsecureL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { - Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); - return createInsecureL2capChannel(psm); - } - /** * Set a keyed metadata of this {@link BluetoothDevice} to a * {@link String} value. -- GitLab From 8b5b3b2ed91abad0a081f509c0bc23c8b3181aaa Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 29 Jan 2020 11:53:10 -0800 Subject: [PATCH 1101/1408] Add api BluetoothAdapter.getMostRecentlyConnectedDevices() to be able to get connected devices ordered by how recently they were connected Bug: 130984590 Test: Manual Change-Id: Ie5d0c7a8e6bc5daad5ff14064b0c65b7c4c5e6b0 --- .../android/bluetooth/BluetoothAdapter.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8415ecd38b7..b6d096c2524 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2161,6 +2161,33 @@ public final class BluetoothAdapter { } } + /** + * Fetches a list of the most recently connected bluetooth devices ordered by how recently they + * were connected with most recently first and least recently last + * + * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were + * connected + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public @NonNull List getMostRecentlyConnectedDevices() { + if (getState() != STATE_ON) { + return new ArrayList<>(); + } + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getMostRecentlyConnectedDevices(); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return new ArrayList<>(); + } + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. -- GitLab From b867092b3481fc95fbaf01c5b97592c62367823c Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 30 Jan 2020 12:02:17 -0800 Subject: [PATCH 1102/1408] Add privileged permission to SystemApi BluetoothDevice#setAlias Bug: 144792904 Test: Manual Change-Id: Ie11109736bb0eb4cd0f06c38db44805c0169ac5f --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 12dc814c341..65f21ed7f44 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1091,7 +1091,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; if (service == null) { -- GitLab From d1a109e70872b72414e1e8405c7ff035f31729a3 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 30 Jan 2020 13:49:35 -0800 Subject: [PATCH 1103/1408] Add BLUETOOTH_PRIVILEGED permission to SystemApi BluetoothHidDevice#setConnectionPolicy Bug: 148597061 Test: Manual Change-Id: I03875cb71483d3d6e18795324a7fbea6681fb9b0 --- framework/java/android/bluetooth/BluetoothHidDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index a923be62fbc..b5959c06cc1 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -706,7 +706,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); -- GitLab From 5358c2101682f6179c8826b8a7fb1e170c3f66d1 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 30 Jan 2020 16:59:02 -0800 Subject: [PATCH 1104/1408] Add new API BluetoothAdapter#removeActiveDevice to replace calls to BluetoothAdapter#setActiveDevice with a null device Bug: 147428526 Test: Manual Change-Id: I838448a504515d5a8b1eec254ccc1d9414239475 --- .../android/bluetooth/BluetoothAdapter.java | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b6d096c2524..3bc83dbcd04 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1766,6 +1766,45 @@ public final class BluetoothAdapter { } /** + * Removes the active device for the grouping of @ActiveDeviceUse specified + * + * @param profiles represents the purpose for which we are setting this as the active device. + * Possible values are: + * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, + * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, + * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} + * @return false on immediate error, true otherwise + * @throws IllegalArgumentException if device is null or profiles is not one of + * {@link ActiveDeviceUse} + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean removeActiveDevice(@ActiveDeviceUse int profiles) { + if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL + && profiles != ACTIVE_DEVICE_ALL) { + Log.e(TAG, "Invalid profiles param value in removeActiveDevice"); + throw new IllegalArgumentException("Profiles must be one of " + + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " + + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " + + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.removeActiveDevice(profiles); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** + * Sets device as the active devices for the profiles passed into the function * * @param device is the remote bluetooth device * @param profiles represents the purpose for which we are setting this as the active device. @@ -1774,18 +1813,26 @@ public final class BluetoothAdapter { * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} * @return false on immediate error, true otherwise + * @throws IllegalArgumentException if device is null or profiles is not one of + * {@link ActiveDeviceUse} * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setActiveDevice(@Nullable BluetoothDevice device, + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setActiveDevice(@NonNull BluetoothDevice device, @ActiveDeviceUse int profiles) { + if (device == null) { + Log.e(TAG, "setActiveDevice: Null device passed as parameter"); + throw new IllegalArgumentException("device cannot be null"); + } if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL && profiles != ACTIVE_DEVICE_ALL) { Log.e(TAG, "Invalid profiles param value in setActiveDevice"); - return false; + throw new IllegalArgumentException("Profiles must be one of " + + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " + + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " + + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); } - try { mServiceLock.readLock().lock(); if (mService != null) { -- GitLab From 4ac534bee87afa14d6fdfbba8209b03a16633e20 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 30 Jan 2020 17:04:54 -0800 Subject: [PATCH 1105/1408] Revert value of BluetoothDevice.ACTION_ALIAS_CHANGED back to android.bluetooth.device.action.ALIAS_CHANGED from android.bluetooth.action.ALIAS_CHANGED Bug: 146158681 Test: Manual Change-Id: Ie247568ff3e5a750d74f22636aca69e3e74a55f5 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 12dc814c341..1872ae03ee9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -179,9 +180,10 @@ public final class BluetoothDevice implements Parcelable { *

          Always contains the extra field {@link #EXTRA_DEVICE}. *

          Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @SuppressLint("ActionValue") @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ALIAS_CHANGED = - "android.bluetooth.action.ALIAS_CHANGED"; + "android.bluetooth.device.action.ALIAS_CHANGED"; /** * Broadcast Action: Indicates a change in the bond state of a remote -- GitLab From a5ff0917296a796c862b44d9121460952d2cb57a Mon Sep 17 00:00:00 2001 From: Muhammad Qureshi Date: Tue, 28 Jan 2020 10:54:17 -0800 Subject: [PATCH 1106/1408] Use FrameworkStatsLog instead of StatsLog As part of statsd becoming a Mainline module in R, autogenerated StatsLog.write() calls are going away and replaced by *StatsLog.java that is autogenerated for each module. This CL replaces some usages of StatsLog with FrameworkStatsLog. Bug: 145952197 Test: m Change-Id: Iaa2db34a7be4c3215f62cc36661ba8ac81656baa --- .../server/bluetooth/BluetoothManagerService.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 3774b645339..311a494ee57 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -69,17 +69,17 @@ import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Slog; -import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.HashMap; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; import java.util.Map; @@ -87,7 +87,6 @@ import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; - class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -2300,9 +2299,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); } - int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : - StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; - StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, + int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : + FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, Binder.getCallingUid(), null, state, reason, packageName); } -- GitLab From 2e2758221e5f4a9b727aa6afdeff77415edb81a8 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 31 Jan 2020 11:40:15 -0800 Subject: [PATCH 1107/1408] Change data type of duration param from int representing seconds to long representing milliseconds in BluetoothAdapter#setScanMode Bug: 144380530 Test: Manual Change-Id: I76528478a64d22afefd4ec964d1a78295dd3c94f --- .../android/bluetooth/BluetoothAdapter.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3bc83dbcd04..56424c9f5a7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1485,9 +1485,8 @@ public final class BluetoothAdapter { *

          The Bluetooth scan mode determines if the local adapter is * connectable and/or discoverable from remote Bluetooth devices. *

          For privacy reasons, discoverable mode is automatically turned off - * after duration seconds. For example, 120 seconds should be - * enough for a remote device to initiate and complete its discovery - * process. + * after durationMillis milliseconds. For example, 120000 milliseconds should be + * enough for a remote device to initiate and complete its discovery process. *

          Valid scan mode values are: * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, @@ -1502,24 +1501,29 @@ public final class BluetoothAdapter { * instead. * * @param mode valid scan mode - * @param duration time in seconds to apply scan mode, only used for {@link + * @param durationMillis time in milliseconds to apply scan mode, only used for {@link * #SCAN_MODE_CONNECTABLE_DISCOVERABLE} * @return true if the scan mode was set, false otherwise * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public boolean setScanMode(@ScanMode int mode, int duration) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setScanMode(@ScanMode int mode, long durationMillis) { if (getState() != STATE_ON) { return false; } try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, duration); + int durationSeconds = Math.toIntExact(durationMillis / 1000); + return mService.setScanMode(mode, durationSeconds); } } catch (RemoteException e) { Log.e(TAG, "", e); + } catch (ArithmeticException ex) { + Log.e(TAG, "setScanMode: Duration in seconds outside of the bounds of an int"); + throw new IllegalArgumentException("Duration not in bounds. In seconds, the " + + "durationMillis must be in the range of an int"); } finally { mServiceLock.readLock().unlock(); } @@ -1552,13 +1556,22 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; } - /* getDiscoverableTimeout() to use the latest from NV than use 0 */ - return setScanMode(mode, getDiscoverableTimeout()); + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.setScanMode(mode, getDiscoverableTimeout()); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; } /** @hide */ -- GitLab From 71b54554e6d291e688ad24d76fe06e0a07a11c7d Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Mon, 30 Dec 2019 10:57:12 -0800 Subject: [PATCH 1108/1408] Binder cache for Bluetooth getState(). Bug: 140788621 Test: A special build that puts the PropertyInvalidatedCache in verification mode was loaded on the device. Then one iteration of MPTS was executed. No cache inconsistencies were found and no SELinux violations (associated with the binder cache) were found. The number of cache misses was approximately 3% of the total binder calls. All binder calls went through the cache. Repeated the test with a work profile installed (to provide a second user ID). Change-Id: I2f1d0b6d61dedc5d1badb06a20c5932eca415904 --- .../android/bluetooth/BluetoothAdapter.java | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2e93d43f7c1..f39847429a2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.app.ActivityThread; +import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; @@ -994,6 +995,37 @@ public final class BluetoothAdapter { return false; } + private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state"; + + private final PropertyInvalidatedCache mBluetoothGetStateCache = + new PropertyInvalidatedCache( + 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { + @Override + protected Integer recompute(Void query) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getState(); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothAdapter.STATE_OFF; + } + }; + + /** @hide */ + public void disableBluetoothGetStateCache() { + mBluetoothGetStateCache.disableLocal(); + } + + /** @hide */ + public static void invalidateBluetoothGetStateCache() { + PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY); + } + /** * Get the current state of the local Bluetooth adapter. *

          Possible return values are @@ -1007,18 +1039,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = BluetoothAdapter.STATE_OFF; - - try { - mServiceLock.readLock().lock(); - if (mService != null) { - state = mService.getState(); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } + int state = mBluetoothGetStateCache.query(null); // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -1056,18 +1077,7 @@ public final class BluetoothAdapter { @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") public int getLeState() { - int state = BluetoothAdapter.STATE_OFF; - - try { - mServiceLock.readLock().lock(); - if (mService != null) { - state = mService.getState(); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } + int state = mBluetoothGetStateCache.query(null); if (VDBG) { Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); -- GitLab From 3da3b7a21ab53068b194c1fd54ce6c8c2374196d Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 5 Feb 2020 14:03:19 -0800 Subject: [PATCH 1109/1408] BluetoothAdapter#connectAllEnabledProfiles and BluetoothAdapter#disconnectAllEnabledProfiles updated to require BLUETOOTH_PRIVILEGED permission and update documentation to indicate connection and disconnection is asynchronous Bug: 147321746 Test: Manual Change-Id: I961f02a539a247b1397ce4f478b9dc804d9973ab --- .../android/bluetooth/BluetoothAdapter.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 56424c9f5a7..0a9dbb608b9 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1861,15 +1861,19 @@ public final class BluetoothAdapter { } /** - * Connects all enabled and supported bluetooth profiles between the local and remote device + * Connects all enabled and supported bluetooth profiles between the local and remote device. + * Connection is asynchronous and you should listen to each profile's broadcast intent + * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example, + * to verify a2dp is connected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} * * @param device is the remote device with which to connect these profiles - * @return true if all profiles successfully connected, false if an error occurred + * @return true if message sent to try to connect all profiles, false if an error occurred * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -1886,15 +1890,19 @@ public final class BluetoothAdapter { } /** - * Disconnects all enabled and supported bluetooth profiles between the local and remote device + * Disconnects all enabled and supported bluetooth profiles between the local and remote device. + * Disconnection is asynchronous and you should listen to each profile's broadcast intent + * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, + * to verify a2dp is disconnected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} * * @param device is the remote device with which to disconnect these profiles - * @return true if all profiles successfully disconnected, false if an error occurred + * @return true if message sent to try to disconnect all profiles, false if an error occurred * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); -- GitLab From f4529f00b1ee030a3a0129f22211921c660f42b2 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 3 Feb 2020 16:51:11 -0800 Subject: [PATCH 1110/1408] Require user pass in a non-null BluetoothDevice to all BluetoothA2dp APIs, rename APIs as per API guidelines Bug: 147287141 Test: Manual Change-Id: If6475af6fa7feab84eed7d3edeb0d2e24c1ed925 --- .../java/android/bluetooth/BluetoothA2dp.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d8c653c6d0d..b672a0857cc 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,8 +643,9 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); + verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -670,9 +671,14 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@Nullable BluetoothDevice device, - @Nullable BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@NonNull BluetoothDevice device, + @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); + verifyDeviceNotNull(device, "setCodecConfigPreference"); + if (codecConfig == null) { + Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); + throw new IllegalArgumentException("codecConfig cannot be null"); + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -695,8 +701,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@Nullable BluetoothDevice device) { + public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -709,8 +716,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@Nullable BluetoothDevice device) { + public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -750,7 +758,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { + public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsSupported"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -775,7 +784,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { + public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -800,8 +810,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { + verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -854,6 +865,13 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 13cabe68bac255d461f721c2357c7b840f2b0872 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 7 Feb 2020 17:21:21 -0800 Subject: [PATCH 1111/1408] Update BluetoothPbap class description to indicate how to create an instance of the class, add intdef to return value of BluetoothPbap#getConnectionState, and move all SystemApis to require the BLUETOOTH_PRIVILEGED permission Bug: 148966894 Test: Manual Change-Id: I4a798b0a16ab839f2047fc58f21c420cc99b6db6 --- .../java/android/bluetooth/BluetoothPbap.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index e07ca521e77..1f89ddf0afc 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -38,9 +38,6 @@ import java.util.Arrays; import java.util.List; /** - * The Android Bluetooth API is not finalized, and *will* change. Use at your - * own risk. - * * Public API for controlling the Bluetooth Pbap Service. This includes * Bluetooth Phone book Access profile. * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap @@ -56,6 +53,11 @@ import java.util.List; * notification when it is bound, this is especially important if you wish to * immediately call methods on BluetoothPbap after construction. * + * To get an instance of the BluetoothPbap class, you can call + * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param + * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of + * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}. + * * Android only supports one connected Bluetooth Pce at a time. * * @hide @@ -87,6 +89,7 @@ public class BluetoothPbap implements BluetoothProfile { */ @SuppressLint("ActionValue") @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; @@ -235,7 +238,8 @@ public class BluetoothPbap implements BluetoothProfile { */ @SystemApi @Override - public int getConnectionState(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) { log("getConnectionState: device=" + device); try { final IBluetoothPbap service = mService; @@ -287,7 +291,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); -- GitLab From 3b91300c6368be029c1616b9d0e8ab1c724e8658 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Mon, 30 Dec 2019 11:54:37 -0800 Subject: [PATCH 1112/1408] Binder caches for Bluetooth * A cache for isOffloadedFilteringSupported(). * A cache for getProfileConnectionState(). Bug: 140788621 Test: A special build that puts the PropertyInvalidatedCache in verification mode was loaded on the device. Then one iteration of MPTS was executed. No cache inconsistencies were found and no SELinux violations (associated with the binder cache) were found. The number of cache misses was approximately 15% of the total binder calls. A second test was run in which bluetooth headphones were connected and disconnected. Then bluetooth itself was disabled and then enabled. The caches were invalidated as expected and no errors were uncovered. Change-Id: Icfad1071725e2d1e320fd252a49f0c4ae8ce6ad0 --- .../android/bluetooth/BluetoothAdapter.java | 93 ++++++++++++++----- 1 file changed, 71 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 587c92e014c..66bfcbd27ca 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1970,6 +1970,38 @@ public final class BluetoothAdapter { } } + private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY = + "cache_key.bluetooth.is_offloaded_filtering_supported"; + private final PropertyInvalidatedCache mBluetoothFilteringCache = + new PropertyInvalidatedCache( + 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { + @Override + protected Boolean recompute(Void query) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.isOffloadedFilteringSupported(); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + + } + }; + + /** @hide */ + public void disableIsOffloadedFilteringSupportedCache() { + mBluetoothFilteringCache.disableLocal(); + } + + /** @hide */ + public static void invalidateIsOffloadedFilteringSupportedCache() { + PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY); + } + /** * Return true if offloaded filters are supported * @@ -1979,17 +2011,7 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return false; } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isOffloadedFilteringSupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; + return mBluetoothFilteringCache.query(null); } /** @@ -2361,6 +2383,43 @@ public final class BluetoothAdapter { return BluetoothAdapter.STATE_DISCONNECTED; } + private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY = + "cache_key.bluetooth.get_profile_connection_state"; + private final PropertyInvalidatedCache + mGetProfileConnectionStateCache = + new PropertyInvalidatedCache( + 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { + @Override + protected Integer recompute(Integer query) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getProfileConnectionState(query); + } + } catch (RemoteException e) { + Log.e(TAG, "getProfileConnectionState:", e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothProfile.STATE_DISCONNECTED; + } + @Override + public String queryToString(Integer query) { + return String.format("getProfileConnectionState(profile=\"%d\")", + query); + } + }; + + /** @hide */ + public void disableGetProfileConnectionStateCache() { + mGetProfileConnectionStateCache.disableLocal(); + } + + /** @hide */ + public static void invalidateGetProfileConnectionStateCache() { + PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY); + } + /** * Get the current connection state of a profile. * This function can be used to check whether the local Bluetooth adapter @@ -2378,17 +2437,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) { return BluetoothProfile.STATE_DISCONNECTED; } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getProfileConnectionState(profile); - } - } catch (RemoteException e) { - Log.e(TAG, "getProfileConnectionState:", e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothProfile.STATE_DISCONNECTED; + return mGetProfileConnectionStateCache.query(new Integer(profile)); } /** -- GitLab From 310160a8e64f9062a2f6f6c36c64c71e25e5d7cf Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 10 Feb 2020 13:37:51 -0800 Subject: [PATCH 1113/1408] BluetoothA2dpSink System APIs now require BLUETOOTH_ADMIN permission Bug: 149216030 Test: Manual Change-Id: Ib28c7f3133eb96c1ba0b43c8b140babde699d5f4 --- framework/java/android/bluetooth/BluetoothA2dpSink.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index ee2cc6d1471..ab492309ba3 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -66,7 +66,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @SuppressLint("ActionValue") - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; @@ -296,7 +296,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -345,7 +345,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -370,7 +370,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@Nullable BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- GitLab From b0af08ddd1ab7fc6d0fd5e9a13fbc2ec9f594e7d Mon Sep 17 00:00:00 2001 From: Daniel Chapin Date: Wed, 12 Feb 2020 17:16:10 +0000 Subject: [PATCH 1114/1408] Revert "Require user pass in a non-null BluetoothDevice to all B..." Revert "Pass in active device to all BluetoothA2dp APIs in packa..." Revert "Pass in active device to all BluetoothA2dp APIs in packa..." Revert submission 10253996-bt-a2dp-no-null Reason for revert: b/149361880 Reverted Changes: If43934374: Pass in active device to all BluetoothA2dp APIs in... I22dd1ca36: Make sure calls to BluetoothA2dp APIs pass non-nul... If6475af6f: Require user pass in a non-null BluetoothDevice to... I9d0e2c89c: Pass in active device to all BluetoothA2dp APIs in... I1faa6174d: Need to now pass in active device instead of null ... I69a941a7e: Pass in active device to all BluetoothA2dp APIs in... Change-Id: I297bda68da5023fd832201c485554d6bff05fa78 --- .../java/android/bluetooth/BluetoothA2dp.java | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b672a0857cc..d8c653c6d0d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,9 +643,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); - verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -671,14 +670,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@NonNull BluetoothDevice device, - @NonNull BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@Nullable BluetoothDevice device, + @Nullable BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); - verifyDeviceNotNull(device, "setCodecConfigPreference"); - if (codecConfig == null) { - Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); - throw new IllegalArgumentException("codecConfig cannot be null"); - } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -701,9 +695,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@NonNull BluetoothDevice device) { + public void enableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -716,9 +709,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@NonNull BluetoothDevice device) { + public void disableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -758,8 +750,7 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { - verifyDeviceNotNull(device, "isOptionalCodecsSupported"); + public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -784,8 +775,7 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { - verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); + public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -810,9 +800,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, + public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { - verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -865,13 +854,6 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { - if (device == null) { - Log.e(TAG, methodName + ": device param is null"); - throw new IllegalArgumentException("Device cannot be null"); - } - } - private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 9f4e11af8f433b19ff918f14cceb5b83001871fd Mon Sep 17 00:00:00 2001 From: Daniel Chapin Date: Wed, 12 Feb 2020 17:16:10 +0000 Subject: [PATCH 1115/1408] Revert "Require user pass in a non-null BluetoothDevice to all B..." Revert "Pass in active device to all BluetoothA2dp APIs in packa..." Revert "Pass in active device to all BluetoothA2dp APIs in packa..." Revert submission 10253996-bt-a2dp-no-null Reason for revert: b/149361880 Reverted Changes: If43934374: Pass in active device to all BluetoothA2dp APIs in... I22dd1ca36: Make sure calls to BluetoothA2dp APIs pass non-nul... If6475af6f: Require user pass in a non-null BluetoothDevice to... I9d0e2c89c: Pass in active device to all BluetoothA2dp APIs in... I1faa6174d: Need to now pass in active device instead of null ... I69a941a7e: Pass in active device to all BluetoothA2dp APIs in... Change-Id: I297bda68da5023fd832201c485554d6bff05fa78 (cherry picked from commit b0af08ddd1ab7fc6d0fd5e9a13fbc2ec9f594e7d) --- .../java/android/bluetooth/BluetoothA2dp.java | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b672a0857cc..d8c653c6d0d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,9 +643,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); - verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -671,14 +670,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@NonNull BluetoothDevice device, - @NonNull BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@Nullable BluetoothDevice device, + @Nullable BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); - verifyDeviceNotNull(device, "setCodecConfigPreference"); - if (codecConfig == null) { - Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); - throw new IllegalArgumentException("codecConfig cannot be null"); - } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -701,9 +695,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@NonNull BluetoothDevice device) { + public void enableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -716,9 +709,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@NonNull BluetoothDevice device) { + public void disableOptionalCodecs(@Nullable BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -758,8 +750,7 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { - verifyDeviceNotNull(device, "isOptionalCodecsSupported"); + public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -784,8 +775,7 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { - verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); + public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -810,9 +800,8 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, + public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { - verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -865,13 +854,6 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } - private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { - if (device == null) { - Log.e(TAG, methodName + ": device param is null"); - throw new IllegalArgumentException("Device cannot be null"); - } - } - private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 2167e541b1840a617b3632047d63719234a7ab1c Mon Sep 17 00:00:00 2001 From: Mike Ma Date: Wed, 12 Feb 2020 13:07:47 -0800 Subject: [PATCH 1116/1408] Implement dumpsys bluetooth_manager --proto Add BluetoothManagerServiceDumpProto for protobuf dumpsys of bluetooth manager service. Primarily used by incident service to capture an incident report proto. Command to invoke (any of the following after lunch and env setup): $ adb shell dumpsys bluetooth_manager --proto $ adb shell incident 3050 $ incident_report 3050 Bug: 146085372 Test: Execute the above commands and compare the output against normal dumpsys Change-Id: I7ccef334ffeae02316db3afff929998b544972b1 --- .../bluetooth/BluetoothManagerService.java | 131 +++++++++++------- 1 file changed, 83 insertions(+), 48 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 311a494ee57..a4a42bcaecb 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -69,6 +69,7 @@ import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -196,6 +197,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName; } + void dump(ProtoOutputStream proto) { + proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp); + proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable); + proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName); + proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason); + } } private final LinkedList mActiveLogs = new LinkedList<>(); @@ -2408,56 +2415,56 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) { return; } + if ((args.length > 0) && args[0].startsWith("--proto")) { + dumpProto(fd); + return; + } String errorMsg = null; - boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); - - if (!protoOut) { - writer.println("Bluetooth Status"); - writer.println(" enabled: " + isEnabled()); - writer.println(" state: " + BluetoothAdapter.nameForState(mState)); - writer.println(" address: " + mAddress); - writer.println(" name: " + mName); - if (mEnable) { - long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime; - String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d", - (int) (onDuration / (1000 * 60 * 60)), - (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60), - (int) (onDuration % 1000)); - writer.println(" time since enabled: " + onDurationString); - } - - if (mActiveLogs.size() == 0) { - writer.println("\nBluetooth never enabled!"); - } else { - writer.println("\nEnable log:"); - for (ActiveLog log : mActiveLogs) { - writer.println(" " + log); - } + writer.println("Bluetooth Status"); + writer.println(" enabled: " + isEnabled()); + writer.println(" state: " + BluetoothAdapter.nameForState(mState)); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName); + if (mEnable) { + long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime; + String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d", + (int) (onDuration / (1000 * 60 * 60)), + (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60), + (int) (onDuration % 1000)); + writer.println(" time since enabled: " + onDurationString); + } + + if (mActiveLogs.size() == 0) { + writer.println("\nBluetooth never enabled!"); + } else { + writer.println("\nEnable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(" " + log); } + } - writer.println( - "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); - if (mCrashes == CRASH_LOG_MAX_SIZE) { - writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); - } - for (Long time : mCrashTimestamps) { - writer.println(" " + timeToLog(time)); - } + writer.println( + "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); + if (mCrashes == CRASH_LOG_MAX_SIZE) { + writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); + } + for (Long time : mCrashTimestamps) { + writer.println(" " + timeToLog(time)); + } - writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s") - + "registered"); - for (ClientDeathRecipient app : mBleApps.values()) { - writer.println(" " + app.getPackageName()); - } + writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s") + + " registered"); + for (ClientDeathRecipient app : mBleApps.values()) { + writer.println(" " + app.getPackageName()); + } - writer.println(""); - writer.flush(); - if (args.length == 0) { - // Add arg to produce output - args = new String[1]; - args[0] = "--print"; - } + writer.println(""); + writer.flush(); + if (args.length == 0) { + // Add arg to produce output + args = new String[1]; + args[0] = "--print"; } if (mBluetoothBinder == null) { @@ -2470,14 +2477,42 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (errorMsg != null) { - // Silently return if we are extracting metrics in Protobuf format - if (protoOut) { - return; - } writer.println(errorMsg); } } + private void dumpProto(FileDescriptor fd) { + final ProtoOutputStream proto = new ProtoOutputStream(fd); + proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled()); + proto.write(BluetoothManagerServiceDumpProto.STATE, mState); + proto.write(BluetoothManagerServiceDumpProto.STATE_NAME, + BluetoothAdapter.nameForState(mState)); + proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress); + proto.write(BluetoothManagerServiceDumpProto.NAME, mName); + if (mEnable) { + proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime); + } + proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS, + SystemClock.elapsedRealtime()); + for (ActiveLog log : mActiveLogs) { + long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS); + log.dump(proto); + proto.end(token); + } + proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes); + proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED, + mCrashes == CRASH_LOG_MAX_SIZE); + for (Long time : mCrashTimestamps) { + proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time); + } + proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size()); + for (ClientDeathRecipient app : mBleApps.values()) { + proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES, + app.getPackageName()); + } + proto.flush(); + } + private static String getEnableDisableReasonString(int reason) { switch (reason) { case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST: -- GitLab From c3389265edab7bf009a17e30fcd4069d06bd1230 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Thu, 13 Feb 2020 11:34:40 -0800 Subject: [PATCH 1117/1408] Binder cache for Bluetooth getBondState() Bug: 140788621 Test: A special build that puts the PropertyInvalidatedCache in verification mode was loaded on the device. Then one iteration of MPTS was executed. No cache inconsistencies were found and no SELinux violations (associated with the binder cache) were found. The number of cache misses was approximately 10% of the total binder calls. Then the phone was cycled through bluetooth pairing and unpairing events. Change-Id: Ia494f0ad58b889130052e5beb3bec6d1011508ef --- .../android/bluetooth/BluetoothDevice.java | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5b60b85f472..3e1a480b4f6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; @@ -1299,6 +1300,31 @@ public final class BluetoothDevice implements Parcelable { return false; } + private static final String BLUETOOTH_BONDING_CACHE_PROPERTY = + "cache_key.bluetooth.get_bond_state"; + private final PropertyInvalidatedCache mBluetoothBondCache = + new PropertyInvalidatedCache( + 8, BLUETOOTH_BONDING_CACHE_PROPERTY) { + @Override + protected Integer recompute(BluetoothDevice query) { + try { + return sService.getBondState(query); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + }; + + /** @hide */ + public void disableBluetoothGetBondStateCache() { + mBluetoothBondCache.disableLocal(); + } + + /** @hide */ + public static void invalidateBluetoothGetBondStateCache() { + PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY); + } + /** * Get the bond state of the remote device. *

          Possible values for the bond state are: @@ -1316,9 +1342,13 @@ public final class BluetoothDevice implements Parcelable { return BOND_NONE; } try { - return service.getBondState(this); - } catch (RemoteException e) { - Log.e(TAG, "", e); + return mBluetoothBondCache.query(this); + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "", e); + } else { + throw e; + } } return BOND_NONE; } -- GitLab From 1f211dfc8388a89d518b5abaf3ef5760be3721b2 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 24 Feb 2020 13:28:30 -0800 Subject: [PATCH 1118/1408] Update javadoc to reflect that BluetoothPbap#setConnectionPolicy returns true on successfully setting the connection policy instead of on disconnection. It also now indicates that if BluetoothProfile#CONNECTION_POLICY_FORBIDDEN is passed in, the profile will be disconnected. Bug: 148966894 Test: Manual Change-Id: I881c240fcbce0c8148625c35e6e88ab02ea7122f --- .../java/android/bluetooth/BluetoothPbap.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 1f89ddf0afc..277a5a8f162 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -278,16 +278,19 @@ public class BluetoothPbap implements BluetoothProfile { } /** - * Pbap does not store connection policy, so this function only disconnects pbap if - * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}. + * Set connection policy of the profile and tries to disconnect it if connectionPolicy is + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} * *

          The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * Connection policy can be one of: + * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, + * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} * * @param device Paired bluetooth device - * @param connectionPolicy determines whether to disconnect the device - * @return true if pbap is successfully disconnected, false otherwise + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * * @hide */ @SystemApi -- GitLab From 8aa8b299fdcc73026325d62ab4535d8876857241 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 25 Feb 2020 11:33:43 -0800 Subject: [PATCH 1119/1408] Add missing RequiresPermission annotations in BluetoothHidHost and BluetoothMap APIs, disallow null device input for setConnectionPolicy, getConnectionPolicy, and getConnectionState in BluetoothHidHost, and BluetoothMap implements AutoCloseable, its close() method is public, and utilizes a CloseGuard. Bug: 149238030 Test: Manual Change-Id: I8add9e26afcaf1a988c15e3cc7f8c446491f0686 --- .../android/bluetooth/BluetoothHidHost.java | 18 ++++++++++---- .../java/android/bluetooth/BluetoothMap.java | 24 ++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 26e3e271bff..e9e1f686690 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -330,6 +329,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost service = getService(); @@ -370,8 +370,12 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { @@ -416,9 +420,12 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN @@ -465,8 +472,11 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 1c62faa97ee..cc2b6155a7e 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -27,6 +27,7 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.util.CloseGuard; import android.util.Log; import java.util.ArrayList; @@ -39,12 +40,14 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothMap implements BluetoothProfile { +public final class BluetoothMap implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothMap"; private static final boolean DBG = true; private static final boolean VDBG = false; + private CloseGuard mCloseGuard; + /** @hide */ @SuppressLint("ActionValue") @SystemApi @@ -86,15 +89,16 @@ public final class BluetoothMap implements BluetoothProfile { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); mAdapter = BluetoothAdapter.getDefaultAdapter(); mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); } - @SuppressLint("GenericException") - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); } + close(); } /** @@ -105,7 +109,10 @@ public final class BluetoothMap implements BluetoothProfile { * * @hide */ - public synchronized void close() { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -250,6 +257,7 @@ public final class BluetoothMap implements BluetoothProfile { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap service = getService(); -- GitLab From 190a48317d1a7d52460d45dc9d31ffe6f4e4855b Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 25 Feb 2020 22:28:00 +0000 Subject: [PATCH 1120/1408] Revert "Revert "Require user pass in a non-null BluetoothDevice ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert submission 10303287-revert-10253996-bt-a2dp-no-null-FQRXACWPIA Reason for revert: Fixing breakage Reverted Changes: I4d9f2f819:Revert "Make sure calls to BluetoothA2dp APIs pass... I771ca0d57:Revert "Need to now pass in active device instead ... I76529c7a1:Revert "Pass in active device to all BluetoothA2dp... I297bda68d:Revert "Require user pass in a non-null BluetoothD... I525327959:Revert "Pass in active device to all BluetoothA2dp... I1d8660b11:Revert "Pass in active device to all BluetoothA2dp... Bug: 147287141 Test: Manual Change-Id: I4d7d971af75bff8967fd807d34dad90c32e24eba --- .../java/android/bluetooth/BluetoothA2dp.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d8c653c6d0d..b672a0857cc 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,8 +643,9 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); + verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -670,9 +671,14 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@Nullable BluetoothDevice device, - @Nullable BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@NonNull BluetoothDevice device, + @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); + verifyDeviceNotNull(device, "setCodecConfigPreference"); + if (codecConfig == null) { + Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); + throw new IllegalArgumentException("codecConfig cannot be null"); + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -695,8 +701,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@Nullable BluetoothDevice device) { + public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -709,8 +716,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@Nullable BluetoothDevice device) { + public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -750,7 +758,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { + public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsSupported"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -775,7 +784,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { + public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -800,8 +810,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { + verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -854,6 +865,13 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From c78ccdc320d5902748edb97ba763490a4c1c84cd Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 24 Feb 2020 13:28:30 -0800 Subject: [PATCH 1121/1408] Update javadoc to reflect that BluetoothPbap#setConnectionPolicy returns true on successfully setting the connection policy instead of on disconnection. It also now indicates that if BluetoothProfile#CONNECTION_POLICY_FORBIDDEN is passed in, the profile will be disconnected. Bug: 148966894 Test: Manual Change-Id: I881c240fcbce0c8148625c35e6e88ab02ea7122f --- .../java/android/bluetooth/BluetoothPbap.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 1f89ddf0afc..277a5a8f162 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -278,16 +278,19 @@ public class BluetoothPbap implements BluetoothProfile { } /** - * Pbap does not store connection policy, so this function only disconnects pbap if - * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}. + * Set connection policy of the profile and tries to disconnect it if connectionPolicy is + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} * *

          The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * Connection policy can be one of: + * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, + * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} * * @param device Paired bluetooth device - * @param connectionPolicy determines whether to disconnect the device - * @return true if pbap is successfully disconnected, false otherwise + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * * @hide */ @SystemApi -- GitLab From 106cfbf961c79e17db03f591647c22a344dcd8b0 Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Tue, 3 Dec 2019 18:32:14 -0500 Subject: [PATCH 1122/1408] Sanitize Bluetooth device names without regex Saves a considerable amount of RAM. Fixes: 145623858 Test: Pair with a simulated device with whitespace in the name Change-Id: I1c8b00922534ee5941be6b14948c0941c5640862 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5b60b85f472..838a3b95da2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1025,7 +1025,11 @@ public final class BluetoothDevice implements Parcelable { try { String name = service.getRemoteName(this); if (name != null) { - return name.replaceAll("[\\t\\n\\r]+", " "); + // remove whitespace characters from the name + return name + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } return null; } catch (RemoteException e) { -- GitLab From d87b6b0457b2b31f682623c28f289404ed349281 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 25 Feb 2020 11:33:43 -0800 Subject: [PATCH 1123/1408] Add missing RequiresPermission annotations in BluetoothHidHost and BluetoothMap APIs, disallow null device input for setConnectionPolicy, getConnectionPolicy, and getConnectionState in BluetoothHidHost, and BluetoothMap implements AutoCloseable, its close() method is public, and utilizes a CloseGuard. Bug: 149238030 Test: Manual Merged-In: I8add9e26afcaf1a988c15e3cc7f8c446491f0686 Change-Id: I8add9e26afcaf1a988c15e3cc7f8c446491f0686 --- .../android/bluetooth/BluetoothHidHost.java | 18 ++++++++++---- .../java/android/bluetooth/BluetoothMap.java | 24 ++++++++++++------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 26e3e271bff..e9e1f686690 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -330,6 +329,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost service = getService(); @@ -370,8 +370,12 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { @@ -416,9 +420,12 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN @@ -465,8 +472,11 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 1c62faa97ee..cc2b6155a7e 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -27,6 +27,7 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.util.CloseGuard; import android.util.Log; import java.util.ArrayList; @@ -39,12 +40,14 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothMap implements BluetoothProfile { +public final class BluetoothMap implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothMap"; private static final boolean DBG = true; private static final boolean VDBG = false; + private CloseGuard mCloseGuard; + /** @hide */ @SuppressLint("ActionValue") @SystemApi @@ -86,15 +89,16 @@ public final class BluetoothMap implements BluetoothProfile { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); mAdapter = BluetoothAdapter.getDefaultAdapter(); mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); } - @SuppressLint("GenericException") - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); } + close(); } /** @@ -105,7 +109,10 @@ public final class BluetoothMap implements BluetoothProfile { * * @hide */ - public synchronized void close() { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -250,6 +257,7 @@ public final class BluetoothMap implements BluetoothProfile { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap service = getService(); -- GitLab From c7ac493cf64bfe12a9f9ed50183210d49635cb8b Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 2 Mar 2020 16:41:20 -0800 Subject: [PATCH 1124/1408] Update BluetoothDevice SystemApi permissions and disallow passing a null pin to BluetoothDevice#setPin Bug: 147428695 Test: Manual Change-Id: I74613d61420babecf5f1976fe33a2e1a729b71a1 --- .../android/bluetooth/BluetoothDevice.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3e1a480b4f6..6e5f914c359 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1118,7 +1118,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1209,7 +1209,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1247,13 +1247,12 @@ public final class BluetoothDevice implements Parcelable { /** * Cancel an in-progress bonding request started with {@link #createBond}. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess() { final IBluetooth service = sService; if (service == null) { @@ -1276,13 +1275,12 @@ public final class BluetoothDevice implements Parcelable { *

          Delete the link key associated with the remote device, and * immediately terminate connections to that device that require * authentication and encryption. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeBond() { final IBluetooth service = sService; if (service == null) { @@ -1355,13 +1353,12 @@ public final class BluetoothDevice implements Parcelable { /** * Returns whether there is an open connection to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return True if there is at least one open connection to this device. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isConnected() { final IBluetooth service = sService; if (service == null) { @@ -1379,13 +1376,12 @@ public final class BluetoothDevice implements Parcelable { /** * Returns whether there is an open connection to this device * that has been encrypted. - *

          Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return True if there is at least one encrypted connection to this device. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEncrypted() { final IBluetooth service = sService; if (service == null) { @@ -1538,7 +1534,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setPin(@Nullable String pin) { + public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); if (pinBytes == null) { return false; @@ -1574,6 +1570,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { @@ -1605,8 +1602,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getPhonebookAccessPermission() { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @AccessPermission int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { return ACCESS_UNKNOWN; @@ -1685,7 +1682,6 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the phonebook access is allowed to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link * #ACCESS_REJECTED}. @@ -1694,7 +1690,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setPhonebookAccessPermission(int value) { + public boolean setPhonebookAccessPermission(@AccessPermission int value) { final IBluetooth service = sService; if (service == null) { return false; @@ -1714,7 +1710,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1761,7 +1757,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { -- GitLab From 6a8b2392a0407704c3885cc299d0bc7267c887c7 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 2 Mar 2020 16:41:20 -0800 Subject: [PATCH 1125/1408] Update BluetoothDevice SystemApi permissions and disallow passing a null pin to BluetoothDevice#setPin Bug: 147428695 Test: Manual Merged-In: I74613d61420babecf5f1976fe33a2e1a729b71a1 Change-Id: I74613d61420babecf5f1976fe33a2e1a729b71a1 --- .../android/bluetooth/BluetoothDevice.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 838a3b95da2..4acc6a5ed51 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1121,7 +1121,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1212,7 +1212,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1250,13 +1250,12 @@ public final class BluetoothDevice implements Parcelable { /** * Cancel an in-progress bonding request started with {@link #createBond}. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess() { final IBluetooth service = sService; if (service == null) { @@ -1279,13 +1278,12 @@ public final class BluetoothDevice implements Parcelable { *

          Delete the link key associated with the remote device, and * immediately terminate connections to that device that require * authentication and encryption. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeBond() { final IBluetooth service = sService; if (service == null) { @@ -1329,13 +1327,12 @@ public final class BluetoothDevice implements Parcelable { /** * Returns whether there is an open connection to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return True if there is at least one open connection to this device. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isConnected() { final IBluetooth service = sService; if (service == null) { @@ -1353,13 +1350,12 @@ public final class BluetoothDevice implements Parcelable { /** * Returns whether there is an open connection to this device * that has been encrypted. - *

          Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return True if there is at least one encrypted connection to this device. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEncrypted() { final IBluetooth service = sService; if (service == null) { @@ -1512,7 +1508,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setPin(@Nullable String pin) { + public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); if (pinBytes == null) { return false; @@ -1548,6 +1544,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { @@ -1579,8 +1576,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getPhonebookAccessPermission() { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @AccessPermission int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { return ACCESS_UNKNOWN; @@ -1659,7 +1656,6 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the phonebook access is allowed to this device. - *

          Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link * #ACCESS_REJECTED}. @@ -1668,7 +1664,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setPhonebookAccessPermission(int value) { + public boolean setPhonebookAccessPermission(@AccessPermission int value) { final IBluetooth service = sService; if (service == null) { return false; @@ -1688,7 +1684,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1735,7 +1731,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { -- GitLab From 7b5846641ca271cfbb7dbd9b4d0bd568e00be170 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 25 Feb 2020 22:28:00 +0000 Subject: [PATCH 1126/1408] Revert "Revert "Require user pass in a non-null BluetoothDevice ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert submission 10303287-revert-10253996-bt-a2dp-no-null-FQRXACWPIA Reason for revert: Fixing breakage Reverted Changes: I4d9f2f819:Revert "Make sure calls to BluetoothA2dp APIs pass... I771ca0d57:Revert "Need to now pass in active device instead ... I76529c7a1:Revert "Pass in active device to all BluetoothA2dp... I297bda68d:Revert "Require user pass in a non-null BluetoothD... I525327959:Revert "Pass in active device to all BluetoothA2dp... I1d8660b11:Revert "Pass in active device to all BluetoothA2dp... Bug: 147287141 Test: Manual Merged-In: I4d7d971af75bff8967fd807d34dad90c32e24eba Change-Id: I4d7d971af75bff8967fd807d34dad90c32e24eba --- .../java/android/bluetooth/BluetoothA2dp.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d8c653c6d0d..b672a0857cc 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,8 +643,9 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); + verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -670,9 +671,14 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@Nullable BluetoothDevice device, - @Nullable BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@NonNull BluetoothDevice device, + @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); + verifyDeviceNotNull(device, "setCodecConfigPreference"); + if (codecConfig == null) { + Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); + throw new IllegalArgumentException("codecConfig cannot be null"); + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -695,8 +701,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@Nullable BluetoothDevice device) { + public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -709,8 +716,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@Nullable BluetoothDevice device) { + public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -750,7 +758,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { + public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsSupported"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -775,7 +784,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { + public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -800,8 +810,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { + verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -854,6 +865,13 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 18860a3fc5872dc7bb521c3c33763a296d7fbfc4 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 6 Mar 2020 18:04:15 -0800 Subject: [PATCH 1127/1408] Add error code for BluetoothDevice#getBatteryLevel to represent Bluetooth is off Bug: 147428695 Test: Manual Change-Id: I07c2fa49954632da6aa6a93706883e4cdfd32fa6 --- .../android/bluetooth/BluetoothDevice.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 838a3b95da2..57a9ae13765 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -229,6 +229,13 @@ public final class BluetoothDevice implements Parcelable { */ public static final int BATTERY_LEVEL_UNKNOWN = -1; + /** + * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off + * + * @hide + */ + public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100; + /** * Used as a Parcelable {@link BluetoothDevice} extra field in every intent * broadcast by this class. It contains the {@link BluetoothDevice} that @@ -1115,9 +1122,9 @@ public final class BluetoothDevice implements Parcelable { /** * Get the most recent identified battery level of this Bluetooth device * - * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if - * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting - * service, or return value is invalid + * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if + * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does + * not have any battery reporting service, or return value is invalid * @hide */ @SystemApi @@ -1126,7 +1133,7 @@ public final class BluetoothDevice implements Parcelable { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); - return BATTERY_LEVEL_UNKNOWN; + return BATTERY_LEVEL_BLUETOOTH_OFF; } try { return service.getBatteryLevel(this); @@ -1705,7 +1712,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the message access is allowed to this device. * - * @param value is the value we are setting the message access permission to + * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, + * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if + * the permission is not being granted. * @return Whether the value has been successfully set. * @hide */ @@ -1752,8 +1761,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the Sim access is allowed to this device. * - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link - * #ACCESS_REJECTED}. + * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, + * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if + * the permission is not being granted. * @return Whether the value has been successfully set. * @hide */ -- GitLab From e18a909013a8f521a0876ab5fd67c2ec8d151323 Mon Sep 17 00:00:00 2001 From: weichinweng Date: Thu, 5 Mar 2020 10:37:44 +0800 Subject: [PATCH 1128/1408] Fix bluetooth can't turn off during network reset (2/3) Remove disable Bluetooth action from AdapterService and move to BluetoothManagerService. Add factory reset reason into Bluetooth enable/disable reason list. Bug: 110181479 Test: manual Change-Id: I4bff3c3bb75fbb0d1e13c459c0d9d3fd3b8b3195 --- .../android/bluetooth/BluetoothAdapter.java | 7 +- .../bluetooth/BluetoothManagerService.java | 104 ++++++++++++------ 2 files changed, 74 insertions(+), 37 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 66bfcbd27ca..6ae68fcad6f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1229,10 +1229,11 @@ public final class BluetoothAdapter { public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null) { - return mService.factoryReset(); + if (mService != null && mService.factoryReset() + && mManagerService != null && mManagerService.onFactoryReset()) { + return true; } - Log.e(TAG, "factoryReset(): IBluetooth Service is null"); + Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index a4a42bcaecb..03ca1c610f8 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -85,6 +85,7 @@ import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -272,6 +273,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + public boolean onFactoryReset() { + // Wait for stable state if bluetooth is temporary state. + int state = getState(); + if (state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_OFF) { + if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) { + return false; + } + } + + // Clear registered LE apps to force shut-off Bluetooth + clearBleApps(); + state = getState(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) { + return false; + } + if (state == BluetoothAdapter.STATE_BLE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + return true; + } else if (state == BluetoothAdapter.STATE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.disable(); + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to shutdown Bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + return false; + } + public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { @@ -1670,7 +1711,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by "waitForOnOff(false, true)". + // is accomplished by + // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1680,7 +1722,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1693,10 +1735,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); mEnable = false; handleDisable(); - waitForOnOff(false, false); + waitForState(Set.of(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF)); } else { mEnable = false; handleDisable(); @@ -1819,9 +1866,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (!mEnable) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); handleDisable(); - waitForOnOff(false, false); + waitForState(Set.of(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF)); } break; } @@ -1853,7 +1905,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { == BluetoothAdapter.STATE_OFF)) { if (mEnable) { Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1982,7 +2034,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_TURNING_ON; } - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); if (mState == BluetoothAdapter.STATE_TURNING_ON) { bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); @@ -1997,7 +2049,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - boolean didDisableTimeout = !waitForOnOff(false, true); + boolean didDisableTimeout = + !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -2243,12 +2296,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** - * if on is true, wait for state become ON - * if off is true, wait for state become OFF - * if both on and off are false, wait for state not ON - */ - private boolean waitForOnOff(boolean on, boolean off) { + private boolean waitForState(Set states) { int i = 0; while (i < 10) { try { @@ -2256,18 +2304,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth == null) { break; } - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) { - return true; - } - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) { - return true; - } + if (states.contains(mBluetooth.getState())) { + return true; } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); @@ -2275,14 +2313,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - if (on || off) { - SystemClock.sleep(300); - } else { - SystemClock.sleep(50); - } + SystemClock.sleep(300); i++; } - Slog.e(TAG, "waitForOnOff time out"); + Slog.e(TAG, "waitForState " + states + " time out"); return false; } @@ -2343,7 +2377,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName(), false); handleDisable(); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); sendBluetoothServiceDownCallback(); @@ -2533,6 +2567,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "USER_SWITCH"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: + return "FACTORY_RESET"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From a5cc35166fda753ce16037a63500c735952852a0 Mon Sep 17 00:00:00 2001 From: weichinweng Date: Thu, 5 Mar 2020 10:37:44 +0800 Subject: [PATCH 1129/1408] Fix bluetooth can't turn off during network reset (2/3) Remove disable Bluetooth action from AdapterService and move to BluetoothManagerService. Add factory reset reason into Bluetooth enable/disable reason list. Bug: 110181479 Test: manual Change-Id: I4bff3c3bb75fbb0d1e13c459c0d9d3fd3b8b3195 Merged-In: I4bff3c3bb75fbb0d1e13c459c0d9d3fd3b8b3195 --- .../android/bluetooth/BluetoothAdapter.java | 6 +- .../bluetooth/BluetoothManagerService.java | 114 ++++++++++++------ 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 31bbd16497c..3b620b388ef 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1195,9 +1195,11 @@ public final class BluetoothAdapter { public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null) { - return mService.factoryReset(); + if (mService != null && mService.factoryReset() + && mManagerService != null && mManagerService.onFactoryReset()) { + return true; } + Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 188d6549486..f88032a40f6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -73,11 +73,14 @@ import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -257,6 +260,47 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + public boolean onFactoryReset() { + // Wait for stable state if bluetooth is temporary state. + int state = getState(); + if (state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_OFF) { + if (!waitForState(new HashSet(Arrays.asList(BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_ON)))) { + return false; + } + } + + // Clear registered LE apps to force shut-off Bluetooth + clearBleApps(); + state = getState(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) { + return false; + } + if (state == BluetoothAdapter.STATE_BLE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + return true; + } else if (state == BluetoothAdapter.STATE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.disable(); + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to shutdown Bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + return false; + } + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { @Override public void onChange(boolean unused) { @@ -1627,7 +1671,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by "waitForOnOff(false, true)". + // is accomplished by + // "waitForState(new HashSet( + // Arrays.asList(BluetoothAdapter.STATE_OFF)))". // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1637,7 +1683,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. On slower devices, that delay needs to be // on the order of (2 * SERVICE_RESTART_TIME_MS). // - waitForOnOff(false, true); + waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_OFF))); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); @@ -1650,10 +1697,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { - waitForOnOff(true, false); + waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_ON))); mEnable = false; handleDisable(); - waitForOnOff(false, false); + waitForState(new HashSet(Arrays.asList(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF))); } else { mEnable = false; handleDisable(); @@ -1782,9 +1835,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (!mEnable) { - waitForOnOff(true, false); + waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_ON))); handleDisable(); - waitForOnOff(false, false); + waitForState(new HashSet(Arrays.asList(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF))); } break; } @@ -1816,7 +1875,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { == BluetoothAdapter.STATE_OFF)) { if (mEnable) { Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - waitForOnOff(false, true); + waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_OFF))); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS); @@ -1939,7 +1999,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_TURNING_ON; } - waitForOnOff(true, false); + waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_ON))); if (mState == BluetoothAdapter.STATE_TURNING_ON) { bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); @@ -1954,7 +2015,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - boolean didDisableTimeout = !waitForOnOff(false, true); + boolean didDisableTimeout = + !waitForState(new HashSet(Arrays.asList( + BluetoothAdapter.STATE_OFF))); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -2206,12 +2269,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** - * if on is true, wait for state become ON - * if off is true, wait for state become OFF - * if both on and off are false, wait for state not ON - */ - private boolean waitForOnOff(boolean on, boolean off) { + private boolean waitForState(Set states) { int i = 0; while (i < 10) { try { @@ -2219,18 +2277,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth == null) { break; } - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) { - return true; - } - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) { - return true; - } + if (states.contains(mBluetooth.getState())) { + return true; } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); @@ -2238,14 +2286,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - if (on || off) { - SystemClock.sleep(300); - } else { - SystemClock.sleep(50); - } + SystemClock.sleep(300); i++; } - Slog.e(TAG, "waitForOnOff time out"); + Slog.e(TAG, "waitForState " + states + " time out"); return false; } @@ -2306,7 +2350,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName(), false); handleDisable(); - waitForOnOff(false, true); + waitForState(new HashSet(Arrays.asList(BluetoothAdapter.STATE_OFF))); sendBluetoothServiceDownCallback(); @@ -2468,6 +2512,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "USER_SWITCH"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: + return "FACTORY_RESET"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From 1ca15d954d4754b59d2108b7dbad3b71e707eca8 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 9 Mar 2020 16:58:34 -0700 Subject: [PATCH 1130/1408] Update permissions of BluetoothPan System APIs and re-hide some APIs that do not need to be System APIs. Bug: 146045934 Test: Manual Change-Id: Ic6325fde05294eb0266fee25f4b3e7098749a287 --- .../java/android/bluetooth/BluetoothPan.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ec63fd058b1..7af770e69ef 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -174,8 +174,9 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * Closes the connection to the service and unregisters callbacks + * + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) public void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); @@ -185,7 +186,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { return mProfileConnector.getService(); } - @RequiresPermission(Manifest.permission.BLUETOOTH) + /** @hide */ protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); @@ -280,7 +281,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -304,8 +305,11 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * {@inheritDoc} + * @hide */ + @SystemApi @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothPan service = getService(); @@ -344,9 +348,11 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * {@inheritDoc} + * @hide */ + @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); @@ -366,8 +372,10 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * Turns on/off bluetooth tethering * * @param value is whether to enable or disable bluetooth tethering + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -385,8 +393,10 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * Determines whether tethering is enabled * * @return true if tethering is on, false if not or some error occurred + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); -- GitLab From 5a02d0d7a63bfc93c7dbd13dfe95cc8c5c84abc9 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 5 Mar 2020 15:01:29 -0800 Subject: [PATCH 1131/1408] Rename featureId -> attributionTag In the core functionality this changes everything including aidl's and field names: - Context - ContentProvider - AppOps* - Package parsing For the rest, this is a shallow change to only change to the changed APIs. This keeps the change small-ish Exempt-From-Owner-Approval: Rename Fixes: 148792795 Test: TH Change-Id: I2a2245fe76e09e62cb13d5785d2efb4a304ba54a --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++---- framework/java/android/bluetooth/BluetoothManager.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6ae68fcad6f..608b563bfc7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -849,7 +849,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } return sBluetoothLeScanner; @@ -1663,11 +1663,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getFeatureId() { + private String getAttributionTag() { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { - return mContext.getFeatureId(); + return mContext.getAttributionTag(); } return null; } @@ -1709,7 +1709,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getFeatureId()); + return mService.startDiscovery(getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 7ff64663349..3b4fe0a30b8 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (context.getFeatureId() == null) { + if (context.getAttributionTag() == null) { context = context.getApplicationContext(); if (context == null) { throw new IllegalArgumentException( -- GitLab From a3b85b3c16c08a442997e3775156bd7abf797a3b Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 5 Mar 2020 15:01:29 -0800 Subject: [PATCH 1132/1408] Rename featureId -> attributionTag In the core functionality this changes everything including aidl's and field names: - Context - ContentProvider - AppOps* - Package parsing For the rest, this is a shallow change to only change to the changed APIs. This keeps the change small-ish Exempt-From-Owner-Approval: Rename Fixes: 148792795 Test: TH Change-Id: I2a2245fe76e09e62cb13d5785d2efb4a304ba54a Merged-In: I2a2245fe76e09e62cb13d5785d2efb4a304ba54a --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++---- framework/java/android/bluetooth/BluetoothManager.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6ae68fcad6f..608b563bfc7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -849,7 +849,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } return sBluetoothLeScanner; @@ -1663,11 +1663,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getFeatureId() { + private String getAttributionTag() { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { - return mContext.getFeatureId(); + return mContext.getAttributionTag(); } return null; } @@ -1709,7 +1709,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getFeatureId()); + return mService.startDiscovery(getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 7ff64663349..3b4fe0a30b8 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (context.getFeatureId() == null) { + if (context.getAttributionTag() == null) { context = context.getApplicationContext(); if (context == null) { throw new IllegalArgumentException( -- GitLab From f0d36b80b84aace92542ef0f059184b4a6309cf1 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 5 Mar 2020 15:01:29 -0800 Subject: [PATCH 1133/1408] Rename featureId -> attributionTag In the core functionality this changes everything including aidl's and field names: - Context - ContentProvider - AppOps* - Package parsing For the rest, this is a shallow change to only change to the changed APIs. This keeps the change small-ish Exempt-From-Owner-Approval: Rename Fixes: 148792795 Test: TH Change-Id: I2a2245fe76e09e62cb13d5785d2efb4a304ba54a Merged-In: I2a2245fe76e09e62cb13d5785d2efb4a304ba54a --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++---- framework/java/android/bluetooth/BluetoothManager.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 6ae68fcad6f..608b563bfc7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -849,7 +849,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getFeatureId()); + getAttributionTag()); } } return sBluetoothLeScanner; @@ -1663,11 +1663,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getFeatureId() { + private String getAttributionTag() { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { - return mContext.getFeatureId(); + return mContext.getAttributionTag(); } return null; } @@ -1709,7 +1709,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getFeatureId()); + return mService.startDiscovery(getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 7ff64663349..3b4fe0a30b8 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,7 +62,7 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (context.getFeatureId() == null) { + if (context.getAttributionTag() == null) { context = context.getApplicationContext(); if (context == null) { throw new IllegalArgumentException( -- GitLab From 7b97603ac022d44076667684ca405f20b65761dc Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 25 Feb 2020 22:28:00 +0000 Subject: [PATCH 1134/1408] Revert "Revert "Require user pass in a non-null BluetoothDevice ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert "Revert "Pass in active device to all BluetoothA2dp APIs ..." Revert submission 10303287-revert-10253996-bt-a2dp-no-null-FQRXACWPIA Reason for revert: Fixing breakage Reverted Changes: I4d9f2f819:Revert "Make sure calls to BluetoothA2dp APIs pass... I771ca0d57:Revert "Need to now pass in active device instead ... I76529c7a1:Revert "Pass in active device to all BluetoothA2dp... I297bda68d:Revert "Require user pass in a non-null BluetoothD... I525327959:Revert "Pass in active device to all BluetoothA2dp... I1d8660b11:Revert "Pass in active device to all BluetoothA2dp... Bug: 147287141 Test: Manual Merged-In: I91ee6878cac1b84bd289278a1b965658a26fe4db Merged-In: I4d7d971af75bff8967fd807d34dad90c32e24eba Change-Id: I4d7d971af75bff8967fd807d34dad90c32e24eba --- .../java/android/bluetooth/BluetoothA2dp.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d8c653c6d0d..b672a0857cc 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -643,8 +643,9 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) { + public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); + verifyDeviceNotNull(device, "getCodecStatus"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -670,9 +671,14 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setCodecConfigPreference(@Nullable BluetoothDevice device, - @Nullable BluetoothCodecConfig codecConfig) { + public void setCodecConfigPreference(@NonNull BluetoothDevice device, + @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); + verifyDeviceNotNull(device, "setCodecConfigPreference"); + if (codecConfig == null) { + Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); + throw new IllegalArgumentException("codecConfig cannot be null"); + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { @@ -695,8 +701,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void enableOptionalCodecs(@Nullable BluetoothDevice device) { + public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "enableOptionalCodecs"); enableDisableOptionalCodecs(device, true); } @@ -709,8 +716,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void disableOptionalCodecs(@Nullable BluetoothDevice device) { + public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); + verifyDeviceNotNull(device, "disableOptionalCodecs"); enableDisableOptionalCodecs(device, false); } @@ -750,7 +758,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus - public int supportsOptionalCodecs(@Nullable BluetoothDevice device) { + public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsSupported"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -775,7 +784,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus - public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) { + public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { + verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -800,8 +810,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device, + public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { + verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); try { if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED @@ -854,6 +865,13 @@ public final class BluetoothA2dp implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 7a9b7fc34bb5522180a4c773361f820c37524a8f Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 19 Mar 2020 11:44:54 -0700 Subject: [PATCH 1135/1408] BluetoothHearingAid#getHiSyncId now consistently uses the term HiSyncId (removed all references to CustomerId) and added link to explain what the HiSyncId represents Bug: 149238489 Test: Manual Change-Id: I4ff2a8d46f3fc5d06a29829a69a27a0c15e466f8 --- .../java/android/bluetooth/BluetoothHearingAid.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 38498bc147b..5891072ec7c 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -496,17 +496,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { } /** - * Get the CustomerId of the device. + * Get the HiSyncId (unique hearing aid device identifier) of the device. + * + * HiSyncId documentation + * can be found here * * @param device Bluetooth device - * @return the CustomerId of the device + * @return the HiSyncId of the device * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public long getHiSyncId(@Nullable BluetoothDevice device) { if (VDBG) { - log("getCustomerId(" + device + ")"); + log("getHiSyncId(" + device + ")"); } final IBluetoothHearingAid service = getService(); try { -- GitLab From e466ff5d119d7ad9913e98203a92adc2fe875af2 Mon Sep 17 00:00:00 2001 From: weichinweng Date: Thu, 5 Mar 2020 10:37:44 +0800 Subject: [PATCH 1136/1408] Fix bluetooth can't turn off during network reset (2/3) Remove disable Bluetooth action from AdapterService and move to BluetoothManagerService. Add factory reset reason into Bluetooth enable/disable reason list. Bug: 110181479 Test: manual Change-Id: I4bff3c3bb75fbb0d1e13c459c0d9d3fd3b8b3195 Merged-In: I4bff3c3bb75fbb0d1e13c459c0d9d3fd3b8b3195 (cherry picked from commit e18a909013a8f521a0876ab5fd67c2ec8d151323) --- .../android/bluetooth/BluetoothAdapter.java | 7 +- .../bluetooth/BluetoothManagerService.java | 104 ++++++++++++------ 2 files changed, 74 insertions(+), 37 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0a9dbb608b9..80f901bcced 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1219,10 +1219,11 @@ public final class BluetoothAdapter { public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null) { - return mService.factoryReset(); + if (mService != null && mService.factoryReset() + && mManagerService != null && mManagerService.onFactoryReset()) { + return true; } - Log.e(TAG, "factoryReset(): IBluetooth Service is null"); + Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index fa8eda54e53..b92f23573a0 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -79,6 +79,7 @@ import java.util.LinkedList; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -261,6 +262,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + public boolean onFactoryReset() { + // Wait for stable state if bluetooth is temporary state. + int state = getState(); + if (state == BluetoothAdapter.STATE_BLE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_ON + || state == BluetoothAdapter.STATE_TURNING_OFF) { + if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) { + return false; + } + } + + // Clear registered LE apps to force shut-off Bluetooth + clearBleApps(); + state = getState(); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth == null) { + return false; + } + if (state == BluetoothAdapter.STATE_BLE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + return true; + } else if (state == BluetoothAdapter.STATE_ON) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, + mContext.getPackageName(), false); + mBluetooth.disable(); + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to shutdown Bluetooth", e); + } finally { + mBluetoothLock.readLock().unlock(); + } + return false; + } + public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { @@ -1630,7 +1671,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by "waitForOnOff(false, true)". + // is accomplished by + // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1640,7 +1682,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1653,10 +1695,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); if (mEnable && mBluetooth != null) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); mEnable = false; handleDisable(); - waitForOnOff(false, false); + waitForState(Set.of(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF)); } else { mEnable = false; handleDisable(); @@ -1779,9 +1826,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (!mEnable) { - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); handleDisable(); - waitForOnOff(false, false); + waitForState(Set.of(BluetoothAdapter.STATE_OFF, + BluetoothAdapter.STATE_TURNING_ON, + BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_BLE_TURNING_ON, + BluetoothAdapter.STATE_BLE_ON, + BluetoothAdapter.STATE_BLE_TURNING_OFF)); } break; } @@ -1813,7 +1865,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { == BluetoothAdapter.STATE_OFF)) { if (mEnable) { Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); @@ -1942,7 +1994,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mState = BluetoothAdapter.STATE_TURNING_ON; } - waitForOnOff(true, false); + waitForState(Set.of(BluetoothAdapter.STATE_ON)); if (mState == BluetoothAdapter.STATE_TURNING_ON) { bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); @@ -1957,7 +2009,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - boolean didDisableTimeout = !waitForOnOff(false, true); + boolean didDisableTimeout = + !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -2203,12 +2256,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - /** - * if on is true, wait for state become ON - * if off is true, wait for state become OFF - * if both on and off are false, wait for state not ON - */ - private boolean waitForOnOff(boolean on, boolean off) { + private boolean waitForState(Set states) { int i = 0; while (i < 10) { try { @@ -2216,18 +2264,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth == null) { break; } - if (on) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - } else if (off) { - if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) { - return true; - } - } else { - if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) { - return true; - } + if (states.contains(mBluetooth.getState())) { + return true; } } catch (RemoteException e) { Slog.e(TAG, "getState()", e); @@ -2235,14 +2273,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } finally { mBluetoothLock.readLock().unlock(); } - if (on || off) { - SystemClock.sleep(300); - } else { - SystemClock.sleep(50); - } + SystemClock.sleep(300); i++; } - Slog.e(TAG, "waitForOnOff time out"); + Slog.e(TAG, "waitForState " + states + " time out"); return false; } @@ -2303,7 +2337,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName(), false); handleDisable(); - waitForOnOff(false, true); + waitForState(Set.of(BluetoothAdapter.STATE_OFF)); sendBluetoothServiceDownCallback(); @@ -2465,6 +2499,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "USER_SWITCH"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: return "RESTORE_USER_SETTING"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: + return "FACTORY_RESET"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From 4f6a1c2c8b9164f3c087b1ac4e1cb36f4f95941e Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 6 Mar 2020 18:04:15 -0800 Subject: [PATCH 1137/1408] Add error code for BluetoothDevice#getBatteryLevel to represent Bluetooth is off Bug: 147428695 Test: Manual Merged-In: I07c2fa49954632da6aa6a93706883e4cdfd32fa6 Change-Id: I07c2fa49954632da6aa6a93706883e4cdfd32fa6 --- .../android/bluetooth/BluetoothDevice.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6e5f914c359..eab7a039494 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -230,6 +230,13 @@ public final class BluetoothDevice implements Parcelable { */ public static final int BATTERY_LEVEL_UNKNOWN = -1; + /** + * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off + * + * @hide + */ + public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100; + /** * Used as a Parcelable {@link BluetoothDevice} extra field in every intent * broadcast by this class. It contains the {@link BluetoothDevice} that @@ -1112,9 +1119,9 @@ public final class BluetoothDevice implements Parcelable { /** * Get the most recent identified battery level of this Bluetooth device * - * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if - * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting - * service, or return value is invalid + * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if + * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does + * not have any battery reporting service, or return value is invalid * @hide */ @SystemApi @@ -1123,7 +1130,7 @@ public final class BluetoothDevice implements Parcelable { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); - return BATTERY_LEVEL_UNKNOWN; + return BATTERY_LEVEL_BLUETOOTH_OFF; } try { return service.getBatteryLevel(this); @@ -1727,7 +1734,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the message access is allowed to this device. * - * @param value is the value we are setting the message access permission to + * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, + * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if + * the permission is not being granted. * @return Whether the value has been successfully set. * @hide */ @@ -1774,8 +1783,9 @@ public final class BluetoothDevice implements Parcelable { /** * Sets whether the Sim access is allowed to this device. * - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link - * #ACCESS_REJECTED}. + * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, + * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if + * the permission is not being granted. * @return Whether the value has been successfully set. * @hide */ -- GitLab From 64df407ce9270cde316618be42bb43e9d4d9fae2 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 19 Mar 2020 11:44:54 -0700 Subject: [PATCH 1138/1408] BluetoothHearingAid#getHiSyncId now consistently uses the term HiSyncId (removed all references to CustomerId) and added link to explain what the HiSyncId represents Bug: 149238489 Test: Manual Merged-In: I4ff2a8d46f3fc5d06a29829a69a27a0c15e466f8 Change-Id: I4ff2a8d46f3fc5d06a29829a69a27a0c15e466f8 --- .../java/android/bluetooth/BluetoothHearingAid.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 38498bc147b..5891072ec7c 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -496,17 +496,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { } /** - * Get the CustomerId of the device. + * Get the HiSyncId (unique hearing aid device identifier) of the device. + * + * HiSyncId documentation + * can be found here * * @param device Bluetooth device - * @return the CustomerId of the device + * @return the HiSyncId of the device * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public long getHiSyncId(@Nullable BluetoothDevice device) { if (VDBG) { - log("getCustomerId(" + device + ")"); + log("getHiSyncId(" + device + ")"); } final IBluetoothHearingAid service = getService(); try { -- GitLab From 6462d007c2c6c7e59308494da4e93625322d5c57 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 9 Mar 2020 16:58:34 -0700 Subject: [PATCH 1139/1408] Update permissions of BluetoothPan System APIs and re-hide some APIs that do not need to be System APIs. Bug: 146045934 Test: Manual Merged-In: Ic6325fde05294eb0266fee25f4b3e7098749a287 Change-Id: Ic6325fde05294eb0266fee25f4b3e7098749a287 --- .../java/android/bluetooth/BluetoothPan.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ec63fd058b1..7af770e69ef 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -174,8 +174,9 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * Closes the connection to the service and unregisters callbacks + * + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) public void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); @@ -185,7 +186,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { return mProfileConnector.getService(); } - @RequiresPermission(Manifest.permission.BLUETOOTH) + /** @hide */ protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); @@ -280,7 +281,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -304,8 +305,11 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * {@inheritDoc} + * @hide */ + @SystemApi @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothPan service = getService(); @@ -344,9 +348,11 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** * {@inheritDoc} + * @hide */ + @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); @@ -366,8 +372,10 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * Turns on/off bluetooth tethering * * @param value is whether to enable or disable bluetooth tethering + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -385,8 +393,10 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * Determines whether tethering is enabled * * @return true if tethering is on, false if not or some error occurred + * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); -- GitLab From ce594994e8aa1b4bf9a6917e18c4f2f3b68c1ddc Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 18 Mar 2020 17:46:33 -0700 Subject: [PATCH 1140/1408] 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 Change-Id: I33ee2c7ccd3827db3d23d6447cf82d9ffc36836a --- .../java/android/bluetooth/BluetoothA2dp.java | 43 ++++++++++++------- .../android/bluetooth/BluetoothA2dpSink.java | 14 +++--- .../android/bluetooth/BluetoothAdapter.java | 15 +++---- .../bluetooth/BluetoothCodecConfig.java | 2 - .../bluetooth/BluetoothCodecStatus.java | 2 - .../android/bluetooth/BluetoothDevice.java | 32 +++++++------- .../android/bluetooth/BluetoothHeadset.java | 40 +++++++++++++---- .../bluetooth/BluetoothHeadsetClient.java | 13 ++++-- .../bluetooth/BluetoothHearingAid.java | 25 ++++------- .../android/bluetooth/BluetoothHidHost.java | 22 +++++----- .../java/android/bluetooth/BluetoothMap.java | 13 +++--- .../android/bluetooth/BluetoothMapClient.java | 19 +++++--- .../java/android/bluetooth/BluetoothPan.java | 23 ++-------- .../java/android/bluetooth/BluetoothPbap.java | 3 +- .../bluetooth/BluetoothPbapClient.java | 19 +++++--- .../java/android/bluetooth/BluetoothSap.java | 8 ++-- 16 files changed, 158 insertions(+), 135 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b672a0857cc..5374d6d55ee 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -462,7 +462,7 @@ public final class BluetoothA2dp 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -481,7 +481,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -517,7 +517,18 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } } /** @@ -532,7 +543,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); try { @@ -640,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -669,8 +680,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); @@ -699,8 +710,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "enableOptionalCodecs"); @@ -714,8 +725,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "disableOptionalCodecs"); @@ -755,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -781,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -808,8 +819,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index ab492309ba3..53f87e6bc05 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.Manifest; -import android.annotation.Nullable; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -120,7 +120,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -277,7 +277,7 @@ public final class BluetoothA2dpSink 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -297,7 +297,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink service = getService(); @@ -327,7 +327,7 @@ public final class BluetoothA2dpSink 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -346,7 +346,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -371,7 +371,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean isAudioPlaying(@Nullable BluetoothDevice device) { + public boolean isAudioPlaying(@NonNull BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 608b563bfc7..4aad3cbb211 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1224,7 +1224,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -1517,8 +1517,9 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + + "shows UI that confirms the user wants to go into discoverable mode.") + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setScanMode(@ScanMode int mode, long durationMillis) { if (getState() != STATE_ON) { return false; @@ -1566,8 +1567,8 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; @@ -1631,7 +1632,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); @@ -1883,7 +1884,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { @@ -1912,7 +1912,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 93e76fa5ba7..d2a15357aa1 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +33,6 @@ import java.util.Objects; * * {@hide} */ -@SystemApi public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index b6e77391da5..1e394b830d5 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Objects; * * {@hide} */ -@SystemApi public final class BluetoothCodecStatus implements Parcelable { /** * Extra for the codec configuration intents of the individual profiles. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index eab7a039494..594e5ffa77a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -36,8 +36,6 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; @@ -1100,8 +1098,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; if (service == null) { @@ -1124,8 +1122,8 @@ public final class BluetoothDevice implements Parcelable { * not have any battery reporting service, or return value is invalid * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1215,8 +1213,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1539,7 +1537,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -1576,8 +1574,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { @@ -1608,8 +1606,8 @@ public final class BluetoothDevice implements Parcelable { * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1716,8 +1714,8 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the message access is allowed to this device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1766,7 +1764,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -2021,7 +2019,7 @@ public final class BluetoothDevice implements Parcelable { * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ - @VisibleForTesting + @UnsupportedAppUsage public static byte[] convertPinToBytes(String pin) { if (pin == null) { return null; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 1ba2bb5e31a..6ce05f984cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -572,7 +572,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return service.setPriority( + device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** @@ -588,7 +603,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -624,7 +639,17 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; } /** @@ -639,7 +664,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; @@ -1126,16 +1151,13 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Get the connected device that is active. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the connected device that is active or null if no device * is active. * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index fbda9e9d6dd..85e0e08b19c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; @@ -442,6 +443,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device a remote device we want connect to * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. + * + * @hide */ @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { @@ -466,6 +469,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device a remote device we want disconnect * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. + * + * @hide */ @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { @@ -564,7 +569,7 @@ public final class BluetoothHeadsetClient 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -583,8 +588,8 @@ public final class BluetoothHeadsetClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadsetClient service = @@ -634,7 +639,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 5891072ec7c..e0674d75974 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -162,13 +162,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -202,13 +200,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -327,15 +323,12 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * Get the connected physical Hearing Aid devices that are active * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the list of active devices. The first element is the left active * device; the second element is the right active device. If either or both side * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); @@ -363,7 +356,7 @@ public final class BluetoothHearingAid 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -382,7 +375,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -414,7 +407,7 @@ public final class BluetoothHearingAid 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -432,7 +425,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -506,8 +499,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public long getHiSyncId(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public long getHiSyncId(@NonNull BluetoothDevice device) { if (VDBG) { log("getHiSyncId(" + device + ")"); } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index e9e1f686690..9561d938384 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -263,13 +263,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -303,13 +301,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -327,7 +323,10 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ + @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { @@ -368,7 +367,10 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ + @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull BluetoothDevice device) { @@ -400,7 +402,7 @@ public final class BluetoothHidHost 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -419,7 +421,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -453,7 +455,7 @@ public final class BluetoothHidHost 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -471,7 +473,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); if (device == null) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index cc2b6155a7e..14a71c44673 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -328,7 +327,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -347,8 +346,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); @@ -378,7 +377,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -396,8 +395,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 8d2aaddd38d..19240dc0bbc 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)) { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 7af770e69ef..a80f5b7f36d 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -30,7 +29,6 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.CloseGuard; import android.util.Log; import java.lang.annotation.Retention; @@ -51,11 +49,10 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothPan implements BluetoothProfile, AutoCloseable { +public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; private static final boolean VDBG = false; - private CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the Pan @@ -168,16 +165,13 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); } /** * Closes the connection to the service and unregisters callbacks - * - * @hide */ - public void close() { + @UnsupportedAppUsage + void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -188,9 +182,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** @hide */ protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } close(); } @@ -204,9 +195,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide @@ -245,9 +233,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide @@ -353,7 +338,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getConnectionState(@Nullable BluetoothDevice device) { + public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 277a5a8f162..d58a8935019 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; @@ -239,7 +238,7 @@ public class BluetoothPbap implements BluetoothProfile { @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) { + public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { log("getConnectionState: device=" + device); try { final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 9563c68ce65..d3452ffb458 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; @@ -101,7 +102,10 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @param device a remote device we want connect to * @return true if command has been issued successfully; false * otherwise; + * + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) { log("connect(" + device + ") for PBAP Client."); @@ -126,7 +130,10 @@ public final class BluetoothPbapClient 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("disconnect(" + device + ")" + new Exception()); @@ -251,7 +258,7 @@ public final class BluetoothPbapClient 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -270,8 +277,8 @@ public final class BluetoothPbapClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -305,7 +312,7 @@ public final class BluetoothPbapClient 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -323,8 +330,8 @@ public final class BluetoothPbapClient 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("getConnectionPolicy(" + device + ")"); } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index bfc3a4d25c2..6e0348158f4 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -310,7 +310,7 @@ public final class BluetoothSap 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -329,7 +329,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -360,7 +360,7 @@ public final class BluetoothSap 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -378,7 +378,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap service = getService(); -- GitLab From c228ce27e2d7fe26193236eb5d4299214deb17ec Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 18 Mar 2020 17:46:33 -0700 Subject: [PATCH 1141/1408] 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/BluetoothA2dp.java | 43 ++++++++++++------- .../android/bluetooth/BluetoothA2dpSink.java | 14 +++--- .../android/bluetooth/BluetoothAdapter.java | 15 +++---- .../bluetooth/BluetoothCodecConfig.java | 2 - .../bluetooth/BluetoothCodecStatus.java | 2 - .../android/bluetooth/BluetoothDevice.java | 32 +++++++------- .../android/bluetooth/BluetoothHeadset.java | 40 +++++++++++++---- .../bluetooth/BluetoothHeadsetClient.java | 13 ++++-- .../bluetooth/BluetoothHearingAid.java | 25 ++++------- .../android/bluetooth/BluetoothHidHost.java | 22 +++++----- .../java/android/bluetooth/BluetoothMap.java | 13 +++--- .../android/bluetooth/BluetoothMapClient.java | 19 +++++--- .../java/android/bluetooth/BluetoothPan.java | 23 ++-------- .../java/android/bluetooth/BluetoothPbap.java | 3 +- .../bluetooth/BluetoothPbapClient.java | 19 +++++--- .../java/android/bluetooth/BluetoothSap.java | 8 ++-- 16 files changed, 158 insertions(+), 135 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index b672a0857cc..5374d6d55ee 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -462,7 +462,7 @@ public final class BluetoothA2dp 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -481,7 +481,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -517,7 +517,18 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } } /** @@ -532,7 +543,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); try { @@ -640,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -669,8 +680,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); @@ -699,8 +710,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "enableOptionalCodecs"); @@ -714,8 +725,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "disableOptionalCodecs"); @@ -755,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -781,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -808,8 +819,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index ab492309ba3..53f87e6bc05 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,7 +17,7 @@ package android.bluetooth; import android.Manifest; -import android.annotation.Nullable; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -120,7 +120,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -277,7 +277,7 @@ public final class BluetoothA2dpSink 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -297,7 +297,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink service = getService(); @@ -327,7 +327,7 @@ public final class BluetoothA2dpSink 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -346,7 +346,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -371,7 +371,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean isAudioPlaying(@Nullable BluetoothDevice device) { + public boolean isAudioPlaying(@NonNull BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0a9dbb608b9..e6b90d87a59 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1214,7 +1214,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -1506,8 +1506,9 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + + "shows UI that confirms the user wants to go into discoverable mode.") + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setScanMode(@ScanMode int mode, long durationMillis) { if (getState() != STATE_ON) { return false; @@ -1555,8 +1556,8 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; @@ -1620,7 +1621,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); @@ -1872,7 +1873,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { @@ -1901,7 +1901,6 @@ public final class BluetoothAdapter { * * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 93e76fa5ba7..d2a15357aa1 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +33,6 @@ import java.util.Objects; * * {@hide} */ -@SystemApi public final class BluetoothCodecConfig implements Parcelable { // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index b6e77391da5..1e394b830d5 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Objects; * * {@hide} */ -@SystemApi public final class BluetoothCodecStatus implements Parcelable { /** * Extra for the codec configuration intents of the individual profiles. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 2a3f2be6422..a2cf7d9cc24 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -35,8 +35,6 @@ import android.os.Process; import android.os.RemoteException; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; - import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; @@ -1103,8 +1101,8 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; if (service == null) { @@ -1145,8 +1143,8 @@ public final class BluetoothDevice implements Parcelable { * not have any battery reporting service, or return value is invalid * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1236,8 +1234,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1531,7 +1529,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -1568,8 +1566,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { @@ -1600,8 +1598,8 @@ public final class BluetoothDevice implements Parcelable { * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1708,8 +1706,8 @@ public final class BluetoothDevice implements Parcelable { * @return Whether the message access is allowed to this device. * @hide */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1758,7 +1756,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(Manifest.permission.BLUETOOTH) public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -2013,7 +2011,7 @@ public final class BluetoothDevice implements Parcelable { * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. * @hide */ - @VisibleForTesting + @UnsupportedAppUsage public static byte[] convertPinToBytes(String pin) { if (pin == null) { return null; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 1ba2bb5e31a..6ce05f984cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -572,7 +572,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF + && priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return service.setPriority( + device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; } /** @@ -588,7 +603,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -624,7 +639,17 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; } /** @@ -639,7 +664,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; @@ -1126,16 +1151,13 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Get the connected device that is active. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the connected device that is active or null if no device * is active. * @hide */ - @SystemApi + @UnsupportedAppUsage @Nullable - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index fbda9e9d6dd..85e0e08b19c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; @@ -442,6 +443,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device a remote device we want connect to * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. + * + * @hide */ @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { @@ -466,6 +469,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device a remote device we want disconnect * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. + * + * @hide */ @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { @@ -564,7 +569,7 @@ public final class BluetoothHeadsetClient 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -583,8 +588,8 @@ public final class BluetoothHeadsetClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadsetClient service = @@ -634,7 +639,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 5891072ec7c..e0674d75974 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -162,13 +162,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -202,13 +200,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -327,15 +323,12 @@ public final class BluetoothHearingAid implements BluetoothProfile { /** * Get the connected physical Hearing Aid devices that are active * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} - * permission. - * * @return the list of active devices. The first element is the left active * device; the second element is the right active device. If either or both side * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @SystemApi + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); @@ -363,7 +356,7 @@ public final class BluetoothHearingAid 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -382,7 +375,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -414,7 +407,7 @@ public final class BluetoothHearingAid 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -432,7 +425,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -506,8 +499,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public long getHiSyncId(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public long getHiSyncId(@NonNull BluetoothDevice device) { if (VDBG) { log("getHiSyncId(" + device + ")"); } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index e9e1f686690..9561d938384 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -263,13 +263,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -303,13 +301,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -327,7 +323,10 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ + @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getConnectedDevices() { @@ -368,7 +367,10 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ + @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull BluetoothDevice device) { @@ -400,7 +402,7 @@ public final class BluetoothHidHost 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -419,7 +421,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -453,7 +455,7 @@ public final class BluetoothHidHost 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -471,7 +473,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); if (device == null) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index cc2b6155a7e..14a71c44673 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -328,7 +327,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -347,8 +346,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); @@ -378,7 +377,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -396,8 +395,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 8d2aaddd38d..19240dc0bbc 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)) { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 7af770e69ef..a80f5b7f36d 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -19,7 +19,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -30,7 +29,6 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.util.CloseGuard; import android.util.Log; import java.lang.annotation.Retention; @@ -51,11 +49,10 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothPan implements BluetoothProfile, AutoCloseable { +public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; private static final boolean VDBG = false; - private CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the Pan @@ -168,16 +165,13 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); } /** * Closes the connection to the service and unregisters callbacks - * - * @hide */ - public void close() { + @UnsupportedAppUsage + void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -188,9 +182,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { /** @hide */ protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } close(); } @@ -204,9 +195,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * the state. Users can get the connection state of the profile * from this intent. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide @@ -245,9 +233,6 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide @@ -353,7 +338,7 @@ public final class BluetoothPan implements BluetoothProfile, AutoCloseable { @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getConnectionState(@Nullable BluetoothDevice device) { + public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 277a5a8f162..d58a8935019 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; @@ -239,7 +238,7 @@ public class BluetoothPbap implements BluetoothProfile { @SystemApi @Override @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) { + public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { log("getConnectionState: device=" + device); try { final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 9563c68ce65..d3452ffb458 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; @@ -101,7 +102,10 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @param device a remote device we want connect to * @return true if command has been issued successfully; false * otherwise; + * + * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connect(BluetoothDevice device) { if (DBG) { log("connect(" + device + ") for PBAP Client."); @@ -126,7 +130,10 @@ public final class BluetoothPbapClient 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("disconnect(" + device + ")" + new Exception()); @@ -251,7 +258,7 @@ public final class BluetoothPbapClient 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -270,8 +277,8 @@ public final class BluetoothPbapClient 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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -305,7 +312,7 @@ public final class BluetoothPbapClient 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -323,8 +330,8 @@ public final class BluetoothPbapClient 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("getConnectionPolicy(" + device + ")"); } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index bfc3a4d25c2..6e0348158f4 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -310,7 +310,7 @@ public final class BluetoothSap 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -329,7 +329,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -360,7 +360,7 @@ public final class BluetoothSap 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -378,7 +378,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap service = getService(); -- GitLab From ef2b72fdcdace32df2640e30f67acd286759a803 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 19 Mar 2020 15:58:28 +0800 Subject: [PATCH 1142/1408] Sequentially handle enable and disable * Replace thread sleeps in MESSAGE_ENABLE and MESSAGE_DISABLE to delay messages. To make sure we do not block the state change process while waiting for a state change callback. * Prevent handling enable or disable at the same time by deferring the the request when we are enabling or disabling Bluetooth. Bug: 128569058 Test: Manual Change-Id: I2301ba22b6c10fcea71e3b66eea3ea0a0d7f0475 Merged-In: I2301ba22b6c10fcea71e3b66eea3ea0a0d7f0475 --- .../bluetooth/BluetoothManagerService.java | 127 ++++++++++++++++-- 1 file changed, 116 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index fa8eda54e53..11c1405d590 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -106,9 +106,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int USER_SWITCHED_TIME_MS = 200; // Delay for the addProxy function in msec private static final int ADD_PROXY_DELAY_MS = 100; + // Delay for retrying enable and disable in msec + private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; private static final int MESSAGE_REGISTER_ADAPTER = 20; private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; @@ -130,6 +134,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; + private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF = 0; @@ -160,6 +165,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; @@ -1597,8 +1604,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_ENABLE: + int quietEnable = msg.arg1; + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, + quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " + + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1621,7 +1638,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - mQuietEnable = (msg.arg1 == 1); + mQuietEnable = (quietEnable == 1); if (mBluetooth == null) { handleEnable(mQuietEnable); } else { @@ -1630,7 +1647,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by "waitForOnOff(false, true)". + // is accomplished by sending delay a message + // MESSAGE_HANDLE_ENABLE_DELAYED // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1640,28 +1658,108 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForOnOff(false, true); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + mWaitForEnableRetry = 0; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); } break; case MESSAGE_DISABLE: + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), + ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth + + ", mBinding = " + mBinding); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); + if (mEnable && mBluetooth != null) { - waitForOnOff(true, false); + mWaitForDisableRetry = 0; + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { mEnable = false; handleDisable(); - waitForOnOff(false, false); - } else { + } + break; + + case MESSAGE_HANDLE_ENABLE_DELAYED: { + // The Bluetooth is turning off, wait for STATE_OFF + if (mState != BluetoothAdapter.STATE_OFF) { + if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForEnableRetry++; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_OFF timeout"); + } + } + // Either state is changed to STATE_OFF or reaches the maximum retry, we + // should move forward to the next step. + mWaitForEnableRetry = 0; + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + Slog.d(TAG, "Handle enable is finished"); + break; + } + + case MESSAGE_HANDLE_DISABLE_DELAYED: { + boolean disabling = (msg.arg1 == 1); + Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); + if (!disabling) { + // The Bluetooth is turning on, wait for STATE_ON + if (mState != BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_ON timeout"); + } + } + // Either state is changed to STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + mWaitForDisableRetry = 0; mEnable = false; handleDisable(); + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { + // The Bluetooth is turning off, wait for exiting STATE_ON + if (mState == BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for exiting STATE_ON timeout"); + } + } + // Either state is exited from STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + Slog.d(TAG, "Handle disable is finished"); } break; + } case MESSAGE_RESTORE_USER_SETTING: if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { @@ -2031,6 +2129,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { + Slog.d(TAG, "binding Bluetooth service"); //Start bind timeout and bind Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); @@ -2418,6 +2517,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" " + app.getPackageName()); } + writer.println("\nBluetoothManagerService:"); + writer.println(" mEnable:" + mEnable); + writer.println(" mQuietEnable:" + mQuietEnable); + writer.println(" mEnableExternal:" + mEnableExternal); + writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); + writer.println(""); writer.flush(); if (args.length == 0) { -- GitLab From 828326ebe0e269c99991783ed9ac5a4248291410 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 19 Mar 2020 16:15:58 +0800 Subject: [PATCH 1143/1408] Sequentially handle enable and disable * Replace thread sleeps in MESSAGE_ENABLE and MESSAGE_DISABLE to delay messages. To make sure we do not block the state change process while waiting for a state change callback. * Prevent handling enable or disable at the same time by deferring the the request when we are enabling or disabling Bluetooth. Bug: 128569058 Test: Manual Change-Id: I2301ba22b6c10fcea71e3b66eea3ea0a0d7f0475 --- .../bluetooth/BluetoothManagerService.java | 133 +++++++++++++++--- 1 file changed, 116 insertions(+), 17 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 03ca1c610f8..1bf559a1702 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -112,9 +112,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int USER_SWITCHED_TIME_MS = 200; // Delay for the addProxy function in msec private static final int ADD_PROXY_DELAY_MS = 100; + // Delay for retrying enable and disable in msec + private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; private static final int MESSAGE_REGISTER_ADAPTER = 20; private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; @@ -136,6 +140,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; + private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF = 0; @@ -166,6 +171,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; @@ -1678,8 +1685,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_ENABLE: + int quietEnable = msg.arg1; + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, + quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " + + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1702,7 +1719,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - mQuietEnable = (msg.arg1 == 1); + mQuietEnable = (quietEnable == 1); if (mBluetooth == null) { handleEnable(mQuietEnable); } else { @@ -1711,8 +1728,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by - // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". + // is accomplished by sending delay a message + // MESSAGE_HANDLE_ENABLE_DELAYED // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1722,33 +1739,108 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + mWaitForEnableRetry = 0; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); } break; case MESSAGE_DISABLE: + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), + ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth + + ", mBinding = " + mBinding); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); + if (mEnable && mBluetooth != null) { - waitForState(Set.of(BluetoothAdapter.STATE_ON)); + mWaitForDisableRetry = 0; + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { mEnable = false; handleDisable(); - waitForState(Set.of(BluetoothAdapter.STATE_OFF, - BluetoothAdapter.STATE_TURNING_ON, - BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_BLE_TURNING_ON, - BluetoothAdapter.STATE_BLE_ON, - BluetoothAdapter.STATE_BLE_TURNING_OFF)); - } else { + } + break; + + case MESSAGE_HANDLE_ENABLE_DELAYED: { + // The Bluetooth is turning off, wait for STATE_OFF + if (mState != BluetoothAdapter.STATE_OFF) { + if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForEnableRetry++; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_OFF timeout"); + } + } + // Either state is changed to STATE_OFF or reaches the maximum retry, we + // should move forward to the next step. + mWaitForEnableRetry = 0; + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + Slog.d(TAG, "Handle enable is finished"); + break; + } + + case MESSAGE_HANDLE_DISABLE_DELAYED: { + boolean disabling = (msg.arg1 == 1); + Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); + if (!disabling) { + // The Bluetooth is turning on, wait for STATE_ON + if (mState != BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_ON timeout"); + } + } + // Either state is changed to STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + mWaitForDisableRetry = 0; mEnable = false; handleDisable(); + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { + // The Bluetooth is turning off, wait for exiting STATE_ON + if (mState == BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for exiting STATE_ON timeout"); + } + } + // Either state is exited from STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + Slog.d(TAG, "Handle disable is finished"); } break; + } case MESSAGE_RESTORE_USER_SETTING: if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { @@ -2124,6 +2216,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { + Slog.d(TAG, "binding Bluetooth service"); //Start bind timeout and bind Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); @@ -2493,6 +2586,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" " + app.getPackageName()); } + writer.println("\nBluetoothManagerService:"); + writer.println(" mEnable:" + mEnable); + writer.println(" mQuietEnable:" + mQuietEnable); + writer.println(" mEnableExternal:" + mEnableExternal); + writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); + writer.println(""); writer.flush(); if (args.length == 0) { -- GitLab From 7aed62feb84b376a364722ddc43a880b85f20b92 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 24 Mar 2020 17:22:15 -0700 Subject: [PATCH 1144/1408] BluetoothHearingAid System APIs now throw an exception if a null BluetoothDevice is passed in Bug: 149238489 Test: Manual Change-Id: I594f558bfe1e286bf74dd8dc3db4c8497fd0a025 --- .../java/android/bluetooth/BluetoothHearingAid.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index e0674d75974..fa62a02499e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -379,6 +379,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + verifyDeviceNotNull(device, "setConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -428,6 +429,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -504,6 +506,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getHiSyncId(" + device + ")"); } + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service == null) { @@ -577,6 +580,13 @@ public final class BluetoothHearingAid implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 78cb0998affedc80b0e4f1c83d58c5250eb07dd1 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 23 Mar 2020 13:48:33 +0800 Subject: [PATCH 1145/1408] Independent methods to enable/disable BLE mode * Use IBluetoothManager.enableBle() and disableBle() to toggle BLE only mode instead of updateBleAppCount() then enable(). * Fix BluetoothAdapter.disableBle sometime won't disable Bluetooth. * Add active log if Bluetooth is disabled via disableBle(). Bug: 139454316 Test: enableBle() -> disableBle() and check Bluetooth is OFF. Change-Id: I173d5fed1b47ff68a6504741e25754e65cbd1455 --- .../android/bluetooth/BluetoothAdapter.java | 37 +--- .../bluetooth/BluetoothManagerService.java | 168 +++++++++++------- 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0a9dbb608b9..d407ad1dbeb 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -916,23 +916,11 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - - int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { - String packageName = ActivityThread.currentPackageName(); - if (DBG) { - Log.d(TAG, "disableBLE(): de-registering " + packageName); - } - try { - mManagerService.updateBleAppCount(mToken, false, packageName); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - } - - if (DBG) { - Log.d(TAG, "disableBLE(): Already disabled"); + String packageName = ActivityThread.currentPackageName(); + try { + return mManagerService.disableBle(packageName, mToken); + } catch (RemoteException e) { + Log.e(TAG, "", e); } return false; } @@ -973,20 +961,9 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - + String packageName = ActivityThread.currentPackageName(); try { - String packageName = ActivityThread.currentPackageName(); - mManagerService.updateBleAppCount(mToken, true, packageName); - if (isLeEnabled()) { - if (DBG) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); - } - return true; - } - if (DBG) { - Log.d(TAG, "enableBLE(): Calling enable"); - } - return mManagerService.enable(packageName); + return mManagerService.enableBle(packageName, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index fa8eda54e53..cca02aac3d3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -740,13 +740,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public int updateBleAppCount(IBinder token, boolean enable, String packageName) { - // Check if packageName belongs to callingUid - final int callingUid = Binder.getCallingUid(); - final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { - checkPackage(callingUid, packageName); - } + private int updateBleAppCount(IBinder token, boolean enable, String packageName) { ClientDeathRecipient r = mBleApps.get(token); if (r == null && enable) { ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); @@ -771,13 +765,94 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, appCount + " registered Ble Apps"); } - if (appCount == 0 && mEnable) { - disableBleScanMode(); + return appCount; + } + + private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed"); + } + return false; } - if (appCount == 0 && !mEnableExternal) { - sendBrEdrDownCallback(); + // Check if packageName belongs to callingUid + final int callingUid = Binder.getCallingUid(); + final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!isCallerSystem) { + checkPackage(callingUid, packageName); + + if (requireForeground && !checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "Not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); } - return appCount; + return true; + } + + public boolean enableBle(String packageName, IBinder token) throws RemoteException { + if (!checkBluetoothPermissions(packageName, false)) { + if (DBG) { + Slog.d(TAG, "enableBle(): bluetooth disallowed"); + } + return false; + } + + if (DBG) { + Slog.d(TAG, "enableBle(" + packageName + "): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); + } + updateBleAppCount(token, true, packageName); + + if (mState == BluetoothAdapter.STATE_ON + || mState == BluetoothAdapter.STATE_BLE_ON + || mState == BluetoothAdapter.STATE_TURNING_ON + || mState == BluetoothAdapter.STATE_TURNING_OFF) { + Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + return true; + } + synchronized (mReceiver) { + // waive WRITE_SECURE_SETTINGS permission check + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + } + return true; + } + + public boolean disableBle(String packageName, IBinder token) throws RemoteException { + if (!checkBluetoothPermissions(packageName, false)) { + if (DBG) { + Slog.d(TAG, "disableBLE(): bluetooth disallowed"); + } + return false; + } + + if (DBG) { + Slog.d(TAG, "disableBle(" + packageName + "): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); + } + + if (mState == BluetoothAdapter.STATE_OFF) { + Slog.d(TAG, "disableBLE(): Already disabled"); + return false; + } + updateBleAppCount(token, false, packageName); + + if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) { + if (mEnable) { + disableBleScanMode(); + } + if (!mEnableExternal) { + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, false); + sendBrEdrDownCallback(); + } + } + return true; } // Clear all apps using BLE scan only mode. @@ -855,29 +930,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean enableNoAutoConnect(String packageName) { - if (isBluetoothDisallowed()) { + if (!checkBluetoothPermissions(packageName, false)) { if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); } return false; } - // Check if packageName belongs to callingUid - final int callingUid = Binder.getCallingUid(); - final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { - checkPackage(callingUid, packageName); - } - - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding); } - int callingAppId = UserHandle.getAppId(callingUid); + int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); if (callingAppId != Process.NFC_UID) { throw new SecurityException("no permission to enable Bluetooth quietly"); } @@ -892,32 +957,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean enable(String packageName) throws RemoteException { - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - - if (isBluetoothDisallowed()) { + if (!checkBluetoothPermissions(packageName, true)) { if (DBG) { Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); } return false; } - if (!callerSystem) { - // Check if packageName belongs to callingUid - checkPackage(callingUid, packageName); - - if (!checkIfCallerIsForegroundUser()) { - Slog.w(TAG, "enable(): not allowed for non-active and non system user"); - return false; - } - - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - - if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { - return false; - } + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!callerSystem && !isEnabled() && mWirelessConsentRequired + && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; } if (DBG) { @@ -939,25 +991,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean disable(String packageName, boolean persist) throws RemoteException { - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - - if (!callerSystem) { - // Check if packageName belongs to callingUid - checkPackage(callingUid, packageName); - - if (!checkIfCallerIsForegroundUser()) { - Slog.w(TAG, "disable(): not allowed for non-active and non system user"); - return false; + if (!checkBluetoothPermissions(packageName, true)) { + if (DBG) { + Slog.d(TAG, "disable(): not disabling - bluetooth disallowed"); } + return false; + } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - - if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { - return false; - } + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!callerSystem && isEnabled() && mWirelessConsentRequired + && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; } if (DBG) { -- GitLab From 8db43964e6e641d31a49f96a1bcdd102fa2f2b2d Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 24 Mar 2020 17:22:15 -0700 Subject: [PATCH 1146/1408] BluetoothHearingAid System APIs now throw an exception if a null BluetoothDevice is passed in Bug: 149238489 Test: Manual Merged-In: I594f558bfe1e286bf74dd8dc3db4c8497fd0a025 Change-Id: I594f558bfe1e286bf74dd8dc3db4c8497fd0a025 --- .../java/android/bluetooth/BluetoothHearingAid.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 5891072ec7c..c52b3d4028b 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -386,6 +386,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + verifyDeviceNotNull(device, "setConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -435,6 +436,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() @@ -511,6 +513,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getHiSyncId(" + device + ")"); } + verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); try { if (service == null) { @@ -584,6 +587,13 @@ public final class BluetoothHearingAid implements BluetoothProfile { return false; } + private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { + if (device == null) { + Log.e(TAG, methodName + ": device param is null"); + throw new IllegalArgumentException("Device cannot be null"); + } + } + private boolean isValidDevice(BluetoothDevice device) { if (device == null) return false; -- GitLab From 820d21e7ca4f9ad6a0b0632c449669cf9192af8c Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 19 Mar 2020 16:15:58 +0800 Subject: [PATCH 1147/1408] Sequentially handle enable and disable * Replace thread sleeps in MESSAGE_ENABLE and MESSAGE_DISABLE to delay messages. To make sure we do not block the state change process while waiting for a state change callback. * Prevent handling enable or disable at the same time by deferring the the request when we are enabling or disabling Bluetooth. Bug: 128569058 Test: Manual Change-Id: I2301ba22b6c10fcea71e3b66eea3ea0a0d7f0475 --- .../bluetooth/BluetoothManagerService.java | 133 +++++++++++++++--- 1 file changed, 116 insertions(+), 17 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b92f23573a0..15a59ced54c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -107,9 +107,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int USER_SWITCHED_TIME_MS = 200; // Delay for the addProxy function in msec private static final int ADD_PROXY_DELAY_MS = 100; + // Delay for retrying enable and disable in msec + private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; private static final int MESSAGE_REGISTER_ADAPTER = 20; private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; @@ -131,6 +135,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; + private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF = 0; @@ -161,6 +166,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; @@ -1638,8 +1645,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_ENABLE: + int quietEnable = msg.arg1; + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, + quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " + + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1662,7 +1679,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - mQuietEnable = (msg.arg1 == 1); + mQuietEnable = (quietEnable == 1); if (mBluetooth == null) { handleEnable(mQuietEnable); } else { @@ -1671,8 +1688,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by - // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". + // is accomplished by sending delay a message + // MESSAGE_HANDLE_ENABLE_DELAYED // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1682,33 +1699,108 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + mWaitForEnableRetry = 0; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); } break; case MESSAGE_DISABLE: + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), + ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth + + ", mBinding = " + mBinding); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); + if (mEnable && mBluetooth != null) { - waitForState(Set.of(BluetoothAdapter.STATE_ON)); + mWaitForDisableRetry = 0; + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { mEnable = false; handleDisable(); - waitForState(Set.of(BluetoothAdapter.STATE_OFF, - BluetoothAdapter.STATE_TURNING_ON, - BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_BLE_TURNING_ON, - BluetoothAdapter.STATE_BLE_ON, - BluetoothAdapter.STATE_BLE_TURNING_OFF)); - } else { + } + break; + + case MESSAGE_HANDLE_ENABLE_DELAYED: { + // The Bluetooth is turning off, wait for STATE_OFF + if (mState != BluetoothAdapter.STATE_OFF) { + if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForEnableRetry++; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_OFF timeout"); + } + } + // Either state is changed to STATE_OFF or reaches the maximum retry, we + // should move forward to the next step. + mWaitForEnableRetry = 0; + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + Slog.d(TAG, "Handle enable is finished"); + break; + } + + case MESSAGE_HANDLE_DISABLE_DELAYED: { + boolean disabling = (msg.arg1 == 1); + Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); + if (!disabling) { + // The Bluetooth is turning on, wait for STATE_ON + if (mState != BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_ON timeout"); + } + } + // Either state is changed to STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + mWaitForDisableRetry = 0; mEnable = false; handleDisable(); + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { + // The Bluetooth is turning off, wait for exiting STATE_ON + if (mState == BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for exiting STATE_ON timeout"); + } + } + // Either state is exited from STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + Slog.d(TAG, "Handle disable is finished"); } break; + } case MESSAGE_RESTORE_USER_SETTING: if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { @@ -2084,6 +2176,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { + Slog.d(TAG, "binding Bluetooth service"); //Start bind timeout and bind Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); @@ -2452,6 +2545,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" " + app.getPackageName()); } + writer.println("\nBluetoothManagerService:"); + writer.println(" mEnable:" + mEnable); + writer.println(" mQuietEnable:" + mQuietEnable); + writer.println(" mEnableExternal:" + mEnableExternal); + writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); + writer.println(""); writer.flush(); if (args.length == 0) { -- GitLab From 10b8502dc5fb36768d710564dede34460c0a65a3 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 23 Mar 2020 14:11:49 +0800 Subject: [PATCH 1148/1408] Fix Bluetooth auto turns ON in airplane mode Fix a race condition that mBleApps is cleared while turning ON BLE, this causes Bluetooth turns on classic profiles instead of stays in BLE_ON state. Bug: 142220937 Test: enableBle() -> turn on airplane mode, check Bluetooth does not turn to ON. Change-Id: I3cea5384f734dde269cf93a93ed7cc4173f669a0 --- .../android/server/bluetooth/BluetoothManagerService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index cca02aac3d3..fcb9af572e1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -881,6 +881,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } + if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) { + // Airplane mode is turned on while enabling BLE only mode, disable + // BLE now. + disableBleScanMode(); + sendBrEdrDownCallback(); + return; + } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { // This triggers transition to STATE_ON mBluetooth.onLeServiceUp(); -- GitLab From ea9e0298b346934247cfb1911299d824a9227d8e Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 23 Mar 2020 13:48:33 +0800 Subject: [PATCH 1149/1408] Independent methods to enable/disable BLE mode * Use IBluetoothManager.enableBle() and disableBle() to toggle BLE only mode instead of updateBleAppCount() then enable(). * Fix BluetoothAdapter.disableBle sometime won't disable Bluetooth. * Add active log if Bluetooth is disabled via disableBle(). Bug: 139454316 Test: enableBle() -> disableBle() and check Bluetooth is OFF. Change-Id: I173d5fed1b47ff68a6504741e25754e65cbd1455 --- .../android/bluetooth/BluetoothAdapter.java | 37 +--- .../bluetooth/BluetoothManagerService.java | 168 +++++++++++------- 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4aad3cbb211..f216db6fc71 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -917,23 +917,11 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - - int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { - String packageName = ActivityThread.currentPackageName(); - if (DBG) { - Log.d(TAG, "disableBLE(): de-registering " + packageName); - } - try { - mManagerService.updateBleAppCount(mToken, false, packageName); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return true; - } - - if (DBG) { - Log.d(TAG, "disableBLE(): Already disabled"); + String packageName = ActivityThread.currentPackageName(); + try { + return mManagerService.disableBle(packageName, mToken); + } catch (RemoteException e) { + Log.e(TAG, "", e); } return false; } @@ -974,20 +962,9 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - + String packageName = ActivityThread.currentPackageName(); try { - String packageName = ActivityThread.currentPackageName(); - mManagerService.updateBleAppCount(mToken, true, packageName); - if (isLeEnabled()) { - if (DBG) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); - } - return true; - } - if (DBG) { - Log.d(TAG, "enableBLE(): Calling enable"); - } - return mManagerService.enable(packageName); + return mManagerService.enableBle(packageName, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 1bf559a1702..d7b4f2e8f13 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -828,13 +828,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public int updateBleAppCount(IBinder token, boolean enable, String packageName) { - // Check if packageName belongs to callingUid - final int callingUid = Binder.getCallingUid(); - final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { - checkPackage(callingUid, packageName); - } + private int updateBleAppCount(IBinder token, boolean enable, String packageName) { ClientDeathRecipient r = mBleApps.get(token); if (r == null && enable) { ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); @@ -859,13 +853,94 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, appCount + " registered Ble Apps"); } - if (appCount == 0 && mEnable) { - disableBleScanMode(); + return appCount; + } + + private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) { + if (isBluetoothDisallowed()) { + if (DBG) { + Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed"); + } + return false; } - if (appCount == 0 && !mEnableExternal) { - sendBrEdrDownCallback(); + // Check if packageName belongs to callingUid + final int callingUid = Binder.getCallingUid(); + final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!isCallerSystem) { + checkPackage(callingUid, packageName); + + if (requireForeground && !checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "Not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); } - return appCount; + return true; + } + + public boolean enableBle(String packageName, IBinder token) throws RemoteException { + if (!checkBluetoothPermissions(packageName, false)) { + if (DBG) { + Slog.d(TAG, "enableBle(): bluetooth disallowed"); + } + return false; + } + + if (DBG) { + Slog.d(TAG, "enableBle(" + packageName + "): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); + } + updateBleAppCount(token, true, packageName); + + if (mState == BluetoothAdapter.STATE_ON + || mState == BluetoothAdapter.STATE_BLE_ON + || mState == BluetoothAdapter.STATE_TURNING_ON + || mState == BluetoothAdapter.STATE_TURNING_OFF) { + Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + return true; + } + synchronized (mReceiver) { + // waive WRITE_SECURE_SETTINGS permission check + sendEnableMsg(false, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + } + return true; + } + + public boolean disableBle(String packageName, IBinder token) throws RemoteException { + if (!checkBluetoothPermissions(packageName, false)) { + if (DBG) { + Slog.d(TAG, "disableBLE(): bluetooth disallowed"); + } + return false; + } + + if (DBG) { + Slog.d(TAG, "disableBle(" + packageName + "): mBluetooth =" + mBluetooth + + " mBinding = " + mBinding + " mState = " + + BluetoothAdapter.nameForState(mState)); + } + + if (mState == BluetoothAdapter.STATE_OFF) { + Slog.d(TAG, "disableBLE(): Already disabled"); + return false; + } + updateBleAppCount(token, false, packageName); + + if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) { + if (mEnable) { + disableBleScanMode(); + } + if (!mEnableExternal) { + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, false); + sendBrEdrDownCallback(); + } + } + return true; } // Clear all apps using BLE scan only mode. @@ -943,29 +1018,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean enableNoAutoConnect(String packageName) { - if (isBluetoothDisallowed()) { + if (!checkBluetoothPermissions(packageName, false)) { if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); } return false; } - // Check if packageName belongs to callingUid - final int callingUid = Binder.getCallingUid(); - final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { - checkPackage(callingUid, packageName); - } - - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding); } - int callingAppId = UserHandle.getAppId(callingUid); + int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); if (callingAppId != Process.NFC_UID) { throw new SecurityException("no permission to enable Bluetooth quietly"); } @@ -980,32 +1045,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean enable(String packageName) throws RemoteException { - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - - if (isBluetoothDisallowed()) { + if (!checkBluetoothPermissions(packageName, true)) { if (DBG) { Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); } return false; } - if (!callerSystem) { - // Check if packageName belongs to callingUid - checkPackage(callingUid, packageName); - - if (!checkIfCallerIsForegroundUser()) { - Slog.w(TAG, "enable(): not allowed for non-active and non system user"); - return false; - } - - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - - if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { - return false; - } + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!callerSystem && !isEnabled() && mWirelessConsentRequired + && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; } if (DBG) { @@ -1027,25 +1079,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public boolean disable(String packageName, boolean persist) throws RemoteException { - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - - if (!callerSystem) { - // Check if packageName belongs to callingUid - checkPackage(callingUid, packageName); - - if (!checkIfCallerIsForegroundUser()) { - Slog.w(TAG, "disable(): not allowed for non-active and non system user"); - return false; + if (!checkBluetoothPermissions(packageName, true)) { + if (DBG) { + Slog.d(TAG, "disable(): not disabling - bluetooth disallowed"); } + return false; + } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - - if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { - return false; - } + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + if (!callerSystem && isEnabled() && mWirelessConsentRequired + && startConsentUiIfNeeded(packageName, + callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; } if (DBG) { -- GitLab From 8823fdc4845476b88aea3ef2ea42dbbf57b75586 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 23 Mar 2020 14:11:49 +0800 Subject: [PATCH 1150/1408] Fix Bluetooth auto turns ON in airplane mode Fix a race condition that mBleApps is cleared while turning ON BLE, this causes Bluetooth turns on classic profiles instead of stays in BLE_ON state. Bug: 142220937 Test: enableBle() -> turn on airplane mode, check Bluetooth does not turn to ON. Change-Id: I3cea5384f734dde269cf93a93ed7cc4173f669a0 --- .../android/server/bluetooth/BluetoothManagerService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d7b4f2e8f13..192ea72224b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -969,6 +969,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } + if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) { + // Airplane mode is turned on while enabling BLE only mode, disable + // BLE now. + disableBleScanMode(); + sendBrEdrDownCallback(); + return; + } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { // This triggers transition to STATE_ON mBluetooth.onLeServiceUp(); -- GitLab From 96914087869e5c82dc88fd2b114854b1589ce276 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Fri, 3 Apr 2020 14:00:40 -0700 Subject: [PATCH 1151/1408] Fix exception handling in getState() binder cache Bug: 153103051 A binder cache query function cannot compute a result based on any data that is not known to the binder server. If the code depends on local data that can change, then invalidation will not work properly. The getState() method returns OFF if the bluetooth service is unavailable. This computation now occurs in the getState() method, outside of the binder cache query() method. The query method converts RemoteExceptions to RuntimeExceptions. Then, the conversion is reversed in getState(). This double conversion is needed because the cache query() method has no throw spec. Test: Run 'atest BluetoothInstrumentationTests' with a special debug image that enables binder cache VERIFY. The test found no cache inconsistencies. Change-Id: I80db86f66d8b51fa94207824c8b15972a9066ef5 --- .../android/bluetooth/BluetoothAdapter.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f216db6fc71..fc48e7f18f5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -979,17 +979,14 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override protected Integer recompute(Void query) { + // This function must be called while holding the + // mServiceLock, and with mService not null. The public + // getState() method makes this guarantee. try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getState(); - } + return mService.getState(); } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); + throw e.rethrowFromSystemServer(); } - return BluetoothAdapter.STATE_OFF; } }; @@ -1016,7 +1013,24 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = mBluetoothGetStateCache.query(null); + int state = BluetoothAdapter.STATE_OFF; + + try { + mServiceLock.readLock().lock(); + // The test for mService must either be outside the cache, or + // the cache must be invalidated when mService changes. + if (mService != null) { + state = mBluetoothGetStateCache.query(null); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "", e.getCause()); + } else { + throw e; + } + } finally { + mServiceLock.readLock().unlock(); + } // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON -- GitLab From 9d30d542c8c6240a3de0637cec05774c1d4fffe8 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 9 Mar 2020 15:42:46 +0800 Subject: [PATCH 1152/1408] Synchronize adapter registration with mCallbacks Move register/unregister callback to the caller thread. This fixes a problem that if an app registers adapter while the BluetoothManagerService is turning on Bluetooth, the app would get a null IBluetooth and would not receive any callback after Bluetooth is turned on. Bug: 149890859 Test: Manual Change-Id: Ia79f6bd1fd6ffab814a15053aeaae031faf875dd --- .../bluetooth/BluetoothManagerService.java | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 168c8cd713b..ebaebae6306 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -113,8 +113,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_DISABLE = 2; private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; - private static final int MESSAGE_REGISTER_ADAPTER = 20; - private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; @@ -590,10 +588,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Callback is null in registerAdapter"); return null; } - Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); - msg.obj = callback; - mHandler.sendMessage(msg); - + synchronized (mCallbacks) { + mCallbacks.register(callback); + } return mBluetooth; } @@ -603,9 +600,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return; } mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); - msg.obj = callback; - mHandler.sendMessage(msg); + synchronized (mCallbacks) { + mCallbacks.unregister(callback); + } } public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { @@ -1478,18 +1475,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is up */ private void sendBluetoothServiceUpCallback() { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); + synchronized (mCallbacks) { + try { + int n = mCallbacks.beginBroadcast(); + Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers."); + for (int i = 0; i < n; i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); + } } + } finally { + mCallbacks.finishBroadcast(); } - } finally { - mCallbacks.finishBroadcast(); } } @@ -1497,18 +1496,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is down */ private void sendBluetoothServiceDownCallback() { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); + synchronized (mCallbacks) { + try { + int n = mCallbacks.beginBroadcast(); + Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers."); + for (int i = 0; i < n; i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); + } } + } finally { + mCallbacks.finishBroadcast(); } - } finally { - mCallbacks.finishBroadcast(); } } @@ -1836,17 +1837,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName()); } break; - - case MESSAGE_REGISTER_ADAPTER: { - IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - mCallbacks.register(callback); - break; - } - case MESSAGE_UNREGISTER_ADAPTER: { - IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - mCallbacks.unregister(callback); - break; - } case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; -- GitLab From 96b659bc9d02c649f9780083040cf4eaf99c6b3a Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Mon, 9 Mar 2020 15:42:46 +0800 Subject: [PATCH 1153/1408] Synchronize adapter registration with mCallbacks Move register/unregister callback to the caller thread. This fixes a problem that if an app registers adapter while the BluetoothManagerService is turning on Bluetooth, the app would get a null IBluetooth and would not receive any callback after Bluetooth is turned on. Bug: 149890859 Test: Manual Change-Id: Ia79f6bd1fd6ffab814a15053aeaae031faf875dd Merged-In: Ia79f6bd1fd6ffab814a15053aeaae031faf875dd --- .../bluetooth/BluetoothManagerService.java | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 192ea72224b..3c0d880916e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -119,8 +119,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_DISABLE = 2; private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; - private static final int MESSAGE_REGISTER_ADAPTER = 20; - private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; @@ -642,10 +640,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Callback is null in registerAdapter"); return null; } - Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER); - msg.obj = callback; - mHandler.sendMessage(msg); - + synchronized (mCallbacks) { + mCallbacks.register(callback); + } return mBluetooth; } @@ -655,9 +652,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return; } mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER); - msg.obj = callback; - mHandler.sendMessage(msg); + synchronized (mCallbacks) { + mCallbacks.unregister(callback); + } } public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { @@ -1559,18 +1556,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is up */ private void sendBluetoothServiceUpCallback() { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); + synchronized (mCallbacks) { + try { + int n = mCallbacks.beginBroadcast(); + Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers."); + for (int i = 0; i < n; i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); + } } + } finally { + mCallbacks.finishBroadcast(); } - } finally { - mCallbacks.finishBroadcast(); } } @@ -1578,18 +1577,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that Adapter service is down */ private void sendBluetoothServiceDownCallback() { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); + synchronized (mCallbacks) { + try { + int n = mCallbacks.beginBroadcast(); + Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers."); + for (int i = 0; i < n; i++) { + try { + mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); + } } + } finally { + mCallbacks.finishBroadcast(); } - } finally { - mCallbacks.finishBroadcast(); } } @@ -1917,17 +1918,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.getPackageName()); } break; - - case MESSAGE_REGISTER_ADAPTER: { - IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - mCallbacks.register(callback); - break; - } - case MESSAGE_UNREGISTER_ADAPTER: { - IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj; - mCallbacks.unregister(callback); - break; - } case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: { IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj; -- GitLab From 46483817a8e84c9f1b0bf738366511c370bf5fa6 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Wed, 8 Apr 2020 21:02:22 +0000 Subject: [PATCH 1154/1408] Revert "Fix exception handling in getState() binder cache" This reverts commit 96914087869e5c82dc88fd2b114854b1589ce276. Bug: b/153505953 Change-Id: I58c46e534ccadf332d10fff8f99c85ad24340c27 --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index fc48e7f18f5..f216db6fc71 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -979,14 +979,17 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override protected Integer recompute(Void query) { - // This function must be called while holding the - // mServiceLock, and with mService not null. The public - // getState() method makes this guarantee. try { - return mService.getState(); + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getState(); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } + return BluetoothAdapter.STATE_OFF; } }; @@ -1013,24 +1016,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = BluetoothAdapter.STATE_OFF; - - try { - mServiceLock.readLock().lock(); - // The test for mService must either be outside the cache, or - // the cache must be invalidated when mService changes. - if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; - } - } finally { - mServiceLock.readLock().unlock(); - } + int state = mBluetoothGetStateCache.query(null); // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON -- GitLab From 966f25e6013218835625b3245c713747e9195f53 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Wed, 8 Apr 2020 21:02:22 +0000 Subject: [PATCH 1155/1408] Revert "Fix exception handling in getState() binder cache" This reverts commit 96914087869e5c82dc88fd2b114854b1589ce276. Bug: b/153505953 Change-Id: I58c46e534ccadf332d10fff8f99c85ad24340c27 (cherry picked from commit 2a8802d2ca8ca6f1902d2c40ffaacc11c40e9e44) --- .../android/bluetooth/BluetoothAdapter.java | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index fc48e7f18f5..f216db6fc71 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -979,14 +979,17 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override protected Integer recompute(Void query) { - // This function must be called while holding the - // mServiceLock, and with mService not null. The public - // getState() method makes this guarantee. try { - return mService.getState(); + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getState(); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } + return BluetoothAdapter.STATE_OFF; } }; @@ -1013,24 +1016,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = BluetoothAdapter.STATE_OFF; - - try { - mServiceLock.readLock().lock(); - // The test for mService must either be outside the cache, or - // the cache must be invalidated when mService changes. - if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; - } - } finally { - mServiceLock.readLock().unlock(); - } + int state = mBluetoothGetStateCache.query(null); // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON -- GitLab From 54846525c509a2d638e059095b12e64accc5b163 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Thu, 9 Apr 2020 09:00:46 -0700 Subject: [PATCH 1156/1408] Fix exception handling in getState() binder cache Bug: 153103051 A binder cache recompute() function cannot compute a result based on any data that is not known to the binder server. If the code depends on local data that can change, then invalidation will not work properly. The original getState() method returned OFF if the bluetooth service was unavailable. This computation now occurs in the getStateInternal() method, outside of the binder cache query() method. The recompute() method converts RemoteExceptions to RuntimeExceptions. Then, the conversion is reversed in getStateInternal(). This double conversion is needed because the cache recompute() method has no throw spec. Test: Create a debug image that enables binder cache VERIFY. Run the following tests: * atest BluetoothInstrumentationTests * atest PtsChreTestCases * atest UserLifecycleTests * manual testing connecting to bluetooth devices and toggling airplane mode. No cache inconsistencies found. No test failures seen. Change-Id: I93b9742587c4eb695d9a11fc6ab145f6a40a0ece --- .../android/bluetooth/BluetoothAdapter.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f216db6fc71..29a98faf5cd 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -980,16 +980,10 @@ public final class BluetoothAdapter { @Override protected Integer recompute(Void query) { try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getState(); - } + return mService.getState(); } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); + throw e.rethrowFromSystemServer(); } - return BluetoothAdapter.STATE_OFF; } }; @@ -1003,6 +997,30 @@ public final class BluetoothAdapter { PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY); } + /** + * Fetch the current bluetooth state. If the service is down, return + * OFF. + */ + @AdapterState + private int getStateInternal() { + int state = BluetoothAdapter.STATE_OFF; + try { + mServiceLock.readLock().lock(); + if (mService != null) { + state = mBluetoothGetStateCache.query(null); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "", e.getCause()); + } else { + throw e; + } + } finally { + mServiceLock.readLock().unlock(); + } + return state; + } + /** * Get the current state of the local Bluetooth adapter. *

          Possible return values are @@ -1016,7 +1034,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = mBluetoothGetStateCache.query(null); + int state = getStateInternal(); // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -1054,7 +1072,7 @@ public final class BluetoothAdapter { @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") public int getLeState() { - int state = mBluetoothGetStateCache.query(null); + int state = getStateInternal(); if (VDBG) { Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); -- GitLab From 8d54511731d897f4542fb50cace8e7cc4e743fe8 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 26 Mar 2020 11:50:35 -0700 Subject: [PATCH 1157/1408] Set attributionTag for noteOp(WRITE_SETTINGS) calls Test: atest FrameworksNetTests TetheringTests:TetheringServiceTest Bug: 136595429 Merged-In: I33f787644c44d7b0e5ce17a433820cfcd985cdfb Change-Id: Ic3d937e7bb5141798234ed5b2852c1f768e97495 --- framework/java/android/bluetooth/BluetoothPan.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index a80f5b7f36d..bfc28fae6e6 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -367,7 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value, pkgName); + service.setBluetoothTethering(value, pkgName, null); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } -- GitLab From 7bdbede874a860498b0ad1ee2611e0a33132c863 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 24 Mar 2020 15:57:49 -0700 Subject: [PATCH 1158/1408] Set attributionTag for noteOp(WRITE_SETTINGS) calls Test: atest FrameworksNetTests Bug: 136595429 Change-Id: I33f787644c44d7b0e5ce17a433820cfcd985cdfb Exempt-From-Owner-Approval: Merge from AOSP --- framework/java/android/bluetooth/BluetoothPan.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index a80f5b7f36d..73c38cbfbe3 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -367,7 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value, pkgName); + service.setBluetoothTethering(value, pkgName, mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } -- GitLab From 443a70307a38a08d29aab75858dd29bf97e8cedf Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 1 May 2020 15:00:00 -0700 Subject: [PATCH 1159/1408] BluetoothProfileConnecter now calls the ServiceListener's onServiceDisconnected method after unbinding the service Bug: 155120232 Test: atest BluetoothHostTest#testMapClose Change-Id: I324b4ea6654261eb67d5ec184f6b3456ba3d1aa4 --- .../bluetooth/BluetoothProfileConnector.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 863fd3698cb..040f58ae486 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -103,14 +103,21 @@ public abstract class BluetoothProfileConnector { private void doUnbind() { synchronized (mConnection) { - if (mService != null) { - logDebug("Unbinding service..."); - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException ie) { - logError("Unable to unbind service: " + ie); - } finally { - mService = null; + try { + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; + } + } + } finally { + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(mProfileId); + mServiceListener = null; } } } @@ -131,7 +138,6 @@ public abstract class BluetoothProfileConnector { } void disconnect() { - mServiceListener = null; IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); if (mgr != null) { try { -- GitLab From d0a2a9a2eb12f3edc97e30179978ebb24ea20791 Mon Sep 17 00:00:00 2001 From: Hungyen Weng Date: Mon, 4 May 2020 02:09:03 +0000 Subject: [PATCH 1160/1408] Revert "BluetoothProfileConnecter now calls the ServiceListener'..." Revert submission 11348591-btmap-close-gts Reason for revert: Droidcop reverted the change due to test failure, Test failed. https://android-build.googleplex.com/builds/tests/view?invocationId=I05500004181087103&testResultId=TR42507671126333727 Reverted Changes: I324b4ea66:BluetoothProfileConnecter now calls the ServiceLis... I1427f1a86:Add GTS test for BluetoothMap#close Bug: 155587865 Change-Id: I9a2645c7bbaec9fdf0c6f5972b08ba70cfe78741 --- .../bluetooth/BluetoothProfileConnector.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 040f58ae486..863fd3698cb 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -103,21 +103,14 @@ public abstract class BluetoothProfileConnector { private void doUnbind() { synchronized (mConnection) { - try { - if (mService != null) { - logDebug("Unbinding service..."); - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException ie) { - logError("Unable to unbind service: " + ie); - } finally { - mService = null; - } - } - } finally { - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(mProfileId); - mServiceListener = null; + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; } } } @@ -138,6 +131,7 @@ public abstract class BluetoothProfileConnector { } void disconnect() { + mServiceListener = null; IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); if (mgr != null) { try { -- GitLab From d2f62fbc09dc3439a97968864e6918108340858e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 13 May 2020 19:12:44 +0200 Subject: [PATCH 1161/1408] BluetoothLeScanner: clarify the documentation for permission requirements for startScan Bug: 145153879 Change-Id: I698952f67cfc97a33b703096cbba65129a91c220 --- .../android/bluetooth/le/BluetoothLeScanner.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 9a17346334d..2888fbd8a36 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -110,8 +110,9 @@ public final class BluetoothLeScanner { * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}. *

          - * An app must hold - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * An app must have + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission + * in order to get results. An App targeting Android Q or later must have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission * in order to get results. * @@ -129,8 +130,9 @@ public final class BluetoothLeScanner { * resumed when screen is turned on again. To avoid this, do filetered scanning by * using proper {@link ScanFilter}. *

          - * An app must hold - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * An app must have + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission + * in order to get results. An App targeting Android Q or later must have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission * in order to get results. * @@ -150,8 +152,9 @@ public final class BluetoothLeScanner { * the PendingIntent. Use this method of scanning if your process is not always running and it * should be started when scan results are available. *

          - * An app must hold - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or + * An app must have + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission + * in order to get results. An App targeting Android Q or later must have * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission * in order to get results. *

          -- GitLab From 4247e445baa9f02ad07885467701fdd1f6ff8a7f Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 26 May 2020 13:28:38 +0800 Subject: [PATCH 1162/1408] Disable Bluetooth if BLE was disabled while enabling * Call onBrEdrDown if BLE app count is 0 when entering BLE_ON state and the Bluetooth ON was triggered via enableBle(). This fixes an issue that Bluetooth automatically turns ON if gms calls enableBle() then disableBle() in a short time. * Fix a deadlock bug that acquires writeLock in a readLock. Bug: 153767389 Bug: 155950370 Test: Manual Change-Id: I4f92581465de81ffe9b45fb042b5cc43e5ab4136 --- .../server/bluetooth/BluetoothManagerService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 192ea72224b..249e376b525 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -957,7 +957,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on. + * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on, + * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off. */ private void continueFromBleOnState() { if (DBG) { @@ -969,11 +970,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } - if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) { - // Airplane mode is turned on while enabling BLE only mode, disable - // BLE now. - disableBleScanMode(); - sendBrEdrDownCallback(); + if (!mEnableExternal && !isBleAppPresent()) { + Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now"); + mEnable = false; + mBluetooth.onBrEdrDown(); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { -- GitLab From 6a60987f068879234001f777cdbb07dd7397954f Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Tue, 26 May 2020 13:28:38 +0800 Subject: [PATCH 1163/1408] Disable Bluetooth if BLE was disabled while enabling * Call onBrEdrDown if BLE app count is 0 when entering BLE_ON state and the Bluetooth ON was triggered via enableBle(). This fixes an issue that Bluetooth automatically turns ON if gms calls enableBle() then disableBle() in a short time. * Fix a deadlock bug that acquires writeLock in a readLock. Bug: 153767389 Bug: 155950370 Test: Manual Change-Id: I4f92581465de81ffe9b45fb042b5cc43e5ab4136 Merged-In: I4f92581465de81ffe9b45fb042b5cc43e5ab4136 --- .../server/bluetooth/BluetoothManagerService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index ebaebae6306..2297d4b7c61 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -873,7 +873,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } /** - * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on. + * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on, + * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off. */ private void continueFromBleOnState() { if (DBG) { @@ -885,11 +886,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); return; } - if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) { - // Airplane mode is turned on while enabling BLE only mode, disable - // BLE now. - disableBleScanMode(); - sendBrEdrDownCallback(); + if (!mEnableExternal && !isBleAppPresent()) { + Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now"); + mEnable = false; + mBluetooth.onBrEdrDown(); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { -- GitLab From a33fbc77255e669560f382879de3b4c779624673 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Fri, 22 May 2020 11:22:45 -0700 Subject: [PATCH 1164/1408] Restart BT when init flags change Restarts immediately, a future patch will ensure we restart at a non-interruptive time. Bug: 156757711 Test: atest BluetoothInstrumentationTests Tag: #feature Change-Id: I8be87c4132cadb39342bee10188db672029ae150 --- .../bluetooth/BluetoothManagerService.java | 186 +++++++++++------- 1 file changed, 112 insertions(+), 74 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0d4efed25da..8f17bcd23b0 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -63,6 +63,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; +import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -114,6 +115,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ADD_PROXY_DELAY_MS = 100; // Delay for retrying enable and disable in msec private static final int ENABLE_DISABLE_DELAY_MS = 300; + private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -133,6 +135,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; private static final int MESSAGE_RESTORE_USER_SETTING = 500; + private static final int MESSAGE_INIT_FLAGS_CHANGED = 600; private static final int RESTORE_SETTING_TO_ON = 1; private static final int RESTORE_SETTING_TO_OFF = 0; @@ -278,6 +281,23 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final DeviceConfig.OnPropertyChangedListener mDeviceConfigChangedListener = + new DeviceConfig.OnPropertyChangedListener() { + @Override + public void onPropertyChanged(String namespace, String name, String value) { + if (!namespace.equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { + return; + } + if (!name.startsWith("INIT_")) { + return; + } + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); + } + }; + public boolean onFactoryReset() { // Wait for stable state if bluetooth is temporary state. int state = getState(); @@ -507,6 +527,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; + DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.NAMESPACE_BLUETOOTH, + (Runnable r) -> r.run(), + mDeviceConfigChangedListener); } /** @@ -2148,80 +2172,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /* disable and enable BT when detect a user switch */ if (mBluetooth != null && isEnabled()) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.unregisterCallback(mBluetoothCallback); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister", re); - } finally { - mBluetoothLock.readLock().unlock(); - } - - if (mState == BluetoothAdapter.STATE_TURNING_OFF) { - // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); - mState = BluetoothAdapter.STATE_OFF; - } - if (mState == BluetoothAdapter.STATE_OFF) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); - mState = BluetoothAdapter.STATE_TURNING_ON; - } - - waitForState(Set.of(BluetoothAdapter.STATE_ON)); - - if (mState == BluetoothAdapter.STATE_TURNING_ON) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); - } - - unbindAllBluetoothProfileServices(); - // disable - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, - mContext.getPackageName(), false); - handleDisable(); - // Pbap service need receive STATE_TURNING_OFF intent to close - bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); - - boolean didDisableTimeout = - !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - - bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); - sendBluetoothServiceDownCallback(); - - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - - // - // If disabling Bluetooth times out, wait for an - // additional amount of time to ensure the process is - // shut down completely before attempting to restart. - // - if (didDisableTimeout) { - SystemClock.sleep(3000); - } else { - SystemClock.sleep(100); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - // enable - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, - mContext.getPackageName(), true); - // mEnable flag could have been reset on disableBLE. Reenable it. - mEnable = true; - handleEnable(mQuietEnable); + restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; @@ -2248,8 +2199,93 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } handleEnable(mQuietEnable); } + break; + } + case MESSAGE_INIT_FLAGS_CHANGED: { + if (DBG) { + Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); + } + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + if (mBluetooth != null && isEnabled()) { + restartForReason( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); + } + break; + } + } + } + + private void restartForReason(int reason) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.unregisterCallback(mBluetoothCallback); } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); } + + if (mState == BluetoothAdapter.STATE_TURNING_OFF) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); + mState = BluetoothAdapter.STATE_OFF; + } + if (mState == BluetoothAdapter.STATE_OFF) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); + mState = BluetoothAdapter.STATE_TURNING_ON; + } + + waitForOnOff(true, false); + + if (mState == BluetoothAdapter.STATE_TURNING_ON) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); + } + + unbindAllBluetoothProfileServices(); + // disable + addActiveLog(reason, mContext.getPackageName(), false); + handleDisable(); + // Pbap service need receive STATE_TURNING_OFF intent to close + bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, + BluetoothAdapter.STATE_TURNING_OFF); + + boolean didDisableTimeout = !waitForOnOff(false, true); + + bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_OFF); + sendBluetoothServiceDownCallback(); + + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); + } + + // + // If disabling Bluetooth times out, wait for an + // additional amount of time to ensure the process is + // shut down completely before attempting to restart. + // + if (didDisableTimeout) { + SystemClock.sleep(3000); + } else { + SystemClock.sleep(100); + } + + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mState = BluetoothAdapter.STATE_OFF; + // enable + addActiveLog(reason, mContext.getPackageName(), true); + // mEnable flag could have been reset on disableBLE. Reenable it. + mEnable = true; + handleEnable(mQuietEnable); } } @@ -2711,6 +2747,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "RESTORE_USER_SETTING"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: return "FACTORY_RESET"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED: + return "INIT_FLAGS_CHANGED"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From 82069a89cc00378c1bb70fb4790acdab68c3a072 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 4 Jun 2020 03:43:19 +0000 Subject: [PATCH 1165/1408] Revert "Restart BT when init flags change" This reverts commit a33fbc77255e669560f382879de3b4c779624673. Reason for revert: For checking Broken build 6557738 on git_master on aosp_blueline-userdebug Bug: 158144238 Exempt-From-Owner-Approval: fix broken build Change-Id: I9b860351a22cbac47c0437cb1c4f8375cf258d8e --- .../bluetooth/BluetoothManagerService.java | 186 +++++++----------- 1 file changed, 74 insertions(+), 112 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8f17bcd23b0..0d4efed25da 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -63,7 +63,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; -import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -115,7 +114,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ADD_PROXY_DELAY_MS = 100; // Delay for retrying enable and disable in msec private static final int ENABLE_DISABLE_DELAY_MS = 300; - private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -135,7 +133,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; private static final int MESSAGE_RESTORE_USER_SETTING = 500; - private static final int MESSAGE_INIT_FLAGS_CHANGED = 600; private static final int RESTORE_SETTING_TO_ON = 1; private static final int RESTORE_SETTING_TO_OFF = 0; @@ -281,23 +278,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final DeviceConfig.OnPropertyChangedListener mDeviceConfigChangedListener = - new DeviceConfig.OnPropertyChangedListener() { - @Override - public void onPropertyChanged(String namespace, String name, String value) { - if (!namespace.equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { - return; - } - if (!name.startsWith("INIT_")) { - return; - } - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); - } - }; - public boolean onFactoryReset() { // Wait for stable state if bluetooth is temporary state. int state = getState(); @@ -527,10 +507,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; - DeviceConfig.addOnPropertyChangedListener( - DeviceConfig.NAMESPACE_BLUETOOTH, - (Runnable r) -> r.run(), - mDeviceConfigChangedListener); } /** @@ -2172,7 +2148,80 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /* disable and enable BT when detect a user switch */ if (mBluetooth != null && isEnabled()) { - restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH); + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.unregisterCallback(mBluetoothCallback); + } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); + } + + if (mState == BluetoothAdapter.STATE_TURNING_OFF) { + // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); + mState = BluetoothAdapter.STATE_OFF; + } + if (mState == BluetoothAdapter.STATE_OFF) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); + mState = BluetoothAdapter.STATE_TURNING_ON; + } + + waitForState(Set.of(BluetoothAdapter.STATE_ON)); + + if (mState == BluetoothAdapter.STATE_TURNING_ON) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); + } + + unbindAllBluetoothProfileServices(); + // disable + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), false); + handleDisable(); + // Pbap service need receive STATE_TURNING_OFF intent to close + bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, + BluetoothAdapter.STATE_TURNING_OFF); + + boolean didDisableTimeout = + !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); + + bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_OFF); + sendBluetoothServiceDownCallback(); + + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); + } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); + } + + // + // If disabling Bluetooth times out, wait for an + // additional amount of time to ensure the process is + // shut down completely before attempting to restart. + // + if (didDisableTimeout) { + SystemClock.sleep(3000); + } else { + SystemClock.sleep(100); + } + + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mState = BluetoothAdapter.STATE_OFF; + // enable + addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, + mContext.getPackageName(), true); + // mEnable flag could have been reset on disableBLE. Reenable it. + mEnable = true; + handleEnable(mQuietEnable); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; @@ -2199,93 +2248,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } handleEnable(mQuietEnable); } - break; - } - case MESSAGE_INIT_FLAGS_CHANGED: { - if (DBG) { - Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); - } - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - if (mBluetooth != null && isEnabled()) { - restartForReason( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); - } - break; - } - } - } - - private void restartForReason(int reason) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.unregisterCallback(mBluetoothCallback); } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister", re); - } finally { - mBluetoothLock.readLock().unlock(); } - - if (mState == BluetoothAdapter.STATE_TURNING_OFF) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); - mState = BluetoothAdapter.STATE_OFF; - } - if (mState == BluetoothAdapter.STATE_OFF) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); - mState = BluetoothAdapter.STATE_TURNING_ON; - } - - waitForOnOff(true, false); - - if (mState == BluetoothAdapter.STATE_TURNING_ON) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); - } - - unbindAllBluetoothProfileServices(); - // disable - addActiveLog(reason, mContext.getPackageName(), false); - handleDisable(); - // Pbap service need receive STATE_TURNING_OFF intent to close - bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); - - boolean didDisableTimeout = !waitForOnOff(false, true); - - bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); - sendBluetoothServiceDownCallback(); - - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - - // - // If disabling Bluetooth times out, wait for an - // additional amount of time to ensure the process is - // shut down completely before attempting to restart. - // - if (didDisableTimeout) { - SystemClock.sleep(3000); - } else { - SystemClock.sleep(100); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - // enable - addActiveLog(reason, mContext.getPackageName(), true); - // mEnable flag could have been reset on disableBLE. Reenable it. - mEnable = true; - handleEnable(mQuietEnable); } } @@ -2747,8 +2711,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "RESTORE_USER_SETTING"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: return "FACTORY_RESET"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED: - return "INIT_FLAGS_CHANGED"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From 49c70e6f1264ac5e7587bd4769eb89099dadce01 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Fri, 22 May 2020 11:22:45 -0700 Subject: [PATCH 1166/1408] Restart BT when init flags change Restarts immediately, a future patch will ensure we restart at a non-interruptive time. Bug: 156757711 Test: atest BluetoothInstrumentationTests Tag: #feature Change-Id: I8aad97b8c21f28d4a18ae597f3838de9caee150a --- .../bluetooth/BluetoothManagerService.java | 195 +++++++++++------- 1 file changed, 121 insertions(+), 74 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8f076332c02..151f4d1e0d3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -59,6 +59,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; +import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -109,6 +110,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int ADD_PROXY_DELAY_MS = 100; // Delay for retrying enable and disable in msec private static final int ENABLE_DISABLE_DELAY_MS = 300; + private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -128,6 +130,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int MESSAGE_ADD_PROXY_DELAYED = 400; private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; private static final int MESSAGE_RESTORE_USER_SETTING = 500; + private static final int MESSAGE_INIT_FLAGS_CHANGED = 600; private static final int RESTORE_SETTING_TO_ON = 1; private static final int RESTORE_SETTING_TO_OFF = 0; @@ -267,6 +270,30 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; + private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { + return; + } + boolean foundInit = false; + for (String name : properties.getKeyset()) { + if (name.startsWith("INIT_")) { + foundInit = true; + break; + } + } + if (!foundInit) { + return; + } + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); + } + }; + public boolean onFactoryReset() { // Wait for stable state if bluetooth is temporary state. int state = getState(); @@ -496,6 +523,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } mSystemUiUid = systemUiUid; + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_BLUETOOTH, + (Runnable r) -> r.run(), + mDeviceConfigChangedListener); } /** @@ -2108,80 +2139,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /* disable and enable BT when detect a user switch */ if (mBluetooth != null && isEnabled()) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.unregisterCallback(mBluetoothCallback); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister", re); - } finally { - mBluetoothLock.readLock().unlock(); - } - - if (mState == BluetoothAdapter.STATE_TURNING_OFF) { - // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); - mState = BluetoothAdapter.STATE_OFF; - } - if (mState == BluetoothAdapter.STATE_OFF) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); - mState = BluetoothAdapter.STATE_TURNING_ON; - } - - waitForState(Set.of(BluetoothAdapter.STATE_ON)); - - if (mState == BluetoothAdapter.STATE_TURNING_ON) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); - } - - unbindAllBluetoothProfileServices(); - // disable - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, - mContext.getPackageName(), false); - handleDisable(); - // Pbap service need receive STATE_TURNING_OFF intent to close - bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); - - boolean didDisableTimeout = - !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - - bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); - sendBluetoothServiceDownCallback(); - - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - - // - // If disabling Bluetooth times out, wait for an - // additional amount of time to ensure the process is - // shut down completely before attempting to restart. - // - if (didDisableTimeout) { - SystemClock.sleep(3000); - } else { - SystemClock.sleep(100); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - // enable - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH, - mContext.getPackageName(), true); - // mEnable flag could have been reset on disableBLE. Reenable it. - mEnable = true; - handleEnable(mQuietEnable); + restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH); } else if (mBinding || mBluetooth != null) { Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); userMsg.arg2 = 1 + msg.arg2; @@ -2208,8 +2166,95 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } handleEnable(mQuietEnable); } + break; + } + case MESSAGE_INIT_FLAGS_CHANGED: { + if (DBG) { + Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); + } + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + if (mBluetooth != null && isEnabled()) { + restartForReason( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); + } + break; + } + } + } + + private void restartForReason(int reason) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + mBluetooth.unregisterCallback(mBluetoothCallback); + } + } catch (RemoteException re) { + Slog.e(TAG, "Unable to unregister", re); + } finally { + mBluetoothLock.readLock().unlock(); + } + + if (mState == BluetoothAdapter.STATE_TURNING_OFF) { + // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); + mState = BluetoothAdapter.STATE_OFF; + } + if (mState == BluetoothAdapter.STATE_OFF) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); + mState = BluetoothAdapter.STATE_TURNING_ON; + } + + waitForState(Set.of(BluetoothAdapter.STATE_ON)); + + if (mState == BluetoothAdapter.STATE_TURNING_ON) { + bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); + } + + unbindAllBluetoothProfileServices(); + // disable + addActiveLog(reason, mContext.getPackageName(), false); + handleDisable(); + // Pbap service need receive STATE_TURNING_OFF intent to close + bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, + BluetoothAdapter.STATE_TURNING_OFF); + + boolean didDisableTimeout = + !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); + + bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, + BluetoothAdapter.STATE_OFF); + sendBluetoothServiceDownCallback(); + + try { + mBluetoothLock.writeLock().lock(); + if (mBluetooth != null) { + mBluetooth = null; + // Unbind + mContext.unbindService(mConnection); } + mBluetoothGatt = null; + } finally { + mBluetoothLock.writeLock().unlock(); } + + // + // If disabling Bluetooth times out, wait for an + // additional amount of time to ensure the process is + // shut down completely before attempting to restart. + // + if (didDisableTimeout) { + SystemClock.sleep(3000); + } else { + SystemClock.sleep(100); + } + + mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); + mState = BluetoothAdapter.STATE_OFF; + // enable + addActiveLog(reason, mContext.getPackageName(), true); + // mEnable flag could have been reset on disableBLE. Reenable it. + mEnable = true; + handleEnable(mQuietEnable); } } @@ -2643,6 +2688,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return "RESTORE_USER_SETTING"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: return "FACTORY_RESET"; + case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED: + return "INIT_FLAGS_CHANGED"; case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: default: return "UNKNOWN[" + reason + "]"; } -- GitLab From 3a268eaff222b076857b736fc17f2f373d739110 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Mon, 1 Jun 2020 15:33:36 -0700 Subject: [PATCH 1167/1408] Create a binder cache for IBluetooth.getConnectionState Bug: 157935587 This adds a binder cache for the IBluetooth.getConnectionState() method. There is no change in bluetooth functionality. Each atest is run twice. Once with the code to be committed and once with a special build that sets PropertyInvalidatedCache DEBUG and VERIFY to true. In the latter case, the test passes if the atest passes and if there are no errors from the cache verification. Tag: #feature Test: atest BluetoothInstrumentationTests Test results are Passed: 479, Failed: 0, Ignored: 4, Assumption Failed: 75 Change-Id: I2946297ffab557877dc7ec56206834d7c3776662 --- .../android/bluetooth/BluetoothAdapter.java | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 29a98faf5cd..8c3268c6fc1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2348,6 +2348,36 @@ public final class BluetoothAdapter { return supportedProfiles; } + private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY = + "cache_key.bluetooth.get_adapter_connection_state"; + private final PropertyInvalidatedCache + mBluetoothGetAdapterConnectionStateCache = + new PropertyInvalidatedCache ( + 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) { + /** + * This method must not be called when mService is null. + */ + @Override + protected Integer recompute(Void query) { + try { + return mService.getAdapterConnectionState(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + }; + + /** @hide */ + public void disableGetAdapterConnectionStateCache() { + mBluetoothGetAdapterConnectionStateCache.disableLocal(); + } + + /** @hide */ + public static void invalidateGetAdapterConnectionStateCache() { + PropertyInvalidatedCache.invalidateCache( + BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY); + } + /** * Get the current connection state of the local Bluetooth adapter. * This can be used to check whether the local Bluetooth adapter is connected @@ -2368,10 +2398,14 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getAdapterConnectionState(); + return mBluetoothGetAdapterConnectionStateCache.query(null); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "getConnectionState:", e.getCause()); + } else { + throw e; } - } catch (RemoteException e) { - Log.e(TAG, "getConnectionState:", e); } finally { mServiceLock.readLock().unlock(); } -- GitLab From a1a0ab30e331d9f5c9f5a65c10f42444e5ef037d Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Mon, 8 Jun 2020 15:02:34 -0700 Subject: [PATCH 1168/1408] Bluetooth: Add pairing initiator extra Remember if the pairing app was in the foreground. Bug: 150156492 Test: pair two devices, unpair on one device, try to reconnect Change-Id: I6f564857974146e0c18134648eb1e297e8afbc6f --- .../android/bluetooth/BluetoothDevice.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index dc7d05300d4..fb9746830d4 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -326,6 +326,26 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents as the value of passkey. + * @hide + */ + public static final String EXTRA_PAIRING_INITIATOR = + "android.bluetooth.device.extra.PAIRING_INITIATOR"; + + /** + * Bluetooth pairing initiator, Foreground App + * @hide + */ + public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1; + + /** + * Bluetooth pairing initiator, Background + * @hide + */ + public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2; + /** * Bluetooth device type, Unknown */ -- GitLab From 222c651028d03639398b3275b7612740dff5242c Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 19 Jun 2020 14:47:41 -0700 Subject: [PATCH 1169/1408] 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. Bug: 159121253 Test: treehugger (i.e. this shouldn't trigger "API has changed" error.) Change-Id: Ie1841a670bdf6c6f4b25a1fc5deed8ec2d18cda2 --- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 3 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 --- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 --- framework/java/android/bluetooth/BluetoothSap.java | 3 --- 4 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 85e0e08b19c..28363250ebd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -587,7 +586,6 @@ public final class BluetoothHeadsetClient 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) { @@ -637,7 +635,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bbc..4f5c4feb368 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 + ")"); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d3452ffb458..f356da18fc7 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; import android.os.Binder; import android.os.IBinder; @@ -276,7 +275,6 @@ public final class BluetoothPbapClient 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) { @@ -329,7 +327,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 6e0348158f4..48e8c1ada25 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -328,7 +327,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -377,7 +375,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); -- GitLab From 171f074f3a7c0ddb9f5cf5ef306e1de0c2c9cb7d Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 19 Jun 2020 14:47:41 -0700 Subject: [PATCH 1170/1408] 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: Ie1841a670bdf6c6f4b25a1fc5deed8ec2d18cda2 --- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 3 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 --- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 --- framework/java/android/bluetooth/BluetoothSap.java | 3 --- 4 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 85e0e08b19c..28363250ebd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -587,7 +586,6 @@ public final class BluetoothHeadsetClient 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) { @@ -637,7 +635,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bbc..4f5c4feb368 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 + ")"); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d3452ffb458..f356da18fc7 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; import android.os.Binder; import android.os.IBinder; @@ -276,7 +275,6 @@ public final class BluetoothPbapClient 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) { @@ -329,7 +327,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 6e0348158f4..48e8c1ada25 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -328,7 +327,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -377,7 +375,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); -- GitLab From d9f7c771e6e7be6337879c5f3b72a6ec3272af8b Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 19 Jun 2020 14:26:01 -0700 Subject: [PATCH 1171/1408] 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/BluetoothHeadsetClient.java | 3 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 --- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 --- framework/java/android/bluetooth/BluetoothSap.java | 3 --- 4 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 85e0e08b19c..28363250ebd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -587,7 +586,6 @@ public final class BluetoothHeadsetClient 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) { @@ -637,7 +635,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bbc..4f5c4feb368 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 + ")"); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d3452ffb458..f356da18fc7 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; import android.os.Binder; import android.os.IBinder; @@ -276,7 +275,6 @@ public final class BluetoothPbapClient 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) { @@ -329,7 +327,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 6e0348158f4..48e8c1ada25 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -328,7 +327,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -377,7 +375,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); -- GitLab From 7498830ee0cbfa567a672503fc9dd9ed381b38f9 Mon Sep 17 00:00:00 2001 From: Fei Zheng Date: Wed, 25 Dec 2019 13:53:16 +0800 Subject: [PATCH 1172/1408] 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 --- .../android/bluetooth/BluetoothMapClient.java | 69 ++++++++ framework/tests/AndroidManifest.xml | 6 +- .../bluetooth/BluetoothStressTest.java | 24 +++ .../bluetooth/BluetoothTestRunner.java | 11 ++ .../android/bluetooth/BluetoothTestUtils.java | 156 +++++++++++++++++- 5 files changed, 263 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0aa5aac5d8f..f01a05a14e9 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; diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index 7f9d8749358..6849a90f501 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -15,14 +15,18 @@ --> + package="com.android.bluetooth.tests" + android:sharedUserId="android.uid.bluetooth" > + + + diff --git a/framework/tests/src/android/bluetooth/BluetoothStressTest.java b/framework/tests/src/android/bluetooth/BluetoothStressTest.java index 4b32ceae061..89dbe3f75b5 100644 --- a/framework/tests/src/android/bluetooth/BluetoothStressTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothStressTest.java @@ -360,6 +360,30 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.unpair(mAdapter, device); } + /* Make sure there is at least 1 unread message in the last week on remote device */ + public void testMceSetMessageStatus() { + int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations; + if (iterations == 0) { + return; + } + + BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); + mTestUtils.enable(mAdapter); + mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null); + mTestUtils.mceGetUnreadMessage(mAdapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ); + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD); + } + + /** + * It is hard to find device to support set undeleted status, so just + * set deleted in 1 iteration + **/ + mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED); + } + private void sleep(long time) { try { Thread.sleep(time); diff --git a/framework/tests/src/android/bluetooth/BluetoothTestRunner.java b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java index 56e691d8c24..d19c2c3e7e2 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestRunner.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestRunner.java @@ -40,6 +40,7 @@ import android.util.Log; * [-e connect_input_iterations ] \ * [-e connect_pan_iterations ] \ * [-e start_stop_sco_iterations ] \ + * [-e mce_set_message_status_iterations ] \ * [-e pair_address

          ] \ * [-e headset_address
          ] \ * [-e a2dp_address
          ] \ @@ -64,6 +65,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { public static int sConnectInputIterations = 100; public static int sConnectPanIterations = 100; public static int sStartStopScoIterations = 100; + public static int sMceSetMessageStatusIterations = 100; public static String sDeviceAddress = ""; public static byte[] sDevicePairPin = {'1', '2', '3', '4'}; @@ -173,6 +175,15 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { } } + val = arguments.getString("mce_set_message_status_iterations"); + if (val != null) { + try { + sMceSetMessageStatusIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + val = arguments.getString("device_address"); if (val != null) { sDeviceAddress = val; diff --git a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java index ed613c36b89..409025bc670 100644 --- a/framework/tests/src/android/bluetooth/BluetoothTestUtils.java +++ b/framework/tests/src/android/bluetooth/BluetoothTestUtils.java @@ -56,6 +56,10 @@ public class BluetoothTestUtils extends Assert { private static final int CONNECT_PROXY_TIMEOUT = 5000; /** Time between polls in ms. */ private static final int POLL_TIME = 100; + /** Timeout to get map message in ms. */ + private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000; + /** Timeout to set map message status in ms. */ + private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000; private abstract class FlagReceiver extends BroadcastReceiver { private int mExpectedFlags = 0; @@ -98,6 +102,8 @@ public class BluetoothTestUtils extends Assert { private static final int STATE_TURNING_ON_FLAG = 1 << 6; private static final int STATE_ON_FLAG = 1 << 7; private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9; + private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10; public BluetoothReceiver(int expectedFlags) { super(expectedFlags); @@ -231,6 +237,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; break; + case BluetoothProfile.MAP_CLIENT: + mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED; + break; default: mConnectionAction = null; } @@ -308,6 +317,34 @@ public class BluetoothTestUtils extends Assert { } } + + private class MceSetMessageStatusReceiver extends FlagReceiver { + private static final int MESSAGE_RECEIVED_FLAG = 1; + private static final int STATUS_CHANGED_FLAG = 1 << 1; + + public MceSetMessageStatusReceiver(int expectedFlags) { + super(expectedFlags); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) { + String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE); + assertNotNull(handle); + setFiredFlag(MESSAGE_RECEIVED_FLAG); + mMsgHandle = handle; + } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) { + int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); + assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); + setFiredFlag(STATUS_CHANGED_FLAG); + } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) { + int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); + assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); + setFiredFlag(STATUS_CHANGED_FLAG); + } + } + } + private BluetoothProfile.ServiceListener mServiceListener = new BluetoothProfile.ServiceListener() { @Override @@ -326,6 +363,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mPan = (BluetoothPan) proxy; break; + case BluetoothProfile.MAP_CLIENT: + mMce = (BluetoothMapClient) proxy; + break; } } } @@ -346,6 +386,9 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.PAN: mPan = null; break; + case BluetoothProfile.MAP_CLIENT: + mMce = null; + break; } } } @@ -362,6 +405,8 @@ public class BluetoothTestUtils extends Assert { private BluetoothHeadset mHeadset = null; private BluetoothHidHost mInput = null; private BluetoothPan mPan = null; + private BluetoothMapClient mMce = null; + private String mMsgHandle = null; /** * Creates a utility instance for testing Bluetooth. @@ -898,7 +943,7 @@ public class BluetoothTestUtils extends Assert { * @param adapter The BT adapter. * @param device The remote device. * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. + * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}.. * @param methodName The method name to printed in the logs. If null, will be * "connectProfile(profile=<profile>, device=<device>)" */ @@ -941,6 +986,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothHeadset)proxy).connect(device)); } else if (profile == BluetoothProfile.HID_HOST) { assertTrue(((BluetoothHidHost)proxy).connect(device)); + } else if (profile == BluetoothProfile.MAP_CLIENT) { + assertTrue(((BluetoothMapClient)proxy).connect(device)); } break; default: @@ -1016,6 +1063,8 @@ public class BluetoothTestUtils extends Assert { assertTrue(((BluetoothHeadset)proxy).disconnect(device)); } else if (profile == BluetoothProfile.HID_HOST) { assertTrue(((BluetoothHidHost)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.MAP_CLIENT) { + assertTrue(((BluetoothMapClient)proxy).disconnect(device)); } break; case BluetoothProfile.STATE_DISCONNECTED: @@ -1373,6 +1422,89 @@ public class BluetoothTestUtils extends Assert { } } + public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) { + int mask; + String methodName = "getUnreadMessage"; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); + assertNotNull(mMce); + + if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { + fail(String.format("%s device is not connected", methodName)); + } + + mMsgHandle = null; + mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG; + MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); + assertTrue(mMce.getUnreadMessages(device)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) { + if ((receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("%s completed", methodName)); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask)); + } + + /** + * Set a message to read/unread/deleted/undeleted + */ + public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) { + int mask; + String methodName = "setMessageStatus"; + + if (!adapter.isEnabled()) { + fail(String.format("%s bluetooth not enabled", methodName)); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail(String.format("%s device not paired", methodName)); + } + + mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); + assertNotNull(mMce); + + if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { + fail(String.format("%s device is not connected", methodName)); + } + + assertNotNull(mMsgHandle); + mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG; + MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); + + assertTrue(mMce.setMessageStatus(device, mMsgHandle, status)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) { + if ((receiver.getFiredFlags() & mask) == mask) { + writeOutput(String.format("%s completed", methodName)); + removeReceiver(receiver); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = receiver.getFiredFlags(); + removeReceiver(receiver); + fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", + methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask)); + } + private void addReceiver(BroadcastReceiver receiver, String[] actions) { IntentFilter filter = new IntentFilter(); for (String action: actions) { @@ -1408,7 +1540,8 @@ public class BluetoothTestUtils extends Assert { String[] actions = { BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED}; + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, + BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED}; ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, expectedFlags); addReceiver(receiver, actions); @@ -1430,6 +1563,16 @@ public class BluetoothTestUtils extends Assert { return receiver; } + private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device, + int expectedFlags) { + String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED, + BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED, + BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED}; + MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags); + addReceiver(receiver, actions); + return receiver; + } + private void removeReceiver(BroadcastReceiver receiver) { mContext.unregisterReceiver(receiver); mReceivers.remove(receiver); @@ -1456,6 +1599,10 @@ public class BluetoothTestUtils extends Assert { if (mPan != null) { return mPan; } + case BluetoothProfile.MAP_CLIENT: + if (mMce != null) { + return mMce; + } break; default: return null; @@ -1483,6 +1630,11 @@ public class BluetoothTestUtils extends Assert { sleep(POLL_TIME); } return mPan; + case BluetoothProfile.MAP_CLIENT: + while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { + sleep(POLL_TIME); + } + return mMce; default: return null; } -- GitLab From aa988ed154110bea2ac85e7963c274517715fa49 Mon Sep 17 00:00:00 2001 From: weichinweng Date: Mon, 29 Jun 2020 08:14:49 +0800 Subject: [PATCH 1173/1408] Add debug log for set/remove active device. Bug: 160746277 Test: atest BluetoothInstrumentationTests Tag: #feature Change-Id: I3decaa102345d9e9485882cfeee2fae203264e25 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f962ea0966b..47bd207fee6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1784,6 +1784,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); return mService.removeActiveDevice(profiles); } } catch (RemoteException e) { @@ -1828,6 +1829,9 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { + if (DBG) { + Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); + } return mService.setActiveDevice(device, profiles); } } catch (RemoteException e) { -- GitLab From 37f4b40fbbb8e712dda525c5531177f6d2f513cd Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Thu, 23 Jul 2020 14:38:32 -0700 Subject: [PATCH 1174/1408] BluetoothManagerService: enforce BLUETOOTH_PRIVILEGED for onFactoryReset Bug: 159061926 Test: Try app_debug.apk in the bug, and make sure Bluetooth won't be turned off by the app Change-Id: I7d7e684be7908fc95f4f87528fb0ba7e5ace9f4a --- .../android/server/bluetooth/BluetoothManagerService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0d4efed25da..8c38b66dd44 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -95,6 +95,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String BLUETOOTH_PRIVILEGED = + android.Manifest.permission.BLUETOOTH_PRIVILEGED; private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid"; private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address"; @@ -279,6 +281,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { }; public boolean onFactoryReset() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, + "Need BLUETOOTH_PRIVILEGED permission"); + // Wait for stable state if bluetooth is temporary state. int state = getState(); if (state == BluetoothAdapter.STATE_BLE_TURNING_ON -- GitLab From d00dcad73277d4905de507b34f9b65fa5b1ced6c Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Tue, 28 Jul 2020 08:15:49 +0000 Subject: [PATCH 1175/1408] Update language to comply with Android's inclusive language guidance See https://source.android.com/setup/contribute/respectful-code for reference #inclusivefixit Bug: 161896447 Test: NA (Comment only) Change-Id: Id4f72f98d5a5def26d42d112aeb1b67217d38af0 --- framework/java/android/bluetooth/BluetoothDevice.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a2cf7d9cc24..8d68cdb1434 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1450,7 +1450,7 @@ public final class BluetoothDevice implements Parcelable { * present in the cache. Clients should use the {@link #getUuids} to get UUIDs * if service discovery is not to be performed. * - * @return False if the sanity check fails, True if the process of initiating an ACL connection + * @return False if the check fails, True if the process of initiating an ACL connection * to the remote device was started. */ @RequiresPermission(Manifest.permission.BLUETOOTH) @@ -1484,7 +1484,7 @@ public final class BluetoothDevice implements Parcelable { * The object type will match one of the SdpXxxRecord types, depending on the UUID searched * for. * - * @return False if the sanity check fails, True if the process + * @return False if the check fails, True if the process * of initiating an ACL connection to the remote device * was started. */ -- GitLab From 6e3b8143292ab819fa9cb62a5af145ae87c16701 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 31 Jul 2020 11:05:01 -0700 Subject: [PATCH 1176/1408] Do not log sensitive information in AudioService. Sensitive information, such as IP and MAC address, should not be logged. In AudioService, only log the last 3 octets of BT device address. Test: make, connect BT device and log Bug: 162299985 Change-Id: I99db812efc327e6f3ebe791274998291a140a5b6 --- .../java/android/bluetooth/BluetoothDevice.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index fb9746830d4..548bca48568 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1032,6 +1032,18 @@ public final class BluetoothDevice implements Parcelable { return mAddress; } + /** + * Returns the anonymized hardware address of this BluetoothDevice. The first three octets + * will be suppressed for anonymization. + *

          For example, "XX:XX:XX:AA:BB:CC". + * + * @return Anonymized bluetooth hardware address as string + * @hide + */ + public String getAnonymizedAddress() { + return "XX:XX:XX" + getAddress().substring(8); + } + /** * Get the friendly Bluetooth name of the remote device. * -- GitLab From 54bd08f8f4b91456ef262b1586a6c854486d5927 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Wed, 22 Jul 2020 19:09:30 -0700 Subject: [PATCH 1177/1408] Removed @Deprecated SystemService callback methods that take a userId. This is just a plain refactoring: the removed methods in the changed classes were called by default by the new methods in the superclass (SystemService). Test: m Test: atest NotificationManagerServiceTest BackupManagerServiceRoboTest Fixes: 161943081 Exempt-From-Owner-Approval: refactoring without side-effects Change-Id: Ifd8df592eb4494cc0922b7e0b2ff20187b8a8b3e --- .../android/server/bluetooth/BluetoothService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothService.java b/service/java/com/android/server/bluetooth/BluetoothService.java index 0bcd9373c66..1a1eecd0f43 100644 --- a/service/java/com/android/server/bluetooth/BluetoothService.java +++ b/service/java/com/android/server/bluetooth/BluetoothService.java @@ -16,10 +16,14 @@ package com.android.server; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.os.UserManager; +import com.android.server.SystemService.TargetUser; + class BluetoothService extends SystemService { private BluetoothManagerService mBluetoothManagerService; private boolean mInitialized = false; @@ -52,16 +56,16 @@ class BluetoothService extends SystemService { } @Override - public void onSwitchUser(int userHandle) { + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { if (!mInitialized) { initialize(); } else { - mBluetoothManagerService.handleOnSwitchUser(userHandle); + mBluetoothManagerService.handleOnSwitchUser(to.getUserIdentifier()); } } @Override - public void onUnlockUser(int userHandle) { - mBluetoothManagerService.handleOnUnlockUser(userHandle); + public void onUserUnlocking(@NonNull TargetUser user) { + mBluetoothManagerService.handleOnUnlockUser(user.getUserIdentifier()); } } -- GitLab From ea321cd2e938a124f690c8a4a1b98e1e4b0c67a6 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Wed, 19 Aug 2020 16:53:06 -0700 Subject: [PATCH 1178/1408] Remove listenUsingEncryptedRfcommOn & listenUsingScoOn both are hidden and not @UnsupportedAppUsage, so are safe to remove Bug: 159815595 Tag: #refactor Test: compile & verify basic functions working Change-Id: I58acb4160207fc0eeaeb7875bfd61f3bd442ad9a --- .../android/bluetooth/BluetoothAdapter.java | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 47bd207fee6..4ee22b78c5d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2562,52 +2562,6 @@ public final class BluetoothAdapter { return socket; } - /** - * Construct an encrypted, RFCOMM server socket. - * Call #accept to retrieve connections to this socket. - * - * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port); - int errno = socket.mSocket.bindListen(); - if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); - } - if (errno < 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - - /** - * Construct a SCO server socket. - * Call #accept to retrieve connections to this socket. - * - * @return A SCO BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - public static BluetoothServerSocket listenUsingScoOn() throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1); - int errno = socket.mSocket.bindListen(); - if (errno < 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - } - return socket; - } - /** * Construct an encrypted, authenticated, L2CAP server socket. * Call #accept to retrieve connections to this socket. -- GitLab From 8c7ed324f76cbc70c0771984f5fa67dbdb0a8964 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 19 Jun 2020 14:47:41 -0700 Subject: [PATCH 1179/1408] 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: Ie1841a670bdf6c6f4b25a1fc5deed8ec2d18cda2 --- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 3 --- framework/java/android/bluetooth/BluetoothMapClient.java | 3 --- framework/java/android/bluetooth/BluetoothPbapClient.java | 3 --- framework/java/android/bluetooth/BluetoothSap.java | 3 --- 4 files changed, 12 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 85e0e08b19c..28363250ebd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.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.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -587,7 +586,6 @@ public final class BluetoothHeadsetClient 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) { @@ -637,7 +635,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 19240dc0bbc..4f5c4feb368 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 + ")"); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d3452ffb458..f356da18fc7 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.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.content.Context; import android.os.Binder; import android.os.IBinder; @@ -276,7 +275,6 @@ public final class BluetoothPbapClient 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) { @@ -329,7 +327,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 6e0348158f4..48e8c1ada25 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -328,7 +327,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -377,7 +375,6 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); -- GitLab From 07f75cf7c0f237ffce9b41c60d20f6c6ed3d50c2 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Thu, 3 Sep 2020 15:50:28 -0700 Subject: [PATCH 1180/1408] [bluetooth] system_server should not call non forUser Settings.Secure.get* methods BUG: 166312046 Test: builds Change-Id: Ia8ce189e50bcabf8949df422e1a89c86c5492e08 --- .../bluetooth/BluetoothManagerService.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f372c6f85ec..0d79240a4b5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -166,6 +166,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private String mAddress; private String mName; private final ContentResolver mContentResolver; + private final int mUserId; private final RemoteCallbackList mCallbacks; private final RemoteCallbackList mStateChangeCallbacks; private IBinder mBluetoothBinder; @@ -481,6 +482,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mName = null; mErrorRecoveryRetryCounter = 0; mContentResolver = context.getContentResolver(); + mUserId = mContentResolver.getUserId(); // Observe BLE scan only mode settings change. registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList(); @@ -625,7 +627,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mContext.getResources() .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation) - && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) + && Settings.Secure.getIntForUser(mContentResolver, + SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId) == 0) { // if the valid flag is not set, don't load the address and name if (DBG) { @@ -633,8 +636,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } return; } - mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); - mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); + mName = Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId); + mAddress = Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId); if (DBG) { Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); } @@ -648,26 +653,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void storeNameAndAddress(String name, String address) { if (name != null) { - Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); + Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name, + mUserId); mName = name; if (DBG) { - Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver, - SECURE_SETTINGS_BLUETOOTH_NAME)); + Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, + mUserId)); } } if (address != null) { - Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); + Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, + address, mUserId); mAddress = address; if (DBG) { Slog.d(TAG, - "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver, - SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, + mUserId)); } } if ((name != null) && (address != null)) { - Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1); + Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1, + mUserId); } } -- GitLab From 1d4f77992e8db920c72178587e7b61b42fe404e4 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 11 Sep 2020 14:43:28 -0600 Subject: [PATCH 1181/1408] Update language to comply with Android's inclusive language guidance See https://source.android.com/setup/contribute/respectful-code for reference Test: none Bug: 168334533 Exempt-From-Owner-Approval: docs updates Change-Id: Id1a0f2a38e41badee3a34bd71af240511f273a7b --- framework/java/android/bluetooth/BluetoothGattCallback.java | 2 +- .../java/android/bluetooth/BluetoothGattServerCallback.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index cf82a330457..f718c0b57c1 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -183,7 +183,7 @@ public abstract class BluetoothGattCallback { * @param gatt GATT client involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid range + * @param latency Worker latency for the connection in number of connection events. Valid range * is from 0 to 499 * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 * (0.1s) to 3200 (32s) diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java index 2c8114be3fe..0ead5f57e86 100644 --- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java @@ -187,7 +187,7 @@ public abstract class BluetoothGattServerCallback { * @param device The remote device involved * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from * 6 (7.5ms) to 3200 (4000ms). - * @param latency Slave latency for the connection in number of connection events. Valid range + * @param latency Worker latency for the connection in number of connection events. Valid range * is from 0 to 499 * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 * (0.1s) to 3200 (32s) -- GitLab From c5386afbc18b5164c381b1d8e099327953205436 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 11 Sep 2020 14:57:21 -0600 Subject: [PATCH 1182/1408] Update language to comply with Android's inclusive language guidance See https://source.android.com/setup/contribute/respectful-code for reference Test: none Bug: 168334533 Exempt-From-Owner-Approval: docs updates Change-Id: I245b8d9cac722da76ea67983738a3cbb9deb68df --- .../java/android/bluetooth/BluetoothAdapter.java | 10 +++++----- .../java/android/bluetooth/BluetoothDevice.java | 12 ++++++------ framework/java/android/bluetooth/BluetoothGatt.java | 4 ++-- .../bluetooth/BluetoothGattCharacteristic.java | 6 +++--- .../android/bluetooth/BluetoothGattDescriptor.java | 6 +++--- .../java/android/bluetooth/BluetoothGattService.java | 2 +- .../android/bluetooth/BluetoothServerSocket.java | 2 +- .../java/android/bluetooth/BluetoothSocket.java | 11 +++++++---- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f062a39c6f9..3c7d8fca20a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2470,7 +2470,7 @@ public final class BluetoothAdapter { * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * * @param channel RFCOMM channel to listen on - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 * connections. * @return a listening RFCOMM BluetoothServerSocket @@ -2528,7 +2528,7 @@ public final class BluetoothAdapter { /** * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. *

          The link key is not required to be authenticated, i.e the communication may be - * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices, + * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, * the link will be encrypted, as encryption is mandartory. * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an @@ -2561,7 +2561,7 @@ public final class BluetoothAdapter { * Create a listening, encrypted, * RFCOMM Bluetooth socket with Service Record. *

          The link will be encrypted, but the link key is not required to be authenticated - * i.e the communication is vulnerable to Man In the Middle attacks. Use + * i.e the communication is vulnerable to Person In the Middle attacks. Use * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. *

          Use this socket if authentication of link key is not possible. * For example, for Bluetooth 2.1 devices, if any of the devices does not have @@ -2646,7 +2646,7 @@ public final class BluetoothAdapter { * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. * * @param port the PSM to listen on - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 * connections. * @return An L2CAP BluetoothServerSocket @@ -3347,7 +3347,7 @@ public final class BluetoothAdapter { * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The * supported Bluetooth transport is LE only. *

          The link key is not required to be authenticated, i.e the communication may be vulnerable - * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and + * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and * authenticated communication channel is desired. *

          Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5c13c08f752..cf8cdd4ea9d 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1813,7 +1813,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. *

          Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1848,7 +1848,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. *

          Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1905,7 +1905,7 @@ public final class BluetoothDevice implements Parcelable { * socket will be encrypted. *

          Use this socket only if an authenticated socket link is possible. * Authentication refers to the authentication of the link key to - * prevent man-in-the-middle type of attacks. + * prevent person-in-the-middle type of attacks. * For example, for Bluetooth 2.1 devices, if any of the devices does not * have an input and output capability or just has the ability to * display a numeric key, a secure socket connection is not possible. @@ -1937,7 +1937,7 @@ public final class BluetoothDevice implements Parcelable { * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure * outgoing connection to this remote device using SDP lookup of uuid. *

          The communication channel will not have an authenticated link key - * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1 + * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1 * devices, the link key will be encrypted, as encryption is mandatory. * For legacy devices (pre Bluetooth 2.1 devices) the link key will * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an @@ -2193,7 +2193,7 @@ public final class BluetoothDevice implements Parcelable { *

          The remote device will be authenticated and communication on this socket will be * encrypted. *

          Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent man-in-the-middle type of attacks. + * to the authentication of the link key to prevent person-in-the-middle type of attacks. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection @@ -2220,7 +2220,7 @@ public final class BluetoothDevice implements Parcelable { *

          Use {@link BluetoothSocket#connect} to initiate the outgoing connection. *

          Application using this API is responsible for obtaining PSM value from remote device. *

          The communication channel may not have an authenticated link key, i.e. it may be subject - * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and + * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and * authenticated communication channel is possible. * * @param psm dynamic PSM value from remote device diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index f877f04626d..c58b5d218e7 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -134,14 +134,14 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_NONE = 0; /** - * Authentication requested; no man-in-the-middle protection required. + * Authentication requested; no person-in-the-middle protection required. * * @hide */ /*package*/ static final int AUTHENTICATION_NO_MITM = 1; /** - * Authentication with man-in-the-middle protection requested. + * Authentication with person-in-the-middle protection requested. * * @hide */ diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 7066f470aa9..8f1b59cf69e 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -84,7 +84,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public static final int PERMISSION_READ_ENCRYPTED = 0x02; /** - * Characteristic permission: Allow reading with man-in-the-middle protection + * Characteristic permission: Allow reading with person-in-the-middle protection */ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; @@ -99,7 +99,7 @@ public class BluetoothGattCharacteristic implements Parcelable { public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; /** - * Characteristic permission: Allow encrypted writes with man-in-the-middle + * Characteristic permission: Allow encrypted writes with person-in-the-middle * protection */ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; @@ -111,7 +111,7 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Characteristic permission: Allow signed write operations with - * man-in-the-middle protection + * person-in-the-middle protection */ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 7cc2d6bc53f..49ba281e2eb 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -58,7 +58,7 @@ public class BluetoothGattDescriptor implements Parcelable { public static final int PERMISSION_READ_ENCRYPTED = 0x02; /** - * Descriptor permission: Allow reading with man-in-the-middle protection + * Descriptor permission: Allow reading with person-in-the-middle protection */ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; @@ -73,7 +73,7 @@ public class BluetoothGattDescriptor implements Parcelable { public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; /** - * Descriptor permission: Allow encrypted writes with man-in-the-middle + * Descriptor permission: Allow encrypted writes with person-in-the-middle * protection */ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; @@ -85,7 +85,7 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Descriptor permission: Allow signed write operations with - * man-in-the-middle protection + * person-in-the-middle protection */ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 13d6d7021ec..e7809aeb1bb 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -44,7 +44,7 @@ public class BluetoothGattService implements Parcelable { /** - * The remote device his service is associated with. + * The remote device this service is associated with. * This applies to client applications only. * * @hide diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 88c186c88aa..5c1bcaf3131 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -110,7 +110,7 @@ public final class BluetoothServerSocket implements Closeable { * @param auth require the remote device to be authenticated * @param encrypt require the connection to be encrypted * @param port remote port - * @param mitm enforce man-in-the-middle protection for authentication. + * @param mitm enforce person-in-the-middle protection for authentication. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index f7743696553..d41a6d064d1 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -128,9 +128,12 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; - private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ - private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ - private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ + /** when true no SPP SDP record will be created */ + private boolean mExcludeSdp = false; + /** when true Person-in-the-middle protection will be enabled */ + private boolean mAuthMitm = false; + /** Minimum 16 digit pin for sec mode 2 connections */ + private boolean mMin16DigitPin = false; @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.") private ParcelFileDescriptor mPfd; @UnsupportedAppUsage @@ -190,7 +193,7 @@ public final class BluetoothSocket implements Closeable { * @param device remote device that this socket can connect to * @param port remote port * @param uuid SDP uuid - * @param mitm enforce man-in-the-middle protection. + * @param mitm enforce person-in-the-middle protection. * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges -- GitLab From bd5a867727010fa04efe63096d7b00b547baec6e Mon Sep 17 00:00:00 2001 From: Ted Wang Date: Thu, 27 Aug 2020 18:22:41 +0800 Subject: [PATCH 1183/1408] Add action for Bluetooth tethering state changed Add new action for Bluetooth tethering state changed and extra for broadcasting Bluetooth tethering state changed. Tag: #feature Bug: 138688805 Test: Manual Change-Id: Ie0f848448f9717a6a1233d0dbb0785da7d30dc81 --- .../java/android/bluetooth/BluetoothPan.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index bfc28fae6e6..4698b077ff5 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -89,6 +89,33 @@ public final class BluetoothPan implements BluetoothProfile { @SuppressLint("ActionValue") public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + /** + * Intent used to broadcast the change in tethering state of the Pan + * Profile + * + *

          This intent will have 1 extra: + *

            + *
          • {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth + * tethering.
          • + *
          + * + *

          {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or + * {@link #TETHERING_STATE_ON} + *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_TETHERING_STATE_CHANGED = + "android.bluetooth.action.TETHERING_STATE_CHANGED"; + + /** + * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent + * The tethering state of the PAN profile. + * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}. + */ + public static final String EXTRA_TETHERING_STATE = + "android.bluetooth.extra.TETHERING_STATE"; + /** @hide */ @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE}) @Retention(RetentionPolicy.SOURCE) @@ -114,6 +141,14 @@ public final class BluetoothPan implements BluetoothProfile { public static final int REMOTE_PANU_ROLE = 2; + /** @hide **/ + @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON}) + @Retention(RetentionPolicy.SOURCE) + public @interface TetheringState{} + + public static final int TETHERING_STATE_OFF = 1; + + public static final int TETHERING_STATE_ON = 2; /** * Return codes for the connect and disconnect Bluez / Dbus calls. * -- GitLab From 66ac01aedaa07d86151954e1a656c48817c804ba Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 16 Sep 2020 15:48:47 +0200 Subject: [PATCH 1184/1408] Bluetooth: log who changes adapter name This is very helpful to find which third party app modified the adapter name. Bug: 168605944 Test: manual - change name, observe logcat output Change-Id: I9a13c89e6ef416286411f786e233d81507c9d53a --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2e4d44cef41..bb567b4e2c7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -412,7 +412,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); if (DBG) { - Slog.d(TAG, "Bluetooth Adapter name changed to " + newName); + Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by " + + mContext.getPackageName()); } if (newName != null) { storeNameAndAddress(newName, null); -- GitLab From 579d71ab7f467dcbefcb0831690703f6506481b7 Mon Sep 17 00:00:00 2001 From: Treehugger Robot Date: Thu, 17 Sep 2020 05:56:37 +0000 Subject: [PATCH 1185/1408] Add action for Bluetooth tethering state changed Add new action for Bluetooth tethering state changed and extra for broadcasting Bluetooth tethering state changed. Tag: #feature Bug: 138688805 Test: Manual Change-Id: Ia9e532d720f6cdd969bcb0fd5beda0a8a8866c3f --- .../java/android/bluetooth/BluetoothPan.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 73c38cbfbe3..ce3c7d25a10 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -89,6 +89,33 @@ public final class BluetoothPan implements BluetoothProfile { @SuppressLint("ActionValue") public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; + /** + * Intent used to broadcast the change in tethering state of the Pan + * Profile + * + *

          This intent will have 1 extra: + *

            + *
          • {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth + * tethering.
          • + *
          + * + *

          {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or + * {@link #TETHERING_STATE_ON} + *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_TETHERING_STATE_CHANGED = + "android.bluetooth.action.TETHERING_STATE_CHANGED"; + + /** + * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent + * The tethering state of the PAN profile. + * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}. + */ + public static final String EXTRA_TETHERING_STATE = + "android.bluetooth.extra.TETHERING_STATE"; + /** @hide */ @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE}) @Retention(RetentionPolicy.SOURCE) @@ -114,6 +141,14 @@ public final class BluetoothPan implements BluetoothProfile { public static final int REMOTE_PANU_ROLE = 2; + /** @hide **/ + @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON}) + @Retention(RetentionPolicy.SOURCE) + public @interface TetheringState{} + + public static final int TETHERING_STATE_OFF = 1; + + public static final int TETHERING_STATE_ON = 2; /** * Return codes for the connect and disconnect Bluez / Dbus calls. * -- GitLab From f12655cc803675060d87665b8f6685d5c4816431 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 21 Sep 2020 20:50:10 -0700 Subject: [PATCH 1186/1408] Move all BluetoothCodecConfig and BluetoothCodecStatus APIs moved from the non-SDK API list to the blocklist in Android 11 back to the non-SDK API list. Tag: #feature Bug: 168812851 Test: atest BluetoothHostTest#testCodecMethodsAccessible Change-Id: I29983284b1a1c271d983c99b286e204604abdc72 --- .../bluetooth/BluetoothCodecConfig.java | 32 ++++++++++++++++++- .../bluetooth/BluetoothCodecStatus.java | 5 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index d2a15357aa1..735980beebb 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d5..7b567b4098e 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } -- GitLab From d173ec435715533f4c9fd3d104df70afae8826d8 Mon Sep 17 00:00:00 2001 From: HsingYuan Lo Date: Wed, 23 Sep 2020 17:43:38 +0800 Subject: [PATCH 1187/1408] Expose service changed event to application (2/3) Handle service changed event from GattService Bug: 154056389 Tag: #feature Test: test service changed scenario Change-Id: If56dcbd7cf17b23c88cceb9c67c5d75b21320965 --- .../java/android/bluetooth/BluetoothGatt.java | 25 +++++++++++++++++++ .../bluetooth/BluetoothGattCallback.java | 13 ++++++++++ 2 files changed, 38 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index c58b5d218e7..6d22eb93fd0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -688,6 +688,31 @@ public final class BluetoothGatt implements BluetoothProfile { } }); } + + /** + * Callback invoked when service changed event is received + * @hide + */ + @Override + public void onServiceChanged(String address) { + if (DBG) { + Log.d(TAG, "onServiceChanged() - Device=" + address); + } + + if (!address.equals(mDevice.getAddress())) { + return; + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGattCallback callback = mCallback; + if (callback != null) { + callback.onServiceChanged(BluetoothGatt.this); + } + } + }); + } }; /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index f718c0b57c1..9f6b8287e79 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -194,4 +194,17 @@ public abstract class BluetoothGattCallback { public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout, int status) { } + + /** + * Callback indicating service changed event is received + * + *

          Receiving this event means that the GATT database is out of sync with + * the remote device. {@link BluetoothGatt#discoverServices} should be + * called to re-discover the services. + * + * @param gatt GATT client involved + * @hide + */ + public void onServiceChanged(BluetoothGatt gatt) { + } } -- GitLab From f36b9b5d686e8bf02a1d9fd482324037ebf2310f Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 25 Sep 2020 20:17:33 +0200 Subject: [PATCH 1188/1408] Make BluetoothGattCallback.onServiceChanged public Bug: 154056389 Test: proper CTS test will be provided Change-Id: I2c9264910b65d62124c75c3ee3fd0b5bd18a2006 --- framework/java/android/bluetooth/BluetoothGattCallback.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 9f6b8287e79..1c40cff076f 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.NonNull; + /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. */ @@ -203,8 +205,7 @@ public abstract class BluetoothGattCallback { * called to re-discover the services. * * @param gatt GATT client involved - * @hide */ - public void onServiceChanged(BluetoothGatt gatt) { + public void onServiceChanged(@NonNull BluetoothGatt gatt) { } } -- GitLab From 7bfef016fe049e35f2456034d17b6f0e57fe1732 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Mon, 5 Oct 2020 11:23:24 -0700 Subject: [PATCH 1189/1408] Bluetooth: Spell mandatory correctly Bug: 169654068 Test: compilation Change-Id: Ibb0046180760e27d6991ae17849a7405738b6f01 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3c7d8fca20a..872c3777112 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2529,7 +2529,7 @@ public final class BluetoothAdapter { * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. *

          The link key is not required to be authenticated, i.e the communication may be * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, - * the link will be encrypted, as encryption is mandartory. + * the link will be encrypted, as encryption is mandatory. * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an * encrypted and authenticated communication channel is desired. @@ -2568,7 +2568,7 @@ public final class BluetoothAdapter { * an input and output capability or just has the ability to display a numeric key, * a secure socket connection is not possible and this socket can be used. * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. - * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory. + * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. *

          Use {@link BluetoothServerSocket#accept} to retrieve incoming -- GitLab From 119a0467e8cec9c218d06012f03bd0c0072ed997 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 6 Oct 2020 11:18:09 -0600 Subject: [PATCH 1190/1408] Tighten up Binder.clearCallingIdentity() usage. The recently added AndroidFrameworkBinderIdentity Error Prone checker examines code to ensure that any cleared identities are restored to avoid obscure security vulnerabilities. This change is a purely mechanical refactoring that adds the "final" keyword to the cleared identity to ensure that it's not accidentally modified before eventually being cleared. Here's the exact command used to generate this CL: $ find . -name "*.java" -exec sed -Ei \ 's/ (long \w+ = .+?clearCallingIdentity)/ final \1/' \ {} \; Bug: 155703208 Test: make Exempt-From-Owner-Approval: trivial refactoring Change-Id: I832c9d70c3dfcd8d669cf71939d97837becc973a --- .../android/server/bluetooth/BluetoothManagerService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 4e405cc7f7e..011231c016e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -604,7 +604,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "Persisting Bluetooth Setting: " + value); } // waive WRITE_SECURE_SETTINGS permission check - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); Binder.restoreCallingIdentity(callingIdentity); } @@ -2378,7 +2378,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int foregroundUser; int callingUser = UserHandle.getCallingUserId(); int callingUid = Binder.getCallingUid(); - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); UserInfo ui = um.getProfileParent(callingUser); int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL; @@ -2605,7 +2605,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private boolean isBluetoothDisallowed() { - long callingIdentity = Binder.clearCallingIdentity(); + final long callingIdentity = Binder.clearCallingIdentity(); try { return mContext.getSystemService(UserManager.class) .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); -- GitLab From f1ecf2233c38adb0ca70b1dec8ef8dad28db8223 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 6 Oct 2020 14:54:58 -0600 Subject: [PATCH 1191/1408] Tighten up Binder.clearCallingIdentity() usage. This is a third CL in a chain that adjusts existing malformed code to follow AndroidFrameworkBinderIdentity best-practices. Specifically, if a thread clears an identity they need to restore it to avoid obscure security vulnerabilities. In addition, the relevant "try" block must start immediately after the identity is cleared to ensure that its restored if/when any exceptions are thrown. Bug: 155703208 Test: make Exempt-From-Owner-Approval: trivial refactoring Change-Id: I74cb958b68d55a647547aae21baff6ddc364859b --- .../android/bluetooth/BluetoothHidDevice.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index b5959c06cc1..2baa73822c9 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -342,44 +342,72 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onConnectionStateChanged(BluetoothDevice device, int state) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onSetProtocol(BluetoothDevice device, byte protocol) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); + } finally { + restoreCallingIdentity(token); + } } @Override public void onVirtualCableUnplug(BluetoothDevice device) { - clearCallingIdentity(); - mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); + final long token = clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); + } finally { + restoreCallingIdentity(token); + } } } -- GitLab From d88b977580187ac5fd90d84682ed19a44992fbcb Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 24 Jun 2020 22:38:33 +0200 Subject: [PATCH 1192/1408] Bluetooth: make it possible to advertise service solicitation UUID Bug: 159827149 Tag: #feature Test: called the API in test app, verified advertisement content Change-Id: I3ab43e39bb7fd8fef97e141eeb981419aa3ec35a --- .../android/bluetooth/le/AdvertiseData.java | 54 ++++++++++++++++--- .../bluetooth/le/BluetoothLeAdvertiser.java | 27 ++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 5fd82583764..573b9323264 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelUuid; @@ -43,17 +44,22 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; + @Nullable + private final List mServiceSolicitationUuids; + private final SparseArray mManufacturerSpecificData; private final Map mServiceData; private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; private AdvertiseData(List serviceUuids, + List serviceSolicitationUuids, SparseArray manufacturerData, Map serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; + mServiceSolicitationUuids = serviceSolicitationUuids; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; @@ -68,6 +74,14 @@ public final class AdvertiseData implements Parcelable { return mServiceUuids; } + /** + * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect. + */ + @Nullable + public List getServiceSolicitationUuids() { + return mServiceSolicitationUuids; + } + /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. @@ -102,8 +116,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData, - mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, + mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -119,6 +133,7 @@ public final class AdvertiseData implements Parcelable { } AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) + && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -128,7 +143,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { - return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData=" + return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" + + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -143,6 +159,8 @@ public final class AdvertiseData implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags); + dest.writeTypedArray(mServiceSolicitationUuids.toArray( + new ParcelUuid[mServiceSolicitationUuids.size()]), flags); // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); @@ -174,6 +192,11 @@ public final class AdvertiseData implements Parcelable { builder.addServiceUuid(uuid); } + ArrayList solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR); + for (ParcelUuid uuid : solicitationUuids) { + builder.addServiceSolicitationUuid(uuid); + } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); @@ -198,6 +221,8 @@ public final class AdvertiseData implements Parcelable { public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); + @Nullable + private List mServiceSolicitationUuids = new ArrayList(); private SparseArray mManufacturerSpecificData = new SparseArray(); private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; @@ -207,16 +232,30 @@ public final class AdvertiseData implements Parcelable { * Add a service UUID to advertise data. * * @param serviceUuid A service UUID to be advertised. - * @throws IllegalArgumentException If the {@code serviceUuids} are null. + * @throws IllegalArgumentException If the {@code serviceUuid} is null. */ public Builder addServiceUuid(ParcelUuid serviceUuid) { if (serviceUuid == null) { - throw new IllegalArgumentException("serivceUuids are null"); + throw new IllegalArgumentException("serviceUuid is null"); } mServiceUuids.add(serviceUuid); return this; } + /** + * Add a service solicitation UUID to advertise data. + * + * @param serviceSolicitationUuid A service solicitation UUID to be advertised. + * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null. + */ + @NonNull + public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) { + if (serviceSolicitationUuid == null) { + throw new IllegalArgumentException("serviceSolicitationUuid is null"); + } + mServiceSolicitationUuids.add(serviceSolicitationUuid); + return this; + } /** * Add service data to advertise data. * @@ -279,8 +318,9 @@ public final class AdvertiseData implements Parcelable { * Build the {@link AdvertiseData}. */ public AdvertiseData build() { - return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData, - mIncludeTxPowerLevel, mIncludeDeviceName); + return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, + mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, + mIncludeDeviceName); } } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 13c5ff69097..5f166f4a41d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -507,6 +507,33 @@ public final class BluetoothLeAdvertiser { + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } + if (data.getServiceSolicitationUuids() != null) { + int num16BitUuids = 0; + int num32BitUuids = 0; + int num128BitUuids = 0; + for (ParcelUuid uuid : data.getServiceSolicitationUuids()) { + if (BluetoothUuid.is16BitUuid(uuid)) { + ++num16BitUuids; + } else if (BluetoothUuid.is32BitUuid(uuid)) { + ++num32BitUuids; + } else { + ++num128BitUuids; + } + } + // 16 bit service uuids are grouped into one field when doing advertising. + if (num16BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; + } + // 32 bit service uuids are grouped into one field when doing advertising. + if (num32BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; + } + // 128 bit service uuids are grouped into one field when doing advertising. + if (num128BitUuids != 0) { + size += OVERHEAD_BYTES_PER_FIELD + + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; + } + } for (ParcelUuid uuid : data.getServiceData().keySet()) { int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; size += OVERHEAD_BYTES_PER_FIELD + uuidLen -- GitLab From 2883f7ab9e740b5da3c872b9fe675691ddfc322d Mon Sep 17 00:00:00 2001 From: Roman Kalukiewicz Date: Wed, 14 Oct 2020 15:59:06 -0700 Subject: [PATCH 1193/1408] Add @Nullable annotation to the parameter of Object.equals() methods. Those annotations could be inferred by some tools (like Kotlin), but the https://checkerframework.org/ doesn't check inherited annotations complaining about all equals() invocations that get nullable argument. The change was generated by running find . -name \*.java | xargs sed -i 's/public boolean equals(Object /public boolean equals(@Nullable Object /' in the frameworks/base directory and by automatically adding and formatting required imports if needed. No manual edits. Bug: 170883422 Test: Annotation change only. Should have not impact. Exempt-From-Owner-Approval: Mechanical change not specific to any component. Change-Id: I5eedb571c9d78862115dfdc5dae1cf2a35343580 --- framework/java/android/bluetooth/BluetoothAudioConfig.java | 3 ++- framework/java/android/bluetooth/BluetoothClass.java | 3 ++- framework/java/android/bluetooth/BluetoothCodecConfig.java | 3 ++- framework/java/android/bluetooth/BluetoothCodecStatus.java | 2 +- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- framework/java/android/bluetooth/BluetoothMasInstance.java | 3 ++- framework/java/android/bluetooth/le/AdvertiseData.java | 2 +- .../java/android/bluetooth/le/PeriodicAdvertisingReport.java | 2 +- framework/java/android/bluetooth/le/ScanFilter.java | 2 +- framework/java/android/bluetooth/le/ScanResult.java | 2 +- 10 files changed, 14 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAudioConfig.java b/framework/java/android/bluetooth/BluetoothAudioConfig.java index 9591a70b05d..4c8b8c11fbc 100644 --- a/framework/java/android/bluetooth/BluetoothAudioConfig.java +++ b/framework/java/android/bluetooth/BluetoothAudioConfig.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +40,7 @@ public final class BluetoothAudioConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothAudioConfig) { BluetoothAudioConfig bac = (BluetoothAudioConfig) o; return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 905b0ceec4e..603a7ff29ea 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -72,7 +73,7 @@ public final class BluetoothClass implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothClass) { return mClass == ((BluetoothClass) o).mClass; } diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index a52fc891790..1d0bf97c34e 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -208,7 +209,7 @@ public final class BluetoothCodecConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecConfig) { BluetoothCodecConfig other = (BluetoothCodecConfig) o; return (other.mCodecType == mCodecType diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7b567b4098e..7764ebeb2e3 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -56,7 +56,7 @@ public final class BluetoothCodecStatus implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecStatus) { BluetoothCodecStatus other = (BluetoothCodecStatus) o; return (Objects.equals(other.mCodecConfig, mCodecConfig) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6287453f639..1b0fe9dc2d7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -974,7 +974,7 @@ public final class BluetoothDevice implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { return mAddress.equals(((BluetoothDevice) o).getAddress()); } diff --git a/framework/java/android/bluetooth/BluetoothMasInstance.java b/framework/java/android/bluetooth/BluetoothMasInstance.java index b64d0492faf..eeaf0854514 100644 --- a/framework/java/android/bluetooth/BluetoothMasInstance.java +++ b/framework/java/android/bluetooth/BluetoothMasInstance.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +35,7 @@ public final class BluetoothMasInstance implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothMasInstance) { return mId == ((BluetoothMasInstance) o).mId; } diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 573b9323264..397326c7fd4 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -124,7 +124,7 @@ public final class AdvertiseData implements Parcelable { * @hide */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 7a8c2c6716e..54b953c25c2 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -148,7 +148,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 7511fd051e4..51f63f7fb9f 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -479,7 +479,7 @@ public final class ScanFilter implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 855d3454170..57dad1a0259 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -309,7 +309,7 @@ public final class ScanResult implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } -- GitLab From 039c048ddd9c73db6d34766ade91044ce1cfef87 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 13 Oct 2020 15:21:54 -0700 Subject: [PATCH 1194/1408] BluetoothManagerService: Separate DeviceConfigListener Use its own class instead. Test: FrameworksServicesTests Test: Push bluetooth INIT_ flags Change-Id: I4452eacffd11020b6c8fa1cb2854fb91c11cac69 --- .../BluetoothDeviceConfigListener.java | 64 +++++++++++++++++++ .../bluetooth/BluetoothManagerService.java | 38 +++-------- 2 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java diff --git a/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java b/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java new file mode 100644 index 00000000000..2dcf82ff941 --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.provider.DeviceConfig; + +/** + * The BluetoothDeviceConfigListener handles system device config change callback and checks + * whether we need to inform BluetoothManagerService on this change. + * + * The information of device config change would not be passed to the BluetoothManagerService + * when Bluetooth is on and Bluetooth is in one of the following situations: + * 1. Bluetooth A2DP is connected. + * 2. Bluetooth Hearing Aid profile is connected. + */ +class BluetoothDeviceConfigListener { + private static final String TAG = "BluetoothDeviceConfigListener"; + + BluetoothManagerService mService; + + BluetoothDeviceConfigListener(BluetoothManagerService service) { + mService = service; + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_BLUETOOTH, + (Runnable r) -> r.run(), + mDeviceConfigChangedListener); + } + + private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { + return; + } + boolean foundInit = false; + for (String name : properties.getKeyset()) { + if (name.startsWith("INIT_")) { + foundInit = true; + break; + } + } + if (!foundInit) { + return; + } + mService.onInitFlagsChanged(); + } + }; + +} diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index bb567b4e2c7..b5d28089db5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -63,7 +63,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; -import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -177,6 +176,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener; + // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; @@ -281,29 +282,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { - return; - } - boolean foundInit = false; - for (String name : properties.getKeyset()) { - if (name.startsWith("INIT_")) { - foundInit = true; - break; - } - } - if (!foundInit) { - return; - } - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); - } - }; + @VisibleForTesting + public void onInitFlagsChanged() { + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); + } public boolean onFactoryReset() { // Wait for stable state if bluetooth is temporary state. @@ -535,10 +520,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_BLUETOOTH, - (Runnable r) -> r.run(), - mDeviceConfigChangedListener); + mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this); } /** -- GitLab From 13ebdff9d9e3df737ab73533054700ef5f845aab Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 13 Oct 2020 16:06:19 -0700 Subject: [PATCH 1195/1408] BluetoothAirplaneModeListener -> BluetoothModeChangeHelper Common code can be used for BluetoothDeviceConfigListener. Test: atest FrameworksServicesTests Change-Id: Ic5ab3c04876c341bde79c03f0551719c5f774437 --- .../BluetoothAirplaneModeListener.java | 126 +-------------- .../bluetooth/BluetoothManagerService.java | 6 +- .../bluetooth/BluetoothModeChangeHelper.java | 143 ++++++++++++++++++ .../BluetoothAirplaneModeListenerTest.java | 6 +- 4 files changed, 151 insertions(+), 130 deletions(-) create mode 100644 service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index 31cd5d519d8..4d9680c785b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -16,22 +16,14 @@ package com.android.server; -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; -import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; -import android.widget.Toast; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; /** @@ -53,7 +45,7 @@ class BluetoothAirplaneModeListener { private final BluetoothManagerService mBluetoothManager; private final BluetoothAirplaneModeHandler mHandler; - private AirplaneModeHelper mAirplaneHelper; + private BluetoothModeChangeHelper mAirplaneHelper; @VisibleForTesting int mToastCount = 0; @@ -97,7 +89,7 @@ class BluetoothAirplaneModeListener { * Call after boot complete */ @VisibleForTesting - void start(AirplaneModeHelper helper) { + void start(BluetoothModeChangeHelper helper) { Log.i(TAG, "start"); mAirplaneHelper = helper; mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); @@ -141,118 +133,4 @@ class BluetoothAirplaneModeListener { } return true; } - - /** - * Helper class that handles callout and callback methods without - * complex logic. - */ - @VisibleForTesting - public static class AirplaneModeHelper { - private volatile BluetoothA2dp mA2dp; - private volatile BluetoothHearingAid mHearingAid; - private final BluetoothAdapter mAdapter; - private final Context mContext; - - AirplaneModeHelper(Context context) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mContext = context; - - mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); - mAdapter.getProfileProxy(mContext, mProfileServiceListener, - BluetoothProfile.HEARING_AID); - } - - private final ServiceListener mProfileServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - // Setup Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = (BluetoothA2dp) proxy; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = (BluetoothHearingAid) proxy; - break; - default: - break; - } - } - - @Override - public void onServiceDisconnected(int profile) { - // Clear Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = null; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = null; - break; - default: - break; - } - } - }; - - @VisibleForTesting - public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); - } - - @VisibleForTesting - public boolean isBluetoothOn() { - final BluetoothAdapter adapter = mAdapter; - if (adapter == null) { - return false; - } - return adapter.getLeState() == BluetoothAdapter.STATE_ON; - } - - @VisibleForTesting - public boolean isAirplaneModeOn() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - } - - @VisibleForTesting - public void onAirplaneModeChanged(BluetoothManagerService managerService) { - managerService.onAirplaneModeChanged(); - } - - @VisibleForTesting - public int getSettingsInt(String name) { - return Settings.Global.getInt(mContext.getContentResolver(), - name, 0); - } - - @VisibleForTesting - public void setSettingsInt(String name, int value) { - Settings.Global.putInt(mContext.getContentResolver(), - name, value); - } - - @VisibleForTesting - public void showToastMessage() { - Resources r = mContext.getResources(); - final CharSequence text = r.getString( - R.string.bluetooth_airplane_mode_toast, 0); - Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); - } - - private boolean isA2dpConnected() { - final BluetoothA2dp a2dp = mA2dp; - if (a2dp == null) { - return false; - } - return a2dp.getConnectedDevices().size() > 0; - } - - private boolean isHearingAidConnected() { - final BluetoothHearingAid hearingAid = mHearingAid; - if (hearingAid == null) { - return false; - } - return hearingAid.getConnectedDevices().size() > 0; - } - }; } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b5d28089db5..cecf690171a 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1350,9 +1350,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } + BluetoothModeChangeHelper bluetoothModeChangeHelper = + new BluetoothModeChangeHelper(mContext); + if (mBluetoothAirplaneModeListener != null) { - mBluetoothAirplaneModeListener.start( - new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); + mBluetoothAirplaneModeListener.start(bluetoothModeChangeHelper); } } diff --git a/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java new file mode 100644 index 00000000000..242fa848c25 --- /dev/null +++ b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java @@ -0,0 +1,143 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; +import android.widget.Toast; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +/** + * Helper class that handles callout and callback methods without + * complex logic. + */ +public class BluetoothModeChangeHelper { + private volatile BluetoothA2dp mA2dp; + private volatile BluetoothHearingAid mHearingAid; + private final BluetoothAdapter mAdapter; + private final Context mContext; + + BluetoothModeChangeHelper(Context context) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; + + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, + BluetoothProfile.HEARING_AID); + } + + private final ServiceListener mProfileServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + // Setup Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = (BluetoothHearingAid) proxy; + break; + default: + break; + } + } + + @Override + public void onServiceDisconnected(int profile) { + // Clear Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = null; + break; + default: + break; + } + } + }; + + @VisibleForTesting + public boolean isA2dpOrHearingAidConnected() { + return isA2dpConnected() || isHearingAidConnected(); + } + + @VisibleForTesting + public boolean isBluetoothOn() { + final BluetoothAdapter adapter = mAdapter; + if (adapter == null) { + return false; + } + return adapter.getLeState() == BluetoothAdapter.STATE_ON; + } + + @VisibleForTesting + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + @VisibleForTesting + public void onAirplaneModeChanged(BluetoothManagerService managerService) { + managerService.onAirplaneModeChanged(); + } + + @VisibleForTesting + public int getSettingsInt(String name) { + return Settings.Global.getInt(mContext.getContentResolver(), + name, 0); + } + + @VisibleForTesting + public void setSettingsInt(String name, int value) { + Settings.Global.putInt(mContext.getContentResolver(), + name, value); + } + + @VisibleForTesting + public void showToastMessage() { + Resources r = mContext.getResources(); + final CharSequence text = r.getString( + R.string.bluetooth_airplane_mode_toast, 0); + Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); + } + + private boolean isA2dpConnected() { + final BluetoothA2dp a2dp = mA2dp; + if (a2dp == null) { + return false; + } + return a2dp.getConnectedDevices().size() > 0; + } + + private boolean isHearingAidConnected() { + final BluetoothHearingAid hearingAid = mHearingAid; + if (hearingAid == null) { + return false; + } + return hearingAid.getConnectedDevices().size() > 0; + } +} diff --git a/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java index 968a402ff3b..3ace3f4c79d 100644 --- a/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -27,8 +27,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; - import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -41,7 +39,7 @@ public class BluetoothAirplaneModeListenerTest { private Context mContext; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; private BluetoothAdapter mBluetoothAdapter; - private AirplaneModeHelper mHelper; + private BluetoothModeChangeHelper mHelper; @Mock BluetoothManagerService mBluetoothManagerService; @@ -49,7 +47,7 @@ public class BluetoothAirplaneModeListenerTest { public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); - mHelper = mock(AirplaneModeHelper.class); + mHelper = mock(BluetoothModeChangeHelper.class); when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); -- GitLab From e96443f27d5c575687adec92f7c0e01a960f13cd Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 13 Oct 2020 16:29:15 -0700 Subject: [PATCH 1196/1408] BluetoothDeviceConfigListener: Don't restart when connected Similar to airplane mode change, don't send onInitFlagsChanged to BluetoothManagerService when A2dp or Hearing aid is connected. Instead, check after 24 hours. Test: FrameworksServicesTests Test: Push bluetooth INIT_ flags Change-Id: Ib036852aa1cf8e3ddd87af7d17d16ad1c66c820e --- .../bluetooth/BluetoothManagerService.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index cecf690171a..e57d0f600c4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -115,6 +115,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Delay for retrying enable and disable in msec private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; + private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -174,6 +175,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private int mWaitForEnableRetry; private int mWaitForDisableRetry; + private BluetoothModeChangeHelper mBluetoothModeChangeHelper; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener; @@ -520,7 +523,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; - mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this); } /** @@ -1350,12 +1352,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } - BluetoothModeChangeHelper bluetoothModeChangeHelper = - new BluetoothModeChangeHelper(mContext); + mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext); if (mBluetoothAirplaneModeListener != null) { - mBluetoothAirplaneModeListener.start(bluetoothModeChangeHelper); + mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper); } + mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this); } /** @@ -2198,6 +2200,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); + break; + } if (mBluetooth != null && isEnabled()) { restartForReason( BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); -- GitLab From ba3049eb34d2c9d75aa5aa33b3783fe03f5318d6 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 15 Oct 2020 11:32:24 -0700 Subject: [PATCH 1197/1408] Bluetooth: Check INIT flag change on audio disconnect We also listen for A2DP or hearing aid disconnect and then reevaluate INIT flag change. Test: FrameworksServicesTests Test: Push bluetooth INIT_ flags Change-Id: I93fa880aa64506577a7937e6666b1942d28458ea --- .../server/bluetooth/BluetoothManagerService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index e57d0f600c4..e919f39c215 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -23,7 +23,9 @@ import android.Manifest; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; @@ -439,6 +441,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessage(msg); } } + } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) + || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { + final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, + BluetoothProfile.STATE_CONNECTED); + if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) + && state == BluetoothProfile.STATE_DISCONNECTED + && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + onInitFlagsChanged(); + } } } }; @@ -489,6 +500,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); filter.addAction(Intent.ACTION_SETTING_RESTORED); + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); -- GitLab From 10d0a560d962be20a9ce50db0bc87e0a204c9ff7 Mon Sep 17 00:00:00 2001 From: Kevin Hufnagle Date: Mon, 19 Oct 2020 21:08:17 -0400 Subject: [PATCH 1198/1408] docs: Use correct profile for intent The ACTION_AUDIO_STATE_CHANGED intent action uses the HDP profile, not the A2DP profile. Bug: 158742305 Test: m ds-docs-java Exempt-From-Owner-Approval: Docs-only change Change-Id: I4358aac8c8aa9fc8b36e517e568e5a9db538555e --- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 6ce05f984cc..e6d6e7ac5dd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -79,7 +79,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Intent used to broadcast the change in the Audio Connection state of the - * A2DP profile. + * HDP profile. * *

          This intent will have 3 extras: *

            -- GitLab From bc9a809f18a3b0ec23cbc39802fb4928c2074ea3 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Tue, 27 Oct 2020 11:47:29 +0000 Subject: [PATCH 1199/1408] 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 Change-Id: I4c8fd0006f950de9955242e93968fb0996ceb372 --- .../java/android/bluetooth/BluetoothA2dp.java | 22 ++++++++-------- .../android/bluetooth/BluetoothA2dpSink.java | 3 ++- .../android/bluetooth/BluetoothAdapter.java | 7 +++--- .../bluetooth/BluetoothCodecStatus.java | 9 ++++--- .../android/bluetooth/BluetoothDevice.java | 25 ++++++++++--------- .../java/android/bluetooth/BluetoothGatt.java | 6 ++--- .../bluetooth/BluetoothGattService.java | 3 ++- .../android/bluetooth/BluetoothHeadset.java | 13 +++++----- .../bluetooth/BluetoothHeadsetClient.java | 11 ++++---- .../bluetooth/BluetoothHeadsetClientCall.java | 11 ++++---- .../bluetooth/BluetoothHearingAid.java | 7 +++--- .../java/android/bluetooth/BluetoothMap.java | 3 ++- .../android/bluetooth/BluetoothMapClient.java | 3 ++- .../java/android/bluetooth/BluetoothPan.java | 3 ++- .../java/android/bluetooth/BluetoothPbap.java | 3 ++- .../android/bluetooth/BluetoothProfile.java | 3 ++- .../java/android/bluetooth/BluetoothSap.java | 3 ++- .../android/bluetooth/BluetoothSocket.java | 3 ++- .../java/android/bluetooth/le/ScanRecord.java | 3 ++- 19 files changed, 79 insertions(+), 62 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 5374d6d55ee..c0cb3234682 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -651,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -680,7 +680,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { @@ -710,7 +710,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); @@ -725,7 +725,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); @@ -766,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -792,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -819,7 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 53f87e6bc05..67f3d7b5d71 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -158,7 +159,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 573892bcf01..475be121b73 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -40,6 +40,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -1170,7 +1171,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disable(boolean persist) { try { @@ -1219,7 +1220,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -2625,7 +2626,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7764ebeb2e3..3a65aaa0d16 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +40,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -198,7 +199,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -208,7 +209,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -218,7 +219,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1b0fe9dc2d7..3b8dec7bf95 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.ParcelUuid; @@ -369,7 +370,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -665,7 +666,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -674,7 +675,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -689,7 +690,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -697,7 +698,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -705,7 +706,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -713,7 +714,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -722,7 +723,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -801,7 +802,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; @@ -1134,7 +1135,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; @@ -1573,7 +1574,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -2187,7 +2188,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 6d22eb93fd0..7a6ff79623a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -58,9 +58,9 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private Boolean mDeviceBusy = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -881,7 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index e7809aeb1bb..23dc7c83085 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,6 +16,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -385,7 +386,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e6d6e7ac5dd..57d1411aa68 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -112,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -635,7 +636,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -782,7 +783,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -1030,7 +1031,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1129,7 +1130,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1155,7 +1156,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 28363250ebd..e5b2a1e23cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -445,7 +446,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -471,7 +472,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -780,7 +781,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -829,7 +830,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

            */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -1014,7 +1015,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d1a096e605d..219d1596fbf 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -144,7 +145,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getId() { return mId; } @@ -164,7 +165,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getState() { return mState; } @@ -174,7 +175,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getNumber() { return mNumber; } @@ -193,7 +194,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isMultiParty() { return mMultiParty; } @@ -203,7 +204,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index fa62a02499e..ff78825e0f9 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -85,7 +86,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -302,7 +303,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -328,7 +329,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 14a71c44673..35549954007 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; @@ -209,7 +210,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index df11d3adac0..ff6cffb272a 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ce3c7d25a10..ecd718cec32 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -234,7 +235,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d58a8935019..6e5c45f3d12 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -322,7 +323,7 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 7538df8bbe5..db851c4f33b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -219,7 +220,7 @@ public interface BluetoothProfile { * * @hide **/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int PRIORITY_AUTO_CONNECT = 1000; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48e8c1ada25..0d70dbdd842 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -219,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d41a6d064d1..65381dbb237 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; @@ -111,7 +112,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index c0c1aa1634f..d9ea7d237e5 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; @@ -195,7 +196,7 @@ public final class ScanRecord { * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { return null; -- GitLab From 8206565829fb7f4025887cf1d61eeea222cd3bfe Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 21 Sep 2020 20:50:10 -0700 Subject: [PATCH 1200/1408] Move all BluetoothCodecConfig and BluetoothCodecStatus APIs moved from the non-SDK API list to the blocklist in Android 11 back to the non-SDK API list. Tag: #feature Bug: 168812851 Test: atest BluetoothHostTest#testCodecMethodsAccessible Merged-In: I29983284b1a1c271d983c99b286e204604abdc72 Change-Id: I29983284b1a1c271d983c99b286e204604abdc72 --- .../bluetooth/BluetoothCodecConfig.java | 32 ++++++++++++++++++- .../bluetooth/BluetoothCodecStatus.java | 5 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index e07bc0215a6..a52fc891790 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d5..7b567b4098e 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } -- GitLab From bddbb77484a3694357df750c5e7df9527f409c8a Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 28 Oct 2020 19:38:11 +0000 Subject: [PATCH 1201/1408] Revert "Add maxTargetSdk restriction to unused APIs." This reverts commit bc9a809f18a3b0ec23cbc39802fb4928c2074ea3. Reason for revert: Droidcop-triggered revert due to breakage https://android-build.googleplex.com/builds/quarterdeck?testMethod=testAppZygotePreload&testClass=android.app.cts.ServiceTest&atpConfigName=suite%2Ftest-mapping-presubmit-retry_cloud-tf&testModule=CtsAppTestCases&fkbb=6936597&lkbb=6936969&lkgb=6936551&testResults=true&branch=git_master&target=cf_x86_phone-userdebug>, bug b/171886397 Bug: 171886397 Change-Id: Ibe0f0430a3451477c1ee8ef56a596e91ea1e7672 --- .../java/android/bluetooth/BluetoothA2dp.java | 22 ++++++++-------- .../android/bluetooth/BluetoothA2dpSink.java | 3 +-- .../android/bluetooth/BluetoothAdapter.java | 7 +++--- .../bluetooth/BluetoothCodecStatus.java | 9 +++---- .../android/bluetooth/BluetoothDevice.java | 25 +++++++++---------- .../java/android/bluetooth/BluetoothGatt.java | 6 ++--- .../bluetooth/BluetoothGattService.java | 3 +-- .../android/bluetooth/BluetoothHeadset.java | 13 +++++----- .../bluetooth/BluetoothHeadsetClient.java | 11 ++++---- .../bluetooth/BluetoothHeadsetClientCall.java | 11 ++++---- .../bluetooth/BluetoothHearingAid.java | 7 +++--- .../java/android/bluetooth/BluetoothMap.java | 3 +-- .../android/bluetooth/BluetoothMapClient.java | 3 +-- .../java/android/bluetooth/BluetoothPan.java | 3 +-- .../java/android/bluetooth/BluetoothPbap.java | 3 +-- .../android/bluetooth/BluetoothProfile.java | 3 +-- .../java/android/bluetooth/BluetoothSap.java | 3 +-- .../android/bluetooth/BluetoothSocket.java | 3 +-- .../java/android/bluetooth/le/ScanRecord.java | 3 +-- 19 files changed, 62 insertions(+), 79 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c0cb3234682..5374d6d55ee 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -651,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -680,7 +680,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { @@ -710,7 +710,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); @@ -725,7 +725,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); @@ -766,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -792,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -819,7 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 67f3d7b5d71..53f87e6bc05 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,7 +24,6 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -159,7 +158,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 475be121b73..573892bcf01 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -40,7 +40,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -1171,7 +1170,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean disable(boolean persist) { try { @@ -1220,7 +1219,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -2626,7 +2625,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 3a65aaa0d16..7764ebeb2e3 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -40,7 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -199,7 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -209,7 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -219,7 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3b8dec7bf95..1b0fe9dc2d7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,7 +28,6 @@ import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.ParcelUuid; @@ -370,7 +369,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -666,7 +665,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -675,7 +674,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -690,7 +689,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -698,7 +697,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -706,7 +705,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -714,7 +713,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -723,7 +722,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -802,7 +801,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; @@ -1135,7 +1134,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; @@ -1574,7 +1573,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -2188,7 +2187,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 7a6ff79623a..6d22eb93fd0 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -58,9 +58,9 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage private Boolean mDeviceBusy = false; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -881,7 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 23dc7c83085..e7809aeb1bb 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,7 +16,6 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -386,7 +385,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 57d1411aa68..e6d6e7ac5dd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,7 +27,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -113,7 +112,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -636,7 +635,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -783,7 +782,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -1031,7 +1030,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1130,7 +1129,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1156,7 +1155,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index e5b2a1e23cc..28363250ebd 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -22,7 +22,6 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -446,7 +445,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -472,7 +471,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -781,7 +780,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -830,7 +829,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

            */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -1015,7 +1014,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 219d1596fbf..d1a096e605d 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -145,7 +144,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public int getId() { return mId; } @@ -165,7 +164,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public int getState() { return mState; } @@ -175,7 +174,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public String getNumber() { return mNumber; } @@ -194,7 +193,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean isMultiParty() { return mMultiParty; } @@ -204,7 +203,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index ff78825e0f9..fa62a02499e 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -26,7 +26,6 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -86,7 +85,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -303,7 +302,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -329,7 +328,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 35549954007..14a71c44673 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,7 +24,6 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; @@ -210,7 +209,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ff6cffb272a..df11d3adac0 100644 --- a/framework/java/android/bluetooth/BluetoothMapClient.java +++ b/framework/java/android/bluetooth/BluetoothMapClient.java @@ -24,7 +24,6 @@ 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; @@ -389,7 +388,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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ecd718cec32..ce3c7d25a10 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,7 +27,6 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -235,7 +234,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 6e5c45f3d12..d58a8935019 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -27,7 +27,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -323,7 +322,7 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index db851c4f33b..7538df8bbe5 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -23,7 +23,6 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -220,7 +219,7 @@ public interface BluetoothProfile { * * @hide **/ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage int PRIORITY_AUTO_CONNECT = 1000; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 0d70dbdd842..48e8c1ada25 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -21,7 +21,6 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -220,7 +219,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 65381dbb237..d41a6d064d1 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; @@ -112,7 +111,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index d9ea7d237e5..c0c1aa1634f 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothUuid; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.ParcelUuid; import android.util.ArrayMap; import android.util.Log; @@ -196,7 +195,7 @@ public final class ScanRecord { * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static ScanRecord parseFromBytes(byte[] scanRecord) { if (scanRecord == null) { return null; -- GitLab From cba870b777fc6d2a03a27758cf1db1c38556efe8 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Tue, 27 Oct 2020 11:47:29 +0000 Subject: [PATCH 1202/1408] 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. This is a resubmit of ag/12929664 with some APIs excluded that caused test failures; see bugs 171886397, 171888296, 171864568. APIs excluded: Landroid/bluetooth/le/ScanRecord;->parseFromBytes([B)Landroid/bluetooth/le/ScanRecord; Landroid/os/Process;->myPpid()I Landroid/os/SharedMemory;->getFd()I Landroid/hardware/input/InputManager;->INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH:I Bug: 170729553 Test: Treehugger Change-Id: I8285daa8530260251ecad6f3f38f98e263629ca7 --- .../java/android/bluetooth/BluetoothA2dp.java | 22 ++++++++-------- .../android/bluetooth/BluetoothA2dpSink.java | 3 ++- .../android/bluetooth/BluetoothAdapter.java | 7 +++--- .../bluetooth/BluetoothCodecStatus.java | 9 ++++--- .../android/bluetooth/BluetoothDevice.java | 25 ++++++++++--------- .../java/android/bluetooth/BluetoothGatt.java | 6 ++--- .../bluetooth/BluetoothGattService.java | 3 ++- .../android/bluetooth/BluetoothHeadset.java | 13 +++++----- .../bluetooth/BluetoothHeadsetClient.java | 11 ++++---- .../bluetooth/BluetoothHeadsetClientCall.java | 11 ++++---- .../bluetooth/BluetoothHearingAid.java | 7 +++--- .../java/android/bluetooth/BluetoothMap.java | 3 ++- .../android/bluetooth/BluetoothMapClient.java | 3 ++- .../java/android/bluetooth/BluetoothPan.java | 3 ++- .../java/android/bluetooth/BluetoothPbap.java | 3 ++- .../android/bluetooth/BluetoothProfile.java | 3 ++- .../java/android/bluetooth/BluetoothSap.java | 3 ++- .../android/bluetooth/BluetoothSocket.java | 3 ++- 18 files changed, 77 insertions(+), 61 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 5374d6d55ee..c0cb3234682 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -651,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -680,7 +680,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { @@ -710,7 +710,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); @@ -725,7 +725,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); @@ -766,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -792,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -819,7 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 53f87e6bc05..67f3d7b5d71 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -158,7 +159,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 573892bcf01..475be121b73 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -40,6 +40,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -1170,7 +1171,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disable(boolean persist) { try { @@ -1219,7 +1220,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -2625,7 +2626,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7764ebeb2e3..3a65aaa0d16 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +40,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -198,7 +199,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -208,7 +209,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -218,7 +219,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1b0fe9dc2d7..3b8dec7bf95 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.ParcelUuid; @@ -369,7 +370,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -665,7 +666,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -674,7 +675,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -689,7 +690,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -697,7 +698,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -705,7 +706,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -713,7 +714,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -722,7 +723,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -801,7 +802,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; @@ -1134,7 +1135,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; @@ -1573,7 +1574,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -2187,7 +2188,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 6d22eb93fd0..7a6ff79623a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -58,9 +58,9 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private Boolean mDeviceBusy = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -881,7 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index e7809aeb1bb..23dc7c83085 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,6 +16,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -385,7 +386,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e6d6e7ac5dd..57d1411aa68 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -112,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -635,7 +636,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -782,7 +783,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -1030,7 +1031,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1129,7 +1130,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1155,7 +1156,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 28363250ebd..e5b2a1e23cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -445,7 +446,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -471,7 +472,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -780,7 +781,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -829,7 +830,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

            */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -1014,7 +1015,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d1a096e605d..219d1596fbf 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -144,7 +145,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getId() { return mId; } @@ -164,7 +165,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getState() { return mState; } @@ -174,7 +175,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getNumber() { return mNumber; } @@ -193,7 +194,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isMultiParty() { return mMultiParty; } @@ -203,7 +204,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index fa62a02499e..ff78825e0f9 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -85,7 +86,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -302,7 +303,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -328,7 +329,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 14a71c44673..35549954007 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; @@ -209,7 +210,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index df11d3adac0..ff6cffb272a 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ce3c7d25a10..ecd718cec32 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -234,7 +235,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d58a8935019..6e5c45f3d12 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -322,7 +323,7 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 7538df8bbe5..db851c4f33b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -219,7 +220,7 @@ public interface BluetoothProfile { * * @hide **/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int PRIORITY_AUTO_CONNECT = 1000; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48e8c1ada25..0d70dbdd842 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -219,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d41a6d064d1..65381dbb237 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; @@ -111,7 +112,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; -- GitLab From 6a221b5f27e76a5d6b90eef4343625bcc3e19ca1 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 28 Oct 2020 12:12:03 -0700 Subject: [PATCH 1203/1408] Introduce a BluetoothConnectionCallback that will fire off events when a device is connected or disconnected. Tag: #feature Bug: 171902843 Test: Manual Change-Id: I194a35a69fe0c5d960ea88a1a0fa52c8df7738e2 --- .../android/bluetooth/BluetoothAdapter.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 872c3777112..7047f73393f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -686,6 +687,8 @@ public final class BluetoothAdapter { private final Map mLeScanClients; private static final Map>> sMetadataListeners = new HashMap<>(); + private final Map + mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener @@ -3538,6 +3541,133 @@ public final class BluetoothAdapter { @Nullable byte[] value); } + private final IBluetoothConnectionCallback mConnectionCallback = + new IBluetoothConnectionCallback.Stub() { + @Override + public void onDeviceConnected(BluetoothDevice device) { + for (Map.Entry callbackExecutorEntry: + mBluetoothConnectionCallbackExecutorMap.entrySet()) { + BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); + Executor executor = callbackExecutorEntry.getValue(); + executor.execute(() -> callback.onDeviceConnected(device)); + } + } + + @Override + public void onDeviceDisconnected(BluetoothDevice device) { + for (Map.Entry callbackExecutorEntry: + mBluetoothConnectionCallbackExecutorMap.entrySet()) { + BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); + Executor executor = callbackExecutorEntry.getValue(); + executor.execute(() -> callback.onDeviceDisconnected(device)); + } + } + }; + + /** + * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device + * (classic or low energy) is connected or disconnected. + * + * @param executor is the callback executor + * @param callback is the connection callback you wish to register + * @return true if the callback was registered successfully, false otherwise + * @throws IllegalArgumentException if the callback is already registered + * @hide + */ + public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull BluetoothConnectionCallback callback) { + if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); + if (callback == null) { + return false; + } + + // If the callback map is empty, we register the service-to-app callback + if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + return false; + } + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + mBluetoothConnectionCallbackExecutorMap.remove(callback); + } finally { + mServiceLock.readLock().unlock(); + } + } + + // Adds the passed in callback to our map of callbacks to executors + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { + throw new IllegalArgumentException("This callback has already been registered"); + } + mBluetoothConnectionCallbackExecutorMap.put(callback, executor); + } + + return true; + } + + /** + * Unregisters the BluetoothConnectionCallback that was previously registered by the application + * + * @param callback is the connection callback you wish to unregister + * @return true if the callback was unregistered successfully, false otherwise + * @hide + */ + public boolean unregisterBluetoothConnectionCallback( + @NonNull BluetoothConnectionCallback callback) { + if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); + if (callback == null) { + return false; + } + + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) { + return false; + } + } + + if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + return true; + } + + // If the callback map is empty, we unregister the service-to-app callback + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.unregisterBluetoothConnectionCallback(mConnectionCallback); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** + * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth + * Low Energy (BLE) device is either connected or disconnected. + * + * @hide + */ + public abstract class BluetoothConnectionCallback { + /** + * Callback triggered when a bluetooth device (classic or BLE) is connected + * @param device is the connected bluetooth device + */ + public void onDeviceConnected(BluetoothDevice device) {} + + /** + * Callback triggered when a bluetooth device (classic or BLE) is disconnected + * @param device is the disconnected bluetooth device + */ + public void onDeviceDisconnected(BluetoothDevice device) {} + } + /** * Converts old constant of priority to the new for connection policy * -- GitLab From 74b0ebeb32cd09572fa1760610ee8dd84d656a66 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Mon, 2 Nov 2020 10:29:35 +0000 Subject: [PATCH 1204/1408] 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: I8285daa8530260251ecad6f3f38f98e263629ca7 Change-Id: I626caf7c1fe46c5ab1f39c2895b42a34319f771a --- .../java/android/bluetooth/BluetoothA2dp.java | 22 ++++++++-------- .../android/bluetooth/BluetoothA2dpSink.java | 3 ++- .../android/bluetooth/BluetoothAdapter.java | 7 +++--- .../bluetooth/BluetoothCodecStatus.java | 9 ++++--- .../android/bluetooth/BluetoothDevice.java | 25 ++++++++++--------- .../java/android/bluetooth/BluetoothGatt.java | 6 ++--- .../bluetooth/BluetoothGattService.java | 3 ++- .../android/bluetooth/BluetoothHeadset.java | 13 +++++----- .../bluetooth/BluetoothHeadsetClient.java | 11 ++++---- .../bluetooth/BluetoothHeadsetClientCall.java | 11 ++++---- .../bluetooth/BluetoothHearingAid.java | 7 +++--- .../java/android/bluetooth/BluetoothMap.java | 3 ++- .../android/bluetooth/BluetoothMapClient.java | 3 ++- .../java/android/bluetooth/BluetoothPan.java | 3 ++- .../java/android/bluetooth/BluetoothPbap.java | 3 ++- .../android/bluetooth/BluetoothProfile.java | 3 ++- .../java/android/bluetooth/BluetoothSap.java | 3 ++- .../android/bluetooth/BluetoothSocket.java | 3 ++- 18 files changed, 77 insertions(+), 61 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 5374d6d55ee..c0cb3234682 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -651,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -680,7 +680,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { @@ -710,7 +710,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); @@ -725,7 +725,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); @@ -766,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -792,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -819,7 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 53f87e6bc05..67f3d7b5d71 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -158,7 +159,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7047f73393f..2dfbb3ae81e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -41,6 +41,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -1173,7 +1174,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disable(boolean persist) { try { @@ -1222,7 +1223,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -2594,7 +2595,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7b567b4098e..f43a9e8cab9 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +40,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -198,7 +199,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -208,7 +209,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -218,7 +219,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cf8cdd4ea9d..0f864a81c14 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.ParcelUuid; @@ -349,7 +350,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -645,7 +646,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -654,7 +655,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -669,7 +670,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -677,7 +678,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -685,7 +686,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -693,7 +694,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -702,7 +703,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -781,7 +782,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; @@ -1102,7 +1103,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; @@ -1541,7 +1542,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -2155,7 +2156,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 6d22eb93fd0..7a6ff79623a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -58,9 +58,9 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private Boolean mDeviceBusy = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -881,7 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index e7809aeb1bb..23dc7c83085 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,6 +16,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -385,7 +386,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index e6d6e7ac5dd..57d1411aa68 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -112,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -635,7 +636,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -782,7 +783,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -1030,7 +1031,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1129,7 +1130,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1155,7 +1156,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 28363250ebd..e5b2a1e23cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -445,7 +446,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -471,7 +472,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -780,7 +781,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -829,7 +830,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

            */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -1014,7 +1015,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d1a096e605d..219d1596fbf 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -144,7 +145,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getId() { return mId; } @@ -164,7 +165,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getState() { return mState; } @@ -174,7 +175,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getNumber() { return mNumber; } @@ -193,7 +194,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isMultiParty() { return mMultiParty; } @@ -203,7 +204,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index fa62a02499e..ff78825e0f9 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -85,7 +86,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -302,7 +303,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -328,7 +329,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 14a71c44673..35549954007 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; @@ -209,7 +210,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index df11d3adac0..ff6cffb272a 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4698b077ff5..f06dad8232f 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -234,7 +235,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d58a8935019..6e5c45f3d12 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -322,7 +323,7 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 7538df8bbe5..db851c4f33b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -219,7 +220,7 @@ public interface BluetoothProfile { * * @hide **/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int PRIORITY_AUTO_CONNECT = 1000; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48e8c1ada25..0d70dbdd842 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -219,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d41a6d064d1..65381dbb237 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; @@ -111,7 +112,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; -- GitLab From 049f0f5f4196aa3c1897bcbfd1a86c66ab1affcc Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 4 Nov 2020 09:29:36 +0000 Subject: [PATCH 1205/1408] 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 --- .../java/android/bluetooth/BluetoothA2dp.java | 22 ++++++++-------- .../android/bluetooth/BluetoothA2dpSink.java | 3 ++- .../android/bluetooth/BluetoothAdapter.java | 7 +++--- .../android/bluetooth/BluetoothDevice.java | 25 ++++++++++--------- .../java/android/bluetooth/BluetoothGatt.java | 6 ++--- .../bluetooth/BluetoothGattService.java | 3 ++- .../android/bluetooth/BluetoothHeadset.java | 13 +++++----- .../bluetooth/BluetoothHeadsetClient.java | 11 ++++---- .../bluetooth/BluetoothHeadsetClientCall.java | 11 ++++---- .../bluetooth/BluetoothHearingAid.java | 7 +++--- .../java/android/bluetooth/BluetoothMap.java | 3 ++- .../android/bluetooth/BluetoothMapClient.java | 3 ++- .../java/android/bluetooth/BluetoothPan.java | 3 ++- .../java/android/bluetooth/BluetoothPbap.java | 3 ++- .../android/bluetooth/BluetoothProfile.java | 3 ++- .../java/android/bluetooth/BluetoothSap.java | 3 ++- .../android/bluetooth/BluetoothSocket.java | 3 ++- 17 files changed, 72 insertions(+), 57 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 5374d6d55ee..c0cb3234682 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -651,7 +651,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -680,7 +680,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { @@ -710,7 +710,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); @@ -725,7 +725,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); @@ -766,7 +766,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_SUPPORTED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { @@ -792,7 +792,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { @@ -819,7 +819,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * OPTIONAL_CODECS_PREF_DISABLED. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 53f87e6bc05..67f3d7b5d71 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -158,7 +159,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7047f73393f..2dfbb3ae81e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -41,6 +41,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -1173,7 +1174,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disable(boolean persist) { try { @@ -1222,7 +1223,7 @@ public final class BluetoothAdapter { * @return true to indicate that the config file was successfully cleared * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { @@ -2594,7 +2595,7 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cf8cdd4ea9d..0f864a81c14 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -28,6 +28,7 @@ import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.ParcelUuid; @@ -349,7 +350,7 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; @@ -645,7 +646,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_FAILED = 1; /** @@ -654,7 +655,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_REJECTED = 2; /** @@ -669,7 +670,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; /** @@ -677,7 +678,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; /** @@ -685,7 +686,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; /** @@ -693,7 +694,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** @@ -702,7 +703,7 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; /** @@ -781,7 +782,7 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.extra.SDP_RECORD"; /** @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String EXTRA_SDP_SEARCH_STATUS = "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; @@ -1102,7 +1103,7 @@ public final class BluetoothDevice implements Parcelable { * @return true on success, false on error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { final IBluetooth service = sService; @@ -1541,7 +1542,7 @@ public final class BluetoothDevice implements Parcelable { * @return true pin has been set false for error * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); @@ -2155,7 +2156,7 @@ public final class BluetoothDevice implements Parcelable { * operations. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 6d22eb93fd0..7a6ff79623a 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -58,9 +58,9 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private Boolean mDeviceBusy = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private int mTransport; private int mPhy; private boolean mOpportunistic; @@ -881,7 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index e7809aeb1bb..23dc7c83085 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -16,6 +16,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -385,7 +386,7 @@ public class BluetoothGattService implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void setAdvertisePreferred(boolean advertisePreferred) { mAdvertisePreferred = advertisePreferred; } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 6ce05f984cc..adb7e2f773a 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -112,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -635,7 +636,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return priority of the device * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -782,7 +783,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -1030,7 +1031,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1129,7 +1130,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1155,7 +1156,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 28363250ebd..e5b2a1e23cc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -445,7 +446,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -471,7 +472,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -780,7 +781,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -829,7 +830,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not * supported.

            */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -1014,7 +1015,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * * Note: This is an internal function and shouldn't be exposed */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index d1a096e605d..219d1596fbf 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -144,7 +145,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return call id. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getId() { return mId; } @@ -164,7 +165,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return state of this particular phone call. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getState() { return mState; } @@ -174,7 +175,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return string representing phone number. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getNumber() { return mNumber; } @@ -193,7 +194,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if call is a multi party call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isMultiParty() { return mMultiParty; } @@ -203,7 +204,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { * * @return true if its outgoing call, false otherwise. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isOutgoing() { return mOutgoing; } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index fa62a02499e..ff78825e0f9 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -85,7 +86,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -302,7 +303,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -328,7 +329,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * is not active, it will be null on that position. Returns empty list on error. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 14a71c44673..35549954007 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; @@ -209,7 +210,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index df11d3adac0..ff6cffb272a 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); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 4698b077ff5..f06dad8232f 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -234,7 +235,7 @@ public final class BluetoothPan implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index d58a8935019..6e5c45f3d12 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -322,7 +323,7 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 7538df8bbe5..db851c4f33b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -219,7 +220,7 @@ public interface BluetoothProfile { * * @hide **/ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int PRIORITY_AUTO_CONNECT = 1000; /** diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 48e8c1ada25..0d70dbdd842 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -219,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return false on error, true otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index d41a6d064d1..65381dbb237 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; @@ -111,7 +112,7 @@ public final class BluetoothSocket implements Closeable { public static final int TYPE_L2CAP_LE = 4; /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) /*package*/ static final int EADDRINUSE = 98; /*package*/ static final int SEC_FLAG_ENCRYPT = 1; -- GitLab From e4a729947ee7a9269f873e2bf943c449bcfaf910 Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Mon, 9 Nov 2020 16:44:29 -0800 Subject: [PATCH 1206/1408] Move UserManagerInternal out of android.os Moving this class out of android.os will reportedly greatly reduce ART overhead. Test: compiles Test: atest FrameworksServicesTests FrameworksCoreTests Bug: 165817914 Exempt-From-Owner-Approval: no-op refactoring Change-Id: I1f3e77120979e2f336e5f42e4ec6e459d6492083 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f6a29aa917f..0a684287849 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -63,8 +63,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.os.UserManagerInternal; -import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -77,6 +75,8 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; -- GitLab From b91f59cfb53b0821df3f69cd60f635f9c21db4c6 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 4 Nov 2020 09:42:27 +0000 Subject: [PATCH 1207/1408] De-restrict BluetoothCodecStatus hidden APIs. They were erroneously included in b/170729553 but should not have been. Tag: #feature Test: Manual Change-Id: Ia3bd3b55de19a89d41a025dc0e03dec850c1ab07 --- .../java/android/bluetooth/BluetoothCodecStatus.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 3a65aaa0d16..7764ebeb2e3 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -40,7 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -199,7 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -209,7 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -219,7 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } -- GitLab From 5c0baa9199db3f440d1f8485a6ed732f6d9b93df Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 21 Sep 2020 20:50:10 -0700 Subject: [PATCH 1208/1408] Move all BluetoothCodecConfig and BluetoothCodecStatus APIs moved from the non-SDK API list to the blocklist in Android 11 back to the non-SDK API list. NoNonSdkCheck: Intentional revert of an app breaking API change Tag: #feature Bug: 168812851 Test: atest BluetoothHostTest#testCodecMethodsAccessible Change-Id: I29983284b1a1c271d983c99b286e204604abdc72 Merged-In: I29983284b1a1c271d983c99b286e204604abdc72 --- .../bluetooth/BluetoothCodecConfig.java | 32 ++++++++++++++++++- .../bluetooth/BluetoothCodecStatus.java | 5 +++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index d2a15357aa1..735980beebb 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_SBC = 0; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_AAC = 1; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX = 2; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_LDAC = 4; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_MAX = 5; - + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; /** @hide */ @@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DISABLED = -1; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_DEFAULT = 0; + @UnsupportedAppUsage public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} + @UnsupportedAppUsage public static final int SAMPLE_RATE_NONE = 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_44100 = 0x1 << 0; + @UnsupportedAppUsage public static final int SAMPLE_RATE_48000 = 0x1 << 1; + @UnsupportedAppUsage public static final int SAMPLE_RATE_88200 = 0x1 << 2; + @UnsupportedAppUsage public static final int SAMPLE_RATE_96000 = 0x1 << 3; + @UnsupportedAppUsage public static final int SAMPLE_RATE_176400 = 0x1 << 4; + @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; @@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_NONE = 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; + @UnsupportedAppUsage public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; @@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} + @UnsupportedAppUsage public static final int CHANNEL_MODE_NONE = 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_MONO = 0x1 << 0; + @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @ChannelMode int channelMode, long codecSpecific1, @@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } + @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType) { mCodecType = codecType; mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; @@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec type */ + @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } @@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec priority */ + @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } @@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec sample rate */ + @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } @@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return the codec bits per sample */ + @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } @@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable { * * @return a codec specific value1. */ + @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 1e394b830d5..7b567b4098e 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } -- GitLab From 3b98ee1de2c7c643c37ed67c9c214b65c8313ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Mon, 16 Nov 2020 11:43:45 +0000 Subject: [PATCH 1209/1408] Add Bluetooth LE Audio Profile (3/3) This is boilerplate code for Bluetooth LE Audio profile Bug: 150670922 Test: compilation Tag: #feature Change-Id: Iadc3af12fd8b2808db2f4e933a1906a819824ade --- .../android/bluetooth/BluetoothLeAudio.java | 451 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- 2 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeAudio.java diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java new file mode 100644 index 00000000000..3f00fa6f418 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -0,0 +1,451 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the LeAudio profile. + * + *

            BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothLeAudio proxy object. + * + *

            Android only supports one set of connected Bluetooth LeAudio device at a time. Each + * method is protected with its appropriate permission. + */ +public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothLeAudio"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * Intent used to broadcast the change in connection state of the LeAudio + * profile. Please note that in the binaural case, there will be two different LE devices for + * the left and right side and each device will have their own connection state changes. + * + *

            This intent will have 3 extras: + *

              + *
            • {@link #EXTRA_STATE} - The current state of the profile.
            • + *
            • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
            • + *
            • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
            • + *
            + * + *

            {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the selection of a connected device as active. + * + *

            This intent will have one extra: + *

              + *
            • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
            • + *
            + * + *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", + IBluetoothLeAudio.class.getName()) { + @Override + public IBluetoothLeAudio getServiceInterface(IBinder service) { + return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothLeAudio proxy object for interacting with the local + * Bluetooth LeAudio service. + */ + /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothLeAudio getService() { + return mProfileConnector.getService(); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + *

            This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean connect(@Nullable BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.connect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Initiate disconnection from a profile + * + *

            This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + *

            If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean disconnect(@Nullable BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.disconnect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getConnectedDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + + /** + * {@inheritDoc} + */ + @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionState(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, LeAudio audio + * streaming is to the active LeAudio device. If a remote device + * is not connected, it cannot be selected as active. + * + *

            This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_LEAUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && ((device == null) || isValidDevice(device))) { + service.setActiveDevice(device); + return true; + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connected LeAudio devices that are active + * + * @return the list of active devices. Returns empty list on error. + * @hide + */ + @NonNull + @RequiresPermission(Manifest.permission.BLUETOOTH) + public List getActiveDevices() { + if (VDBG) log("getActiveDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getActiveDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + + /** + * Get device group id. Devices with same group id belong to same group (i.e left and right + * earbud) + * @param device LE Audio capable device + * @return group id that this device currently belongs to + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getGroupId(@NonNull BluetoothDevice device) { + if (VDBG) log("getGroupId()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getGroupId(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return GROUP_ID_INVALID; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return GROUP_ID_INVALID; + } + } + + /** + * 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 + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * 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 + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionPolicy(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + default: + return ""; + } + } + + private boolean isValidDevice(@Nullable BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index db851c4f33b..c31b04e8145 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -206,13 +206,20 @@ public interface BluetoothProfile { */ int HEARING_AID = 21; + /** + * LE Audio Device + * + * @hide + */ + int LE_AUDIO = 22; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 21; + int MAX_PROFILE_ID = 22; /** * Default priority for devices that we try to auto-connect to and -- GitLab From a5e5c2b032748d60d5964ef9275978bd451ab654 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Wed, 2 Dec 2020 20:48:08 -0800 Subject: [PATCH 1210/1408] BluetoothClass: Use mask to get MajorDeviceClass Bug: 174650196 Test: compiles Change-Id: I6042a02fa329c5291aa7b9719907485e748582bf --- framework/java/android/bluetooth/BluetoothClass.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 905b0ceec4e..1e92fc0568e 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -425,13 +425,13 @@ public final class BluetoothClass implements Parcelable { return false; } } else if (profile == PROFILE_HID) { - return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL; + return getMajorDeviceClass() == Device.Major.PERIPHERAL; } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) { // No good way to distinguish between the two, based on class bits. if (hasService(Service.NETWORKING)) { return true; } - return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING; + return getMajorDeviceClass() == Device.Major.NETWORKING; } else { return false; } -- GitLab From 9544cacc28a304dcc10b2069f8e547c05d592b4b Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Wed, 2 Dec 2020 12:21:36 -0800 Subject: [PATCH 1211/1408] Store CDM device profile and apply role when device is connected Test: manual - ensure role privileges are granted/revoked when device is connected/disconnected Bug: 165951651 Change-Id: Id24a4b3a3510781d9105763b1722f44583a7fd7c --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c07cd52c581..1713a0c158c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3689,7 +3689,7 @@ public final class BluetoothAdapter { * * @hide */ - public abstract class BluetoothConnectionCallback { + public abstract static class BluetoothConnectionCallback { /** * Callback triggered when a bluetooth device (classic or BLE) is connected * @param device is the connected bluetooth device -- GitLab From e8a3cc80b85bcc8b81fb3d40defd4cc894f4314f Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 8 Dec 2020 08:35:54 -0700 Subject: [PATCH 1212/1408] Improve OWNERS coverage across frameworks/base/. As general background, OWNERS files expedite code reviews by helping code authors quickly find relevant reviewers, and they also ensure that stakeholders are involved in code changes in their areas. Some teams under frameworks/base/ have been using OWNERS files successfully for many years, and we're ready to expand them to cover more areas. Here's the historical coverage statistics for the last two years of changes before these new OWNERS changes land: -- 56% of changes are fully covered by OWNERS -- 17% of changes are partially covered by OWNERS -- 25% of changes have no OWNERS coverage Working closely with team leads, we've now identified clear OWNERS on a per-package basis, and we're using "include" directives whenever possible to to simplify future maintenance. With this extensive effort, we've now improved our coverage as follows: -- 98% of changes are fully covered by OWNERS -- 1% of changes are partially covered by OWNERS -- 1% of changes have no OWNERS coverage This specific change is automatically generated by a script from detailed ownership information confirmed by team leads. Bug: 174932174 Test: manual Exempt-From-Owner-Approval: refactoring with team leads buy-in Merged-In: I9789c97c1de8e5d962b48c29c57d82fe83729eba Change-Id: I9789c97c1de8e5d962b48c29c57d82fe83729eba --- framework/java/android/bluetooth/OWNERS | 4 ++++ framework/java/android/bluetooth/le/OWNERS | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 framework/java/android/bluetooth/OWNERS create mode 100644 framework/java/android/bluetooth/le/OWNERS diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS new file mode 100644 index 00000000000..3523ee0640a --- /dev/null +++ b/framework/java/android/bluetooth/OWNERS @@ -0,0 +1,4 @@ +# Bug component: 27441 + +zachoverflow@google.com +siyuanh@google.com diff --git a/framework/java/android/bluetooth/le/OWNERS b/framework/java/android/bluetooth/le/OWNERS new file mode 100644 index 00000000000..3523ee0640a --- /dev/null +++ b/framework/java/android/bluetooth/le/OWNERS @@ -0,0 +1,4 @@ +# Bug component: 27441 + +zachoverflow@google.com +siyuanh@google.com -- GitLab From ee7c90a5f4f7d7bc18a29c58313759c34daa5e36 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 8 Dec 2020 08:36:17 -0700 Subject: [PATCH 1213/1408] Improve OWNERS coverage across frameworks/base/. As general background, OWNERS files expedite code reviews by helping code authors quickly find relevant reviewers, and they also ensure that stakeholders are involved in code changes in their areas. Some teams under frameworks/base/ have been using OWNERS files successfully for many years, and we're ready to expand them to cover more areas. Here's the historical coverage statistics for the last two years of changes before these new OWNERS changes land: -- 56% of changes are fully covered by OWNERS -- 17% of changes are partially covered by OWNERS -- 25% of changes have no OWNERS coverage Working closely with team leads, we've now identified clear OWNERS on a per-package basis, and we're using "include" directives whenever possible to to simplify future maintenance. With this extensive effort, we've now improved our coverage as follows: -- 98% of changes are fully covered by OWNERS -- 1% of changes are partially covered by OWNERS -- 1% of changes have no OWNERS coverage This specific change is automatically generated by a script that identifies relevant "include" directives. Bug: 174932174 Test: manual Exempt-From-Owner-Approval: refactoring with team leads buy-in Merged-In: I3480ddf2fe7ba3dfb922b459d4da01fa17a2c813 Change-Id: I3480ddf2fe7ba3dfb922b459d4da01fa17a2c813 --- framework/tests/OWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 framework/tests/OWNERS diff --git a/framework/tests/OWNERS b/framework/tests/OWNERS new file mode 100644 index 00000000000..98bb8771620 --- /dev/null +++ b/framework/tests/OWNERS @@ -0,0 +1 @@ +include /core/java/android/bluetooth/OWNERS -- GitLab From a107eae7e921aa56d271bc531dbc11d55b4abd0b Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 9 Dec 2020 14:25:15 -0800 Subject: [PATCH 1214/1408] Bluetooth airplane listener: Check for null Check mAirplaneHelper != null before accessing it. Note that this can be null if Settings.Global.RADIO_BLUETOOTH is enabled in AIRPLANE_MODE_RADIOS. We don't need to disable Bluetooth if it's allowed in airplane mode. Test: atest FrameworksServicesTests Bug: 174254527 Change-Id: I93ae4ec6b75b7ffb8e75848d7779ab64593221bd --- .../server/bluetooth/BluetoothAirplaneModeListener.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index 4d9680c785b..aa56da5773e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -119,7 +119,9 @@ class BluetoothAirplaneModeListener { } return; } - mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + if (mAirplaneHelper != null) { + mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + } } @VisibleForTesting -- GitLab From 7113f77d5c5bc9d350ae10d6e50641ecd026f149 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 10 Dec 2020 13:01:26 -0800 Subject: [PATCH 1215/1408] Bluetooth: Check state correctly when client wants to bind Use mState instead of mEnable to check Bluetooth server status. mEnable is used to indicate whether the next action is enabling or disabling the Bluetooth stack, whereas mState indicates the current state. Test: atest FrameworksServicesTests Bug: 173941366 Change-Id: Id85494f7f40a0201061e74fbf3f08f70a632a62c --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0b8b3526400..aab05532e2b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1265,7 +1265,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public boolean bindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { - if (!mEnable) { + if (mState != BluetoothAdapter.STATE_ON) { if (DBG) { Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile + ", while Bluetooth was disabled"); @@ -1431,7 +1431,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - if (!mEnable || state != BluetoothAdapter.STATE_ON) { + if (state != BluetoothAdapter.STATE_ON) { if (DBG) { Slog.d(TAG, "Unable to bindService while Bluetooth is disabled"); } -- GitLab From 302446de37dde1062a543c97a5b8e8cdc3c08407 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 2 Dec 2020 21:12:08 +0100 Subject: [PATCH 1216/1408] Add LE Audio UUID placeholder Actual value will will be added in the future. We already have to reference it in the source. Bug: 150670922 Test: compilation Tag: #feature Sponsor: jpawlowski@ Change-Id: I3ba1e0e1136346e469952568f5a409cf32b898be --- framework/java/android/bluetooth/BluetoothUuid.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index e274af1b5c5..56c48247100 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -153,7 +153,12 @@ public final class BluetoothUuid { @SystemApi public static final ParcelUuid HEARING_AID = ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); - + /** Placeholder until specification is released + * @hide */ + @NonNull + @SystemApi + public static final ParcelUuid LE_AUDIO = + ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE"); /** @hide */ @NonNull @SystemApi -- GitLab From ff82368c84623a778a898525f63f55f894f24598 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Fri, 11 Dec 2020 17:39:35 -0800 Subject: [PATCH 1217/1408] Bluetooth: Check state correctly when client wants to bind Use mState instead of mEnable to check Bluetooth server status. mEnable is used to indicate whether the next action is enabling or disabling the Bluetooth stack, whereas mState indicates the current state. Test: atest FrameworksServicesTests Bug: 173941366 Change-Id: I335f1e33fe84a9dd7e9227dd55d54fcae7c0ec4b --- .../com/android/server/bluetooth/BluetoothManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8c38b66dd44..9ce7cf27f9f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1236,7 +1236,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public boolean bindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { - if (!mEnable) { + if (mState != BluetoothAdapter.STATE_ON) { if (DBG) { Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile + ", while Bluetooth was disabled"); @@ -1400,7 +1400,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - if (!mEnable || state != BluetoothAdapter.STATE_ON) { + if (state != BluetoothAdapter.STATE_ON) { if (DBG) { Slog.d(TAG, "Unable to bindService while Bluetooth is disabled"); } -- GitLab From 00040ba31b89439603026638acd511ade955e543 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 17 Dec 2020 12:10:51 -0800 Subject: [PATCH 1218/1408] Register the service-to-app callback whenever the service comes up for BluetoothConnectionCallback. This ensures that if the bluetooth process dies, the callbacks will be re-established once it comes back up. Tag: #feature Bug: 175700972 Test: Manual Change-Id: I2a20b229fcc1d8f69ccc1b24b62c26ce8155c29f --- .../android/bluetooth/BluetoothAdapter.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2dfbb3ae81e..e4b2d7075d4 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2933,6 +2933,16 @@ public final class BluetoothAdapter { } }); } + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mService.registerBluetoothConnectionCallback(mConnectionCallback); + } catch (RemoteException e) { + Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" + + "connection callback", e); + } + } + } } public void onBluetoothServiceDown() { @@ -3582,25 +3592,25 @@ public final class BluetoothAdapter { return false; } - // If the callback map is empty, we register the service-to-app callback - if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { - return false; + synchronized (mBluetoothConnectionCallbackExecutorMap) { + // If the callback map is empty, we register the service-to-app callback + if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + return false; + } } + } catch (RemoteException e) { + Log.e(TAG, "", e); + mBluetoothConnectionCallbackExecutorMap.remove(callback); + } finally { + mServiceLock.readLock().unlock(); } - } catch (RemoteException e) { - Log.e(TAG, "", e); - mBluetoothConnectionCallbackExecutorMap.remove(callback); - } finally { - mServiceLock.readLock().unlock(); } - } - // Adds the passed in callback to our map of callbacks to executors - synchronized (mBluetoothConnectionCallbackExecutorMap) { + // Adds the passed in callback to our map of callbacks to executors if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { throw new IllegalArgumentException("This callback has already been registered"); } -- GitLab From c611e73406878907e7b216379f5ce3de42b8fe36 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 14 Dec 2020 10:54:45 -0800 Subject: [PATCH 1219/1408] Introduce public APIs to check whether a remote bluetooth headset supports voice recognition as well as echo cancellation and/or noise reduction via the AT+BRSF bitmask Tag: #feature Bug: 172960943 Test: Manual Change-Id: I40579d9b6d493d2b32fb260983eeb7c79cc0d525 --- .../android/bluetooth/BluetoothHeadset.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index adb7e2f773a..f59ae338ee2 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -681,6 +681,48 @@ public final class BluetoothHeadset implements BluetoothProfile { return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } + /** + * Checks whether the headset supports some form of noise reduction + * + * @param device Bluetooth device + * @return true if echo cancellation and/or noise reduction is supported, false otherwise + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) { + if (DBG) log("isNoiseReductionSupported()"); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.isNoiseReductionSupported(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Checks whether the headset supports voice recognition + * + * @param device Bluetooth device + * @return true if voice recognition is supported, false otherwise + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) { + if (DBG) log("isVoiceRecognitionSupported()"); + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.isVoiceRecognitionSupported(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** * Start Bluetooth voice recognition. This methods sends the voice * recognition AT command to the headset and establishes the -- GitLab From b426f901449bce7e901e18fc9d7450a07180dd84 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Wed, 6 Jan 2021 12:05:47 +0000 Subject: [PATCH 1220/1408] Derestrict some non-SDK APIs. Per request from a partner, these APIs will not be restricted as they are in use. This is conceptually a partial revert of change 049f0f5f419. NoNonSdkCheck: b/170729553 Bug: 171933273 Test: Treehugger Change-Id: Ibb525e9a9e2fc90248b74f45f3cdcb0be7487c3a --- framework/java/android/bluetooth/BluetoothA2dp.java | 6 +++--- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- framework/java/android/bluetooth/BluetoothHeadset.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c0cb3234682..15daf1c59d1 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e4b2d7075d4..b7203e3e36b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1174,7 +1174,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean disable(boolean persist) { try { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index adb7e2f773a..fdfc6beca3f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -113,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -1130,7 +1130,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1156,7 +1156,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { -- GitLab From db415b26c8068ae52be23a21d2efdbbdbc9e055b Mon Sep 17 00:00:00 2001 From: Fei Zheng Date: Wed, 25 Dec 2019 11:43:18 +0800 Subject: [PATCH 1221/1408] Bluetooth: Implement API to get device identification information Submitted on behalf of a third-party: Samsung System LSI License rights, if any, to the submission are granted solely by the copyright owner of such submission under its applicable intellectual property. Copyright (C) 2015 Samsung System LSI 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. Third Party code includes additions/modifications from Qualcomm Innovation Center, Inc. Call BluetoothDevice.sdpSearch(BluetoothUuid.DIP) to search DIP information, and receive intent BluetoothDevice.ACTION_SDP_RECORD to get SdpDipRecord from BluetoothDevice.EXTRA_SDP_RECORD Bug: 141666056 Test: atest BluetoothInstrumentationTests Sponsor: alainv@ Tag: #feature Change-Id: I82d0979b77d28ec36fde6616622d216a28310b4a --- .../java/android/bluetooth/BluetoothUuid.java | 5 + .../java/android/bluetooth/SdpDipRecord.java | 104 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 framework/java/android/bluetooth/SdpDipRecord.java diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 56c48247100..c0736a6b7bb 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -162,6 +162,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid DIP = + ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); diff --git a/framework/java/android/bluetooth/SdpDipRecord.java b/framework/java/android/bluetooth/SdpDipRecord.java new file mode 100644 index 00000000000..84b0eef0593 --- /dev/null +++ b/framework/java/android/bluetooth/SdpDipRecord.java @@ -0,0 +1,104 @@ +/* +* Copyright (C) 2015 Samsung System LSI +* 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 java.util.Arrays; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data representation of a Object Push Profile Server side SDP record. + */ +/** @hide */ +public class SdpDipRecord implements Parcelable { + private final int mSpecificationId; + private final int mVendorId; + private final int mVendorIdSource; + private final int mProductId; + private final int mVersion; + private final boolean mPrimaryRecord; + + public SdpDipRecord(int specificationId, + int vendorId, int vendorIdSource, + int productId, int version, + boolean primaryRecord) { + super(); + this.mSpecificationId = specificationId; + this.mVendorId = vendorId; + this.mVendorIdSource = vendorIdSource; + this.mProductId = productId; + this.mVersion = version; + this.mPrimaryRecord = primaryRecord; + } + + public SdpDipRecord(Parcel in) { + this.mSpecificationId = in.readInt(); + this.mVendorId = in.readInt(); + this.mVendorIdSource = in.readInt(); + this.mProductId = in.readInt(); + this.mVersion = in.readInt(); + this.mPrimaryRecord = in.readBoolean(); + } + + public int getSpecificationId() { + return mSpecificationId; + } + + public int getVendorId() { + return mVendorId; + } + + public int getVendorIdSource() { + return mVendorIdSource; + } + + public int getProductId() { + return mProductId; + } + + public int getVersion() { + return mVersion; + } + + public boolean getPrimaryRecord() { + return mPrimaryRecord; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSpecificationId); + dest.writeInt(mVendorId); + dest.writeInt(mVendorIdSource); + dest.writeInt(mProductId); + dest.writeInt(mVersion); + dest.writeBoolean(mPrimaryRecord); + } + + @Override + public int describeContents() { + /* No special objects */ + return 0; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SdpDipRecord createFromParcel(Parcel in) { + return new SdpDipRecord(in); + } + public SdpDipRecord[] newArray(int size) { + return new SdpDipRecord[size]; + } + }; +} -- GitLab From 5d55645dbd60248cb7437c543f4631d3dbbdb243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Thu, 23 Jul 2020 14:57:36 +0200 Subject: [PATCH 1222/1408] gatt: Allow to set eatt support With this patch it is possible to enable eatt_support as a GATT Client or GATT Server. Tag: #feature Bug: 159786353 Test: manually verified against device supporting EATT Sponsor: jpawlowski@ Change-Id: I6835a2bbd1b0ab9d6d64ee2bac5dfc96c0563afd --- .../java/android/bluetooth/BluetoothGatt.java | 21 +++++++++- .../bluetooth/BluetoothGattServer.java | 21 +++++++++- .../android/bluetooth/BluetoothManager.java | 41 ++++++++++++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 7a6ff79623a..381318b26da 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -824,6 +824,25 @@ public final class BluetoothGatt implements BluetoothProfile { * error */ private boolean registerApp(BluetoothGattCallback callback, Handler handler) { + return registerApp(callback, handler, false); + } + + /** + * Register an application callback to start using GATT. + * + *

            This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + * is used to notify success or failure if the function returns true. + * + *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicate to allow for eatt support + * @return If true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + private boolean registerApp(BluetoothGattCallback callback, Handler handler, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -833,7 +852,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 13b1b4f93cf..088b0169b63 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -443,6 +443,25 @@ public final class BluetoothGattServer implements BluetoothProfile { * error */ /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + return registerCallback(callback, false); + } + + /** + * Register an application callback to start using GattServer. + * + *

            This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicates if server can use eatt + * @return true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -459,7 +478,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback = callback; try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); mCallback = null; diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 3b4fe0a30b8..d5c1c3e2d61 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -216,6 +216,24 @@ public final class BluetoothManager { return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO)); } + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, boolean eatt_support) { + return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support)); + } + /** * Open a GATT Server * The callback is used to deliver results to Caller, such as connection status as well @@ -233,6 +251,27 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport) { + return (openGattServer(context, callback, transport, false)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, int transport, boolean eatt_support) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -248,7 +287,7 @@ public final class BluetoothManager { return null; } BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); - Boolean regStatus = mGattServer.registerCallback(callback); + Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { Log.e(TAG, "", e); -- GitLab From 1f7f176260b932ae26e0bd3e44d86fb03c8d829c Mon Sep 17 00:00:00 2001 From: More Kuo Date: Thu, 13 Aug 2020 14:35:26 +0800 Subject: [PATCH 1223/1408] Dynamic Audio Buffer (1/3) Add APIs in BluetoothA2dp to: 1. Get the supported type of the dynamic audio buffer. 2. Get the dynamic audio buffer millis. 3. Set dynamic audio buffer millis. Add BufferConstraints class to access the buffer millis of different codec. Bug: 163968696 Tag: #feature Test: Run dynamic audio buffer test Change-Id: If097d94be861dbb0c4d8f67fe7caf70a99093e34 --- .../java/android/bluetooth/BluetoothA2dp.java | 114 ++++++++++++++++++ .../android/bluetooth/BufferConstraint.java | 105 ++++++++++++++++ .../android/bluetooth/BufferConstraints.java | 96 +++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 framework/java/android/bluetooth/BufferConstraint.java create mode 100644 framework/java/android/bluetooth/BufferConstraints.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c0cb3234682..53cd8d5218f 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -225,6 +225,39 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; + /** @hide */ + @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = { + DYNAMIC_BUFFER_SUPPORT_NONE, + DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD, + DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** + * Indicates the supported type of Dynamic Audio Buffer is not supported. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; + + /** + * Indicates the supported type of Dynamic Audio Buffer is A2DP offload. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; + + /** + * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", @@ -844,6 +877,87 @@ public final class BluetoothA2dp implements BluetoothProfile { } } + /** + * Get the supported type of the Dynamic Audio Buffer. + *

            Possible return values are + * {@link #DYNAMIC_BUFFER_SUPPORT_NONE}, + * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD}, + * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}. + * + * @return supported type of Dynamic Audio Buffer feature + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @Type int getDynamicBufferSupport() { + if (VDBG) log("getDynamicBufferSupport()"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDynamicBufferSupport(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return DYNAMIC_BUFFER_SUPPORT_NONE; + } catch (RemoteException e) { + Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e); + return DYNAMIC_BUFFER_SUPPORT_NONE; + } + } + + /** + * Return the record of {@link BufferConstraints} object that + * has the default/maximum/minimum audio buffer. This can be used to inform what the controller + * has support for the audio buffer. + * + * @return a record with {@link BufferConstraints} or null if report is unavailable + * or unsupported + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @Nullable BufferConstraints getBufferConstraints() { + if (VDBG) log("getBufferConstraints()"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getBufferConstraints(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + + /** + * Set Dynamic Audio Buffer Size. + * + * @param codec audio codec + * @param value buffer millis + * @return true to indicate success, or false on immediate error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { + if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.setBufferMillis(codec, value); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + /** * Helper for converting a state to a string. * diff --git a/framework/java/android/bluetooth/BufferConstraint.java b/framework/java/android/bluetooth/BufferConstraint.java new file mode 100644 index 00000000000..cbffc788c35 --- /dev/null +++ b/framework/java/android/bluetooth/BufferConstraint.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 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.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Stores a codec's constraints on buffering length in milliseconds. + * + * {@hide} + */ +@SystemApi +public final class BufferConstraint implements Parcelable { + + private static final String TAG = "BufferConstraint"; + private int mDefaultMillis; + private int mMaxMillis; + private int mMinMillis; + + public BufferConstraint(int defaultMillis, int maxMillis, + int minMillis) { + mDefaultMillis = defaultMillis; + mMaxMillis = maxMillis; + mMinMillis = minMillis; + } + + BufferConstraint(Parcel in) { + mDefaultMillis = in.readInt(); + mMaxMillis = in.readInt(); + mMinMillis = in.readInt(); + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BufferConstraint createFromParcel(Parcel in) { + return new BufferConstraint(in); + } + + public BufferConstraint[] newArray(int size) { + return new BufferConstraint[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mDefaultMillis); + out.writeInt(mMaxMillis); + out.writeInt(mMinMillis); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Get the default buffer millis + * + * @return default buffer millis + * @hide + */ + @SystemApi + public int getDefaultMillis() { + return mDefaultMillis; + } + + /** + * Get the maximum buffer millis + * + * @return maximum buffer millis + * @hide + */ + @SystemApi + public int getMaxMillis() { + return mMaxMillis; + } + + /** + * Get the minimum buffer millis + * + * @return minimum buffer millis + * @hide + */ + @SystemApi + public int getMinMillis() { + return mMinMillis; + } +} diff --git a/framework/java/android/bluetooth/BufferConstraints.java b/framework/java/android/bluetooth/BufferConstraints.java new file mode 100644 index 00000000000..7e5ec1e7843 --- /dev/null +++ b/framework/java/android/bluetooth/BufferConstraints.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 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.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * A parcelable collection of buffer constraints by codec type. + * + * {@hide} + */ +@SystemApi +public final class BufferConstraints implements Parcelable { + public static final int BUFFER_CODEC_MAX_NUM = 32; + + private static final String TAG = "BufferConstraints"; + + private Map mBufferConstraints; + private List mBufferConstraintList; + + public BufferConstraints(@NonNull List + bufferConstraintList) { + + mBufferConstraintList = new ArrayList(bufferConstraintList); + mBufferConstraints = new HashMap(); + for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) { + mBufferConstraints.put(i, bufferConstraintList.get(i)); + } + } + + BufferConstraints(Parcel in) { + mBufferConstraintList = new ArrayList(); + mBufferConstraints = new HashMap(); + in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader()); + for (int i = 0; i < mBufferConstraintList.size(); i++) { + mBufferConstraints.put(i, mBufferConstraintList.get(i)); + } + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BufferConstraints createFromParcel(Parcel in) { + return new BufferConstraints(in); + } + + public BufferConstraints[] newArray(int size) { + return new BufferConstraints[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeList(mBufferConstraintList); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Get the buffer constraints by codec type. + * + * @param codec Audio codec + * @return buffer constraints by codec type. + * @hide + */ + @SystemApi + public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) { + return mBufferConstraints.get(codec); + } +} -- GitLab From a55a3f72e88e2286606835347a9da8d6b5ddab05 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Thu, 28 Jan 2021 18:54:38 +0100 Subject: [PATCH 1224/1408] AdvertiseData: fix nullable collection Bug: 152525509 Test: make checkapi Change-Id: Ia400cb2bda55b26594bc3a253612583134294edb --- framework/java/android/bluetooth/le/AdvertiseData.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index 573b9323264..fa7ac2b27c9 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -44,7 +44,7 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List mServiceUuids; - @Nullable + @NonNull private final List mServiceSolicitationUuids; private final SparseArray mManufacturerSpecificData; @@ -77,7 +77,7 @@ public final class AdvertiseData implements Parcelable { /** * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect. */ - @Nullable + @NonNull public List getServiceSolicitationUuids() { return mServiceSolicitationUuids; } @@ -221,7 +221,7 @@ public final class AdvertiseData implements Parcelable { public static final class Builder { @Nullable private List mServiceUuids = new ArrayList(); - @Nullable + @NonNull private List mServiceSolicitationUuids = new ArrayList(); private SparseArray mManufacturerSpecificData = new SparseArray(); private Map mServiceData = new ArrayMap(); -- GitLab From d1732c8e57cd399450f8a35bdaad74b25951651c Mon Sep 17 00:00:00 2001 From: Kevin Hufnagle Date: Mon, 19 Oct 2020 21:08:17 -0400 Subject: [PATCH 1225/1408] docs: Use correct profile for intent The ACTION_AUDIO_STATE_CHANGED intent action uses the HDP profile, not the A2DP profile. Bug: 158742305 Test: m ds-docs-java Exempt-From-Owner-Approval: Docs-only change Change-Id: I4358aac8c8aa9fc8b36e517e568e5a9db538555e (cherry picked from commit 10d0a560d962be20a9ce50db0bc87e0a204c9ff7) --- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 36076da91cc..d6b38fd32e8 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -80,7 +80,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Intent used to broadcast the change in the Audio Connection state of the - * A2DP profile. + * HDP profile. * *

            This intent will have 3 extras: *

              -- GitLab From 9965bf714abae172d54502981302d0253058d0ae Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 29 Jan 2021 09:20:44 +0100 Subject: [PATCH 1226/1408] docs: fix typo HDP->HFP in BluetoothHeadset Bug: 158742305 Test: m ds-docs-java Exempt-From-Owner-Approval: Docs-only change Change-Id: Iec48eba9a29e1cb7c2565e773ae876db662552ef --- framework/java/android/bluetooth/BluetoothHeadset.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index d6b38fd32e8..4fb557780d0 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -80,7 +80,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Intent used to broadcast the change in the Audio Connection state of the - * HDP profile. + * HFP profile. * *

              This intent will have 3 extras: *

                -- GitLab From 3477f5da2026cb80ddbf67158630aa5688e09a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 13 Jan 2021 11:02:43 +0100 Subject: [PATCH 1227/1408] Add Volume Control UUID Sponsor: jpawlowski@ Test: compilation Bug: 159786353 Tag: #feature Change-Id: I14116848411026ceab0ee15098687bf821e74d38 --- framework/java/android/bluetooth/BluetoothUuid.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index c0736a6b7bb..d82cf19e882 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -167,6 +167,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid VOLUME_CONTROL = + ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From 65b57adaa21dde636880ab8c133de2b1206d2a3b Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Tue, 12 Jan 2021 11:39:43 -0800 Subject: [PATCH 1228/1408] [DO NOT MERGE] Scan and notify apps when their companion devices are nearby Test: manual Bug: 168052577 Change-Id: Ib2187fb76e604878b1d4dd9c0cd6cea610b2a04d (cherry picked from commit 017c2c41456e3938145cf33facea339f9918b20c) --- .../android/bluetooth/BluetoothAdapter.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7eda50e5c9c..ea7e5ea7c80 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3197,6 +3197,61 @@ public final class BluetoothAdapter { void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); } + /** + * Register a callback to receive events whenever the bluetooth stack goes down and back up, + * e.g. in the event the bluetooth is turned off/on via settings. + * + * If the bluetooth stack is currently up, there will not be an initial callback call. + * You can use the return value as an indication of this being the case. + * + * Callbacks will be delivered on a binder thread. + * + * @return whether bluetooth is already up currently + * + * @hide + */ + public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) { + return getBluetoothService(callback.mRemote) != null; + } + + /** + * Unregister a callback registered via {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) { + removeServiceStateCallback(callback.mRemote); + } + + /** + * A callback for {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public abstract static class ServiceLifecycleCallback { + + /** Called when the bluetooth stack is up */ + public abstract void onBluetoothServiceUp(); + + /** Called when the bluetooth stack is down */ + public abstract void onBluetoothServiceDown(); + + IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { + @Override + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + ServiceLifecycleCallback.this.onBluetoothServiceUp(); + } + + @Override + public void onBluetoothServiceDown() { + ServiceLifecycleCallback.this.onBluetoothServiceDown(); + } + + @Override + public void onBrEdrDown() {} + }; + } + /** * Starts a scan for Bluetooth LE devices. * -- GitLab From 182e8c12f27517c7c11f535472c65fae6c17c500 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 29 Jan 2021 12:38:13 -0800 Subject: [PATCH 1229/1408] Skips the consent pairing dialog for recently associated CDM app and device combos because they have already been shown the CDM prompt which implicitly provides consent to bond. Tag: #feature Bug: 172006481 Test: Manual Change-Id: I541b720c6b8b6e55be10e04f202e0a58cf33715f --- .../android/bluetooth/BluetoothAdapter.java | 2 +- .../android/bluetooth/BluetoothDevice.java | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ea7e5ea7c80..ec46da0dcf0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1654,7 +1654,7 @@ public final class BluetoothAdapter { mContext = context; } - private String getOpPackageName() { + String getOpPackageName() { // Workaround for legacy API for getting a BluetoothAdapter not // passing a context if (mContext != null) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3b8dec7bf95..e7661dbad74 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1236,7 +1236,8 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.createBond(this, transport, oobData); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + return service.createBond(this, transport, oobData, adapter.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1393,6 +1394,31 @@ public final class BluetoothDevice implements Parcelable { return BOND_NONE; } + /** + * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip + * the bluetooth pairing dialog because it has been already consented by the CDM prompt. + * + * @return true if we can bond without the dialog, false otherwise + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean canBondWithoutDialog() { + final IBluetooth service = sService; + if (service == null) { + Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog"); + return false; + } + try { + if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this); + return service.canBondWithoutDialog(this); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + /** * Returns whether there is an open connection to this device. * -- GitLab From 812c75d373d1de294fb7cbcc8ecfe8c2c513acc6 Mon Sep 17 00:00:00 2001 From: Bob Badour Date: Fri, 12 Feb 2021 17:07:05 -0800 Subject: [PATCH 1230/1408] [LSC] Add LOCAL_LICENSE_KINDS to frameworks/base Added SPDX-license-identifier-Apache-2.0 to: apct-tests/perftests/autofill/Android.bp apct-tests/perftests/blobstore/Android.bp apct-tests/perftests/core/Android.bp apct-tests/perftests/core/apps/overlay/Android.bp apct-tests/perftests/core/apps/reources_manager/Android.bp apct-tests/perftests/core/jni/Android.bp apct-tests/perftests/multiuser/Android.bp apct-tests/perftests/multiuser/apps/dummyapp/Android.bp apct-tests/perftests/packagemanager/Android.bp apct-tests/perftests/packagemanager/apps/query-all/Android.bp apct-tests/perftests/textclassifier/Android.bp apct-tests/perftests/utils/Android.bp apct-tests/perftests/windowmanager/Android.bp apex/Android.bp apex/blobstore/framework/Android.bp apex/blobstore/service/Android.bp apex/jobscheduler/framework/Android.bp apex/jobscheduler/service/Android.bp apex/media/Android.bp apex/media/aidl/Android.bp apex/media/framework/Android.bp cmds/am/Android.bp cmds/app_process/Android.bp cmds/appops/Android.bp cmds/appwidget/Android.bp cmds/backup/Android.bp cmds/bmgr/Android.bp cmds/bootanimation/Android.bp cmds/bu/Android.bp cmds/content/Android.bp cmds/dpm/Android.bp cmds/hid/Android.bp cmds/hid/jni/Android.bp cmds/idmap2/Android.bp cmds/ime/Android.bp cmds/incident/Android.bp cmds/incident_helper/Android.bp cmds/incidentd/Android.bp cmds/input/Android.bp cmds/interrupter/Android.bp cmds/locksettings/Android.bp cmds/pm/Android.bp cmds/requestsync/Android.bp cmds/screencap/Android.bp cmds/sm/Android.bp cmds/svc/Android.bp cmds/telecom/Android.bp cmds/uiautomator/Android.bp cmds/uiautomator/cmds/uiautomator/Android.bp cmds/uiautomator/instrumentation/Android.bp cmds/uiautomator/library/Android.bp cmds/vr/Android.bp cmds/wm/Android.bp config/Android.bp core/java/android/service/wallpaper/Android.bp core/jni/Android.bp core/sysprop/Android.bp core/tests/BroadcastRadioTests/Android.bp core/tests/ConnectivityManagerTest/Android.bp core/tests/PackageInstallerSessions/Android.bp core/tests/PlatformCompatFramework/Android.bp core/tests/bandwidthtests/Android.bp core/tests/benchmarks/Android.bp core/tests/bluetoothtests/Android.bp core/tests/bugreports/Android.bp core/tests/coretests/Android.bp core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp core/tests/coretests/BinderProxyCountingTestApp/Android.bp core/tests/coretests/BinderProxyCountingTestService/Android.bp core/tests/coretests/BstatsTestApp/Android.bp core/tests/coretests/DisabledTestApp/Android.bp core/tests/coretests/EnabledTestApp/Android.bp core/tests/coretests/aidl/Android.bp core/tests/coretests/apks/Android.bp core/tests/coretests/apks/install/Android.bp core/tests/coretests/apks/install_bad_dex/Android.bp core/tests/coretests/apks/install_complete_package_info/Android.bp core/tests/coretests/apks/install_decl_perm/Android.bp core/tests/coretests/apks/install_jni_lib/Android.bp core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp core/tests/coretests/apks/install_loc_auto/Android.bp core/tests/coretests/apks/install_loc_internal/Android.bp core/tests/coretests/apks/install_loc_sdcard/Android.bp core/tests/coretests/apks/install_loc_unspecified/Android.bp core/tests/coretests/apks/install_use_perm_good/Android.bp core/tests/coretests/apks/install_uses_feature/Android.bp core/tests/coretests/apks/install_verifier_bad/Android.bp core/tests/coretests/apks/install_verifier_good/Android.bp core/tests/coretests/apks/keyset/Android.bp core/tests/coretests/apks/locales/Android.bp core/tests/coretests/apks/overlay_config/Android.bp core/tests/coretests/apks/version/Android.bp core/tests/coretests/apks/version_nosys/Android.bp core/tests/featureflagtests/Android.bp core/tests/hdmitests/Android.bp core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp core/tests/hosttests/test-apps/NoLocTestApp/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/SharedUid/32/Android.bp core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/64/Android.bp core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp core/tests/hosttests/test-apps/SimpleTestApp/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp core/tests/mockingcoretests/Android.bp core/tests/notificationtests/Android.bp core/tests/overlaytests/device/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp core/tests/overlaytests/host/Android.bp core/tests/overlaytests/remount/Android.bp core/tests/overlaytests/remount/test-apps/Overlay/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp core/tests/overlaytests/remount/test-apps/Target/Android.bp core/tests/packagemanagertests/Android.bp core/tests/privacytests/Android.bp core/tests/screenshothelpertests/Android.bp core/tests/systemproperties/Android.bp core/tests/utillib/Android.bp core/tests/utiltests/Android.bp core/tests/utiltests/jni/Android.bp core/tests/uwbtests/Android.bp core/xsd/Android.bp core/xsd/vts/Android.bp data/etc/Android.bp data/etc/car/Android.bp data/fonts/Android.bp data/keyboards/Android.mk drm/jni/Android.bp errorprone/Android.bp graphics/proto/Android.bp keystore/Android.bp keystore/tests/Android.bp libs/WindowManager/Jetpack/Android.bp libs/WindowManager/Shell/Android.bp libs/WindowManager/Shell/tests/Android.bp libs/androidfw/Android.bp libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp libs/hostgraphics/Android.bp libs/incident/Android.bp libs/input/Android.bp libs/input/tests/Android.bp libs/protoutil/Android.bp libs/services/Android.bp libs/storage/Android.bp libs/usb/tests/AccessoryChat/Android.bp libs/usb/tests/AccessoryChat/accessorychat/Android.bp location/lib/Android.bp location/tests/Android.bp location/tests/locationtests/Android.bp lowpan/tests/Android.bp media/Android.bp media/java/Android.bp media/java/android/media/tv/tunerresourcemanager/Android.bp media/jni/Android.bp media/jni/audioeffect/Android.bp media/jni/soundpool/Android.bp media/jni/soundpool/tests/Android.bp media/lib/remotedisplay/Android.bp media/lib/signer/Android.bp media/lib/tvremote/Android.bp media/lib/tvremote/tests/Android.bp media/mca/filterfw/Android.bp media/mca/filterfw/native/Android.bp media/mca/filterpacks/Android.bp media/mca/samples/CameraEffectsRecordingSample/Android.bp media/mca/tests/Android.bp media/native/midi/Android.bp media/packages/BluetoothMidiService/Android.bp media/packages/BluetoothMidiService/tests/unit/Android.bp media/tests/AudioPolicyTest/Android.bp media/tests/CameraBrowser/Android.bp media/tests/EffectsTest/Android.bp media/tests/MediaDump/Android.bp media/tests/MediaFrameworkTest/Android.bp media/tests/MediaRouter/Android.bp media/tests/MtpTests/Android.bp media/tests/ScoAudioTest/Android.bp media/tests/SoundPoolTest/Android.bp media/tests/TunerTest/Android.bp media/tests/audiotests/Android.bp media/tests/players/Android.bp mime/Android.bp native/android/Android.bp native/graphics/jni/Android.bp native/webview/loader/Android.bp nfc-extras/Android.bp nfc-extras/tests/Android.bp packages/AppPredictionLib/Android.bp packages/BackupEncryption/Android.bp packages/BackupEncryption/test/robolectric-integration/Android.bp packages/BackupEncryption/test/robolectric/Android.bp packages/BackupEncryption/test/unittest/Android.bp packages/BackupRestoreConfirmation/Android.bp packages/CarSystemUI/Android.bp packages/CarrierDefaultApp/Android.bp packages/CarrierDefaultApp/tests/unit/Android.bp packages/CompanionDeviceManager/Android.bp packages/Connectivity/framework/Android.bp packages/Connectivity/service/Android.bp packages/CtsShim/Android.bp packages/CtsShim/build/Android.bp packages/CtsShim/build/jni/Android.bp packages/DynamicSystemInstallationService/Android.bp packages/DynamicSystemInstallationService/tests/Android.bp packages/EasterEgg/Android.bp packages/EncryptedLocalTransport/Android.bp packages/ExtShared/Android.bp packages/ExternalStorageProvider/Android.bp packages/ExternalStorageProvider/tests/Android.bp packages/FakeOemFeatures/Android.bp packages/FusedLocation/Android.bp packages/InputDevices/Android.bp packages/LocalTransport/Android.bp packages/PackageInstaller/Android.bp packages/PrintRecommendationService/Android.bp packages/PrintSpooler/Android.bp packages/PrintSpooler/jni/Android.bp packages/PrintSpooler/tests/outofprocess/Android.bp packages/SettingsLib/ActionBarShadow/Android.bp packages/SettingsLib/ActionButtonsPreference/Android.bp packages/SettingsLib/AdaptiveIcon/Android.bp packages/SettingsLib/Android.bp packages/SettingsLib/AppPreference/Android.bp packages/SettingsLib/BarChartPreference/Android.bp packages/SettingsLib/DisplayDensityUtils/Android.bp packages/SettingsLib/EntityHeaderWidgets/Android.bp packages/SettingsLib/HelpUtils/Android.bp packages/SettingsLib/LayoutPreference/Android.bp packages/SettingsLib/ProgressBar/Android.bp packages/SettingsLib/RadioButtonPreference/Android.bp packages/SettingsLib/RestrictedLockUtils/Android.bp packages/SettingsLib/SchedulesProvider/Android.bp packages/SettingsLib/SearchProvider/Android.bp packages/SettingsLib/SearchWidget/Android.bp packages/SettingsLib/SettingsSpinner/Android.bp packages/SettingsLib/SettingsTheme/Android.bp packages/SettingsLib/Tile/Android.bp packages/SettingsLib/Utils/Android.bp packages/SettingsLib/search/Android.bp packages/SettingsLib/tests/integ/Android.bp packages/SettingsLib/tests/robotests/Android.bp packages/SettingsProvider/Android.bp packages/SharedStorageBackup/Android.bp packages/Shell/Android.bp packages/Shell/tests/Android.bp packages/SimAppDialog/Android.bp packages/SoundPicker/Android.bp packages/StatementService/Android.bp packages/SystemUI/Android.bp packages/SystemUI/plugin/Android.bp packages/SystemUI/plugin/ExamplePlugin/Android.bp packages/SystemUI/plugin_core/Android.bp packages/SystemUI/shared/Android.bp packages/VpnDialogs/Android.bp packages/WAPPushManager/Android.bp packages/WAPPushManager/tests/Android.bp packages/WallpaperBackup/Android.bp packages/WallpaperCropper/Android.bp packages/overlays/Android.mk packages/overlays/tests/Android.bp packages/services/PacProcessor/Android.bp packages/services/PacProcessor/jni/Android.bp packages/services/Proxy/Android.bp proto/Android.bp rs/jni/Android.mk samples/demo/haptic-assessment/Android.bp sax/tests/saxtests/Android.bp services/Android.bp services/accessibility/Android.bp services/appprediction/Android.bp services/appwidget/Android.bp services/autofill/Android.bp services/backup/Android.bp services/backup/backuplib/Android.bp services/companion/Android.bp services/contentcapture/Android.bp services/contentsuggestions/Android.bp services/core/Android.bp services/core/java/com/android/server/vcn/Android.bp services/core/jni/Android.bp services/core/xsd/Android.bp services/core/xsd/vts/Android.bp services/coverage/Android.bp services/devicepolicy/Android.bp services/incremental/Android.bp services/midi/Android.bp services/net/Android.bp services/people/Android.bp services/print/Android.bp services/profcollect/Android.bp services/restrictions/Android.bp services/robotests/Android.bp services/robotests/backup/Android.bp services/systemcaptions/Android.bp services/tests/PackageManagerComponentOverrideTests/Android.bp services/tests/PackageManagerServiceTests/host/Android.bp services/tests/PackageManagerServiceTests/host/test-apps/Android.bp services/tests/mockingservicestests/Android.bp services/tests/rescueparty/Android.bp services/tests/servicestests/Android.bp services/tests/servicestests/aidl/Android.bp services/tests/servicestests/apks/Android.bp services/tests/servicestests/apks/install-split-base/Android.bp services/tests/servicestests/apks/install-split-feature-a/Android.bp services/tests/servicestests/apks/install_intent_filters/Android.bp services/tests/servicestests/apks/install_uses_sdk/Android.bp services/tests/servicestests/test-apps/ConnTestApp/Android.bp services/tests/servicestests/test-apps/JobTestApp/Android.bp services/tests/servicestests/test-apps/PackageParserApp/Android.bp services/tests/servicestests/test-apps/PackageParsingTestManifests/Android.bp services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp services/tests/servicestests/test-apps/SuspendTestApp/Android.bp services/tests/shortcutmanagerutils/Android.bp services/tests/uiservicestests/Android.bp services/tests/wmtests/Android.bp services/usage/Android.bp services/usb/Android.bp services/voiceinteraction/Android.bp services/wifi/Android.bp startop/apps/test/Android.bp startop/iorap/Android.bp startop/iorap/functional_tests/Android.bp startop/iorap/stress/Android.bp startop/iorap/tests/Android.bp startop/view_compiler/Android.bp startop/view_compiler/dex_builder_test/Android.bp test-base/hiddenapi/Android.bp test-mock/Android.bp test-runner/tests/Android.bp tests/AccessibilityEventsLogger/Android.bp tests/AccessoryDisplay/common/Android.bp tests/AccessoryDisplay/sink/Android.bp tests/AccessoryDisplay/source/Android.bp tests/ActivityManagerPerfTests/stub-app/Android.bp tests/ActivityManagerPerfTests/test-app/Android.bp tests/ActivityManagerPerfTests/tests/Android.bp tests/ActivityManagerPerfTests/utils/Android.bp tests/ActivityTests/Android.bp tests/ActivityViewTest/Android.bp tests/AmSlam/Android.bp tests/ApkVerityTest/Android.bp tests/ApkVerityTest/ApkVerityTestApp/Android.bp tests/ApkVerityTest/block_device_writer/Android.bp tests/AppLaunch/Android.bp tests/AppLaunchWear/Android.bp tests/AppResourcesLoaders/Android.bp tests/AppResourcesLoaders/Overlay/Android.bp tests/Assist/Android.bp tests/AutoVerify/app1/Android.bp tests/AutoVerify/app2/Android.bp tests/AutoVerify/app3/Android.bp tests/AutoVerify/app4/Android.bp tests/BackgroundDexOptServiceIntegrationTests/Android.bp tests/BandwidthTests/Android.bp tests/BatteryWaster/Android.bp tests/BiDiTests/Android.bp tests/BlobStoreTestUtils/Android.bp tests/BootImageProfileTest/Android.bp tests/BrowserPowerTest/Android.bp tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp tests/CameraPrewarmTest/Android.bp tests/Codegen/Android.bp tests/Compatibility/Android.bp tests/CoreTests/android/Android.bp tests/DataIdleTest/Android.bp tests/DozeTest/Android.bp tests/DpiTest/Android.bp tests/DynamicCodeLoggerIntegrationTests/Android.mk tests/FeatureSplit/base/Android.bp tests/FeatureSplit/feature1/Android.bp tests/FeatureSplit/feature2/Android.bp tests/FixVibrateSetting/Android.bp tests/FlickerTests/Android.bp tests/FlickerTests/test-apps/Android.bp tests/FlickerTests/test-apps/flickerapp/Android.bp tests/FrameworkPerf/Android.bp tests/GamePerformance/Android.bp tests/GridLayoutTest/Android.bp tests/HierarchyViewerTest/Android.bp tests/HugeBackup/Android.bp tests/HwAccelerationTest/Android.bp tests/Internal/Android.bp tests/JankBench/Android.bp tests/JobSchedulerPerfTests/Android.bp tests/JobSchedulerTestApp/Android.bp tests/LargeAssetTest/Android.bp tests/LegacyAssistant/Android.bp tests/LocalizationTest/Android.bp tests/LocationTracker/Android.bp tests/LotsOfApps/Android.bp tests/LowStorageTest/Android.bp tests/ManagedProfileLifecycleStressTest/Android.bp tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp tests/MemoryUsage/Android.bp tests/MirrorSurfaceTest/Android.bp tests/NativeProcessesMemoryTest/Android.bp tests/NetworkSecurityConfigTest/Android.bp tests/NullHomeTest/Android.bp tests/OdmApps/Android.bp tests/OdmApps/app/Android.bp tests/OdmApps/priv-app/Android.bp tests/OneMedia/Android.bp tests/PackageWatchdog/Android.bp tests/PlatformCompatGating/Android.bp tests/PlatformCompatGating/test-rules/Android.bp tests/ProtoInputStreamTests/Android.bp tests/RemoteDisplayProvider/Android.bp tests/RenderThreadTest/Android.bp tests/RollbackTest/Android.bp tests/SerialChat/Android.bp tests/ServiceCrashTest/Android.bp tests/SharedLibrary/client/Android.bp tests/SharedLibrary/lib/Android.bp tests/ShowWhenLockedApp/Android.bp tests/SmokeTest/Android.bp tests/SmokeTest/tests/Android.bp tests/SmokeTestApps/Android.bp tests/SoundTriggerTestApp/Android.bp tests/Split/Android.bp tests/StagedInstallTest/Android.bp tests/StatusBar/Android.bp tests/SurfaceComposition/Android.bp tests/SurfaceControlViewHostTest/Android.bp tests/SystemMemoryTest/device/Android.bp tests/SystemMemoryTest/host/Android.bp tests/SystemUIDemoModeController/Android.bp tests/TaskOrganizerTest/Android.bp tests/TelephonyCommonTests/Android.bp tests/TouchLatency/Android.bp tests/TransformTest/Android.bp tests/TtsTests/Android.bp tests/UiBench/Android.bp tests/UsageReportingTest/Android.bp tests/UsageStatsPerfTests/Android.bp tests/UsageStatsTest/Android.bp tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp tests/UsbManagerTests/Android.bp tests/UsbManagerTests/lib/Android.bp tests/UsbTests/Android.bp tests/UsesFeature2Test/Android.bp tests/VectorDrawableTest/Android.bp tests/VoiceEnrollment/Android.bp tests/VoiceInteraction/Android.bp tests/WallpaperTest/Android.bp tests/WindowAnimationJank/Android.bp tests/WindowInsetsTests/Android.bp tests/appwidgets/AppWidgetHostTest/Android.bp tests/appwidgets/AppWidgetProviderTest/Android.bp tests/backup/Android.mk tests/benchmarks/Android.bp tests/libs-permissions/Android.bp tests/net/Android.bp tests/net/common/Android.bp tests/net/deflake/Android.bp tests/net/integration/Android.bp tests/net/jni/Android.bp tests/net/smoketest/Android.bp tests/notification/Android.bp tests/permission/Android.bp tests/privapp-permissions/Android.bp tests/testables/Android.bp tests/testables/tests/Android.bp tests/utils/StubIME/Android.bp tests/utils/hostutils/Android.bp tests/utils/testutils/Android.bp tests/vcn/Android.bp tools/aapt/Android.bp tools/aapt2/Android.bp tools/aapt2/integration-tests/AutoVersionTest/Android.bp tools/aapt2/integration-tests/BasicTest/Android.bp tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk tools/aapt2/integration-tests/StaticLibTest/App/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp tools/aapt2/integration-tests/SymlinkTest/Android.bp tools/bit/Android.bp tools/codegen/Android.bp tools/dump-coverage/Android.bp tools/incident_report/Android.bp tools/incident_section_gen/Android.bp tools/lock_agent/Android.bp tools/locked_region_code_injection/Android.bp tools/obbtool/Android.bp tools/powermodel/Android.bp tools/preload-check/Android.bp tools/preload-check/device/Android.bp tools/preload/loadclass/Android.bp tools/processors/staledataclass/Android.bp tools/processors/view_inspector/Android.bp tools/protologtool/Android.bp tools/sdkparcelables/Android.bp tools/split-select/Android.bp tools/streaming_proto/Android.bp tools/validatekeymaps/Android.bp wifi/java/Android.bp wifi/tests/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD to: libs/hwui/Android.bp native/webview/plat_support/Android.bp obex/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD SPDX-license-identifier-CC-BY SPDX-license-identifier-CPL-1.0 SPDX-license-identifier-GPL SPDX-license-identifier-GPL-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS SPDX-license-identifier-W3C legacy_unencumbered to: Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_unencumbered to: core/java/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-CPL-1.0 to: test-base/Android.bp test-runner/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL to: core/res/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL-2.0 to: libs/usb/Android.bp libs/usb/tests/accessorytest/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT to: tools/preload/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS to: api/Android.bp boot/Android.bp cmds/device_config/Android.bp cmds/settings/Android.bp core/api/Android.bp core/tests/coretests/certs/Android.bp core/tests/overlaytests/remount/test-apps/certs/Android.bp core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp libs/tracingproxy/Android.bp services/startop/Android.bp test-legacy/Android.mk tests/ApkVerityTest/testdata/Android.bp tests/TransitionTests/Android.bp Bug: 68860345 Bug: 151177513 Bug: 151953481 Test: m all Exempt-From-Owner-Approval: janitorial work Change-Id: Ic44d662936d1ff0cae7fbe915932b37aa4e4869a Merged-in: I6e9103c3275cb2e6df5dc586588eccd7d2ab6b06 --- framework/tests/Android.bp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/tests/Android.bp b/framework/tests/Android.bp index 4b6f9db33af..a2e4dff8f82 100644 --- a/framework/tests/Android.bp +++ b/framework/tests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BluetoothTests", // Include all test java files. -- GitLab From 67ef1c99c949c42af591e98474fb0cddf65478bd Mon Sep 17 00:00:00 2001 From: Bob Badour Date: Fri, 12 Feb 2021 17:07:05 -0800 Subject: [PATCH 1231/1408] [LSC] Add LOCAL_LICENSE_KINDS to frameworks/base Added SPDX-license-identifier-Apache-2.0 to: apct-tests/perftests/autofill/Android.bp apct-tests/perftests/blobstore/Android.bp apct-tests/perftests/core/Android.bp apct-tests/perftests/core/apps/overlay/Android.bp apct-tests/perftests/core/apps/reources_manager/Android.bp apct-tests/perftests/core/jni/Android.bp apct-tests/perftests/multiuser/Android.bp apct-tests/perftests/multiuser/apps/dummyapp/Android.bp apct-tests/perftests/packagemanager/Android.bp apct-tests/perftests/packagemanager/apps/query-all/Android.bp apct-tests/perftests/textclassifier/Android.bp apct-tests/perftests/utils/Android.bp apct-tests/perftests/windowmanager/Android.bp apex/Android.bp apex/blobstore/framework/Android.bp apex/blobstore/service/Android.bp apex/jobscheduler/framework/Android.bp apex/jobscheduler/service/Android.bp apex/media/Android.bp apex/media/aidl/Android.bp apex/media/framework/Android.bp cmds/am/Android.bp cmds/app_process/Android.bp cmds/appops/Android.bp cmds/appwidget/Android.bp cmds/backup/Android.bp cmds/bmgr/Android.bp cmds/bootanimation/Android.bp cmds/bu/Android.bp cmds/content/Android.bp cmds/dpm/Android.bp cmds/hid/Android.bp cmds/hid/jni/Android.bp cmds/idmap2/Android.bp cmds/ime/Android.bp cmds/incident/Android.bp cmds/incident_helper/Android.bp cmds/incidentd/Android.bp cmds/input/Android.bp cmds/interrupter/Android.bp cmds/locksettings/Android.bp cmds/pm/Android.bp cmds/requestsync/Android.bp cmds/screencap/Android.bp cmds/sm/Android.bp cmds/svc/Android.bp cmds/telecom/Android.bp cmds/uiautomator/Android.bp cmds/uiautomator/cmds/uiautomator/Android.bp cmds/uiautomator/instrumentation/Android.bp cmds/uiautomator/library/Android.bp cmds/vr/Android.bp cmds/wm/Android.bp config/Android.bp core/java/android/service/wallpaper/Android.bp core/jni/Android.bp core/sysprop/Android.bp core/tests/BroadcastRadioTests/Android.bp core/tests/ConnectivityManagerTest/Android.bp core/tests/PackageInstallerSessions/Android.bp core/tests/PlatformCompatFramework/Android.bp core/tests/bandwidthtests/Android.bp core/tests/benchmarks/Android.bp core/tests/bluetoothtests/Android.bp core/tests/bugreports/Android.bp core/tests/coretests/Android.bp core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp core/tests/coretests/BinderProxyCountingTestApp/Android.bp core/tests/coretests/BinderProxyCountingTestService/Android.bp core/tests/coretests/BstatsTestApp/Android.bp core/tests/coretests/DisabledTestApp/Android.bp core/tests/coretests/EnabledTestApp/Android.bp core/tests/coretests/aidl/Android.bp core/tests/coretests/apks/Android.bp core/tests/coretests/apks/install/Android.bp core/tests/coretests/apks/install_bad_dex/Android.bp core/tests/coretests/apks/install_complete_package_info/Android.bp core/tests/coretests/apks/install_decl_perm/Android.bp core/tests/coretests/apks/install_jni_lib/Android.bp core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp core/tests/coretests/apks/install_loc_auto/Android.bp core/tests/coretests/apks/install_loc_internal/Android.bp core/tests/coretests/apks/install_loc_sdcard/Android.bp core/tests/coretests/apks/install_loc_unspecified/Android.bp core/tests/coretests/apks/install_use_perm_good/Android.bp core/tests/coretests/apks/install_uses_feature/Android.bp core/tests/coretests/apks/install_verifier_bad/Android.bp core/tests/coretests/apks/install_verifier_good/Android.bp core/tests/coretests/apks/keyset/Android.bp core/tests/coretests/apks/locales/Android.bp core/tests/coretests/apks/overlay_config/Android.bp core/tests/coretests/apks/version/Android.bp core/tests/coretests/apks/version_nosys/Android.bp core/tests/featureflagtests/Android.bp core/tests/hdmitests/Android.bp core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp core/tests/hosttests/test-apps/NoLocTestApp/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/SharedUid/32/Android.bp core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/64/Android.bp core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp core/tests/hosttests/test-apps/SimpleTestApp/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp core/tests/mockingcoretests/Android.bp core/tests/notificationtests/Android.bp core/tests/overlaytests/device/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp core/tests/overlaytests/host/Android.bp core/tests/overlaytests/remount/Android.bp core/tests/overlaytests/remount/test-apps/Overlay/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp core/tests/overlaytests/remount/test-apps/Target/Android.bp core/tests/packagemanagertests/Android.bp core/tests/privacytests/Android.bp core/tests/screenshothelpertests/Android.bp core/tests/systemproperties/Android.bp core/tests/utillib/Android.bp core/tests/utiltests/Android.bp core/tests/utiltests/jni/Android.bp core/tests/uwbtests/Android.bp core/xsd/Android.bp core/xsd/vts/Android.bp data/etc/Android.bp data/etc/car/Android.bp data/fonts/Android.bp data/keyboards/Android.mk drm/jni/Android.bp errorprone/Android.bp graphics/proto/Android.bp keystore/Android.bp keystore/tests/Android.bp libs/WindowManager/Jetpack/Android.bp libs/WindowManager/Shell/Android.bp libs/WindowManager/Shell/tests/Android.bp libs/androidfw/Android.bp libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp libs/hostgraphics/Android.bp libs/incident/Android.bp libs/input/Android.bp libs/input/tests/Android.bp libs/protoutil/Android.bp libs/services/Android.bp libs/storage/Android.bp libs/usb/tests/AccessoryChat/Android.bp libs/usb/tests/AccessoryChat/accessorychat/Android.bp location/lib/Android.bp location/tests/Android.bp location/tests/locationtests/Android.bp lowpan/tests/Android.bp media/Android.bp media/java/Android.bp media/java/android/media/tv/tunerresourcemanager/Android.bp media/jni/Android.bp media/jni/audioeffect/Android.bp media/jni/soundpool/Android.bp media/jni/soundpool/tests/Android.bp media/lib/remotedisplay/Android.bp media/lib/signer/Android.bp media/lib/tvremote/Android.bp media/lib/tvremote/tests/Android.bp media/mca/filterfw/Android.bp media/mca/filterfw/native/Android.bp media/mca/filterpacks/Android.bp media/mca/samples/CameraEffectsRecordingSample/Android.bp media/mca/tests/Android.bp media/native/midi/Android.bp media/packages/BluetoothMidiService/Android.bp media/packages/BluetoothMidiService/tests/unit/Android.bp media/tests/AudioPolicyTest/Android.bp media/tests/CameraBrowser/Android.bp media/tests/EffectsTest/Android.bp media/tests/MediaDump/Android.bp media/tests/MediaFrameworkTest/Android.bp media/tests/MediaRouter/Android.bp media/tests/MtpTests/Android.bp media/tests/ScoAudioTest/Android.bp media/tests/SoundPoolTest/Android.bp media/tests/TunerTest/Android.bp media/tests/audiotests/Android.bp media/tests/players/Android.bp mime/Android.bp native/android/Android.bp native/graphics/jni/Android.bp native/webview/loader/Android.bp nfc-extras/Android.bp nfc-extras/tests/Android.bp packages/AppPredictionLib/Android.bp packages/BackupEncryption/Android.bp packages/BackupEncryption/test/robolectric-integration/Android.bp packages/BackupEncryption/test/robolectric/Android.bp packages/BackupEncryption/test/unittest/Android.bp packages/BackupRestoreConfirmation/Android.bp packages/CarSystemUI/Android.bp packages/CarrierDefaultApp/Android.bp packages/CarrierDefaultApp/tests/unit/Android.bp packages/CompanionDeviceManager/Android.bp packages/Connectivity/framework/Android.bp packages/Connectivity/service/Android.bp packages/CtsShim/Android.bp packages/CtsShim/build/Android.bp packages/CtsShim/build/jni/Android.bp packages/DynamicSystemInstallationService/Android.bp packages/DynamicSystemInstallationService/tests/Android.bp packages/EasterEgg/Android.bp packages/EncryptedLocalTransport/Android.bp packages/ExtShared/Android.bp packages/ExternalStorageProvider/Android.bp packages/ExternalStorageProvider/tests/Android.bp packages/FakeOemFeatures/Android.bp packages/FusedLocation/Android.bp packages/InputDevices/Android.bp packages/LocalTransport/Android.bp packages/PackageInstaller/Android.bp packages/PrintRecommendationService/Android.bp packages/PrintSpooler/Android.bp packages/PrintSpooler/jni/Android.bp packages/PrintSpooler/tests/outofprocess/Android.bp packages/SettingsLib/ActionBarShadow/Android.bp packages/SettingsLib/ActionButtonsPreference/Android.bp packages/SettingsLib/AdaptiveIcon/Android.bp packages/SettingsLib/Android.bp packages/SettingsLib/AppPreference/Android.bp packages/SettingsLib/BarChartPreference/Android.bp packages/SettingsLib/DisplayDensityUtils/Android.bp packages/SettingsLib/EntityHeaderWidgets/Android.bp packages/SettingsLib/HelpUtils/Android.bp packages/SettingsLib/LayoutPreference/Android.bp packages/SettingsLib/ProgressBar/Android.bp packages/SettingsLib/RadioButtonPreference/Android.bp packages/SettingsLib/RestrictedLockUtils/Android.bp packages/SettingsLib/SchedulesProvider/Android.bp packages/SettingsLib/SearchProvider/Android.bp packages/SettingsLib/SearchWidget/Android.bp packages/SettingsLib/SettingsSpinner/Android.bp packages/SettingsLib/SettingsTheme/Android.bp packages/SettingsLib/Tile/Android.bp packages/SettingsLib/Utils/Android.bp packages/SettingsLib/search/Android.bp packages/SettingsLib/tests/integ/Android.bp packages/SettingsLib/tests/robotests/Android.bp packages/SettingsProvider/Android.bp packages/SharedStorageBackup/Android.bp packages/Shell/Android.bp packages/Shell/tests/Android.bp packages/SimAppDialog/Android.bp packages/SoundPicker/Android.bp packages/StatementService/Android.bp packages/SystemUI/Android.bp packages/SystemUI/plugin/Android.bp packages/SystemUI/plugin/ExamplePlugin/Android.bp packages/SystemUI/plugin_core/Android.bp packages/SystemUI/shared/Android.bp packages/VpnDialogs/Android.bp packages/WAPPushManager/Android.bp packages/WAPPushManager/tests/Android.bp packages/WallpaperBackup/Android.bp packages/WallpaperCropper/Android.bp packages/overlays/Android.mk packages/overlays/tests/Android.bp packages/services/PacProcessor/Android.bp packages/services/PacProcessor/jni/Android.bp packages/services/Proxy/Android.bp proto/Android.bp rs/jni/Android.mk samples/demo/haptic-assessment/Android.bp sax/tests/saxtests/Android.bp services/Android.bp services/accessibility/Android.bp services/appprediction/Android.bp services/appwidget/Android.bp services/autofill/Android.bp services/backup/Android.bp services/backup/backuplib/Android.bp services/companion/Android.bp services/contentcapture/Android.bp services/contentsuggestions/Android.bp services/core/Android.bp services/core/java/com/android/server/vcn/Android.bp services/core/jni/Android.bp services/core/xsd/Android.bp services/core/xsd/vts/Android.bp services/coverage/Android.bp services/devicepolicy/Android.bp services/incremental/Android.bp services/midi/Android.bp services/net/Android.bp services/people/Android.bp services/print/Android.bp services/profcollect/Android.bp services/restrictions/Android.bp services/robotests/Android.bp services/robotests/backup/Android.bp services/systemcaptions/Android.bp services/tests/PackageManagerComponentOverrideTests/Android.bp services/tests/PackageManagerServiceTests/host/Android.bp services/tests/PackageManagerServiceTests/host/test-apps/Android.bp services/tests/mockingservicestests/Android.bp services/tests/rescueparty/Android.bp services/tests/servicestests/Android.bp services/tests/servicestests/aidl/Android.bp services/tests/servicestests/apks/Android.bp services/tests/servicestests/apks/install-split-base/Android.bp services/tests/servicestests/apks/install-split-feature-a/Android.bp services/tests/servicestests/apks/install_intent_filters/Android.bp services/tests/servicestests/apks/install_uses_sdk/Android.bp services/tests/servicestests/test-apps/ConnTestApp/Android.bp services/tests/servicestests/test-apps/JobTestApp/Android.bp services/tests/servicestests/test-apps/PackageParserApp/Android.bp services/tests/servicestests/test-apps/PackageParsingTestManifests/Android.bp services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp services/tests/servicestests/test-apps/SuspendTestApp/Android.bp services/tests/shortcutmanagerutils/Android.bp services/tests/uiservicestests/Android.bp services/tests/wmtests/Android.bp services/usage/Android.bp services/usb/Android.bp services/voiceinteraction/Android.bp services/wifi/Android.bp startop/apps/test/Android.bp startop/iorap/Android.bp startop/iorap/functional_tests/Android.bp startop/iorap/stress/Android.bp startop/iorap/tests/Android.bp startop/view_compiler/Android.bp startop/view_compiler/dex_builder_test/Android.bp test-base/hiddenapi/Android.bp test-mock/Android.bp test-runner/tests/Android.bp tests/AccessibilityEventsLogger/Android.bp tests/AccessoryDisplay/common/Android.bp tests/AccessoryDisplay/sink/Android.bp tests/AccessoryDisplay/source/Android.bp tests/ActivityManagerPerfTests/stub-app/Android.bp tests/ActivityManagerPerfTests/test-app/Android.bp tests/ActivityManagerPerfTests/tests/Android.bp tests/ActivityManagerPerfTests/utils/Android.bp tests/ActivityTests/Android.bp tests/ActivityViewTest/Android.bp tests/AmSlam/Android.bp tests/ApkVerityTest/Android.bp tests/ApkVerityTest/ApkVerityTestApp/Android.bp tests/ApkVerityTest/block_device_writer/Android.bp tests/AppLaunch/Android.bp tests/AppLaunchWear/Android.bp tests/AppResourcesLoaders/Android.bp tests/AppResourcesLoaders/Overlay/Android.bp tests/Assist/Android.bp tests/AutoVerify/app1/Android.bp tests/AutoVerify/app2/Android.bp tests/AutoVerify/app3/Android.bp tests/AutoVerify/app4/Android.bp tests/BackgroundDexOptServiceIntegrationTests/Android.bp tests/BandwidthTests/Android.bp tests/BatteryWaster/Android.bp tests/BiDiTests/Android.bp tests/BlobStoreTestUtils/Android.bp tests/BootImageProfileTest/Android.bp tests/BrowserPowerTest/Android.bp tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp tests/CameraPrewarmTest/Android.bp tests/Codegen/Android.bp tests/Compatibility/Android.bp tests/CoreTests/android/Android.bp tests/DataIdleTest/Android.bp tests/DozeTest/Android.bp tests/DpiTest/Android.bp tests/DynamicCodeLoggerIntegrationTests/Android.mk tests/FeatureSplit/base/Android.bp tests/FeatureSplit/feature1/Android.bp tests/FeatureSplit/feature2/Android.bp tests/FixVibrateSetting/Android.bp tests/FlickerTests/Android.bp tests/FlickerTests/test-apps/Android.bp tests/FlickerTests/test-apps/flickerapp/Android.bp tests/FrameworkPerf/Android.bp tests/GamePerformance/Android.bp tests/GridLayoutTest/Android.bp tests/HierarchyViewerTest/Android.bp tests/HugeBackup/Android.bp tests/HwAccelerationTest/Android.bp tests/Internal/Android.bp tests/JankBench/Android.bp tests/JobSchedulerPerfTests/Android.bp tests/JobSchedulerTestApp/Android.bp tests/LargeAssetTest/Android.bp tests/LegacyAssistant/Android.bp tests/LocalizationTest/Android.bp tests/LocationTracker/Android.bp tests/LotsOfApps/Android.bp tests/LowStorageTest/Android.bp tests/ManagedProfileLifecycleStressTest/Android.bp tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp tests/MemoryUsage/Android.bp tests/MirrorSurfaceTest/Android.bp tests/NativeProcessesMemoryTest/Android.bp tests/NetworkSecurityConfigTest/Android.bp tests/NullHomeTest/Android.bp tests/OdmApps/Android.bp tests/OdmApps/app/Android.bp tests/OdmApps/priv-app/Android.bp tests/OneMedia/Android.bp tests/PackageWatchdog/Android.bp tests/PlatformCompatGating/Android.bp tests/PlatformCompatGating/test-rules/Android.bp tests/ProtoInputStreamTests/Android.bp tests/RemoteDisplayProvider/Android.bp tests/RenderThreadTest/Android.bp tests/RollbackTest/Android.bp tests/SerialChat/Android.bp tests/ServiceCrashTest/Android.bp tests/SharedLibrary/client/Android.bp tests/SharedLibrary/lib/Android.bp tests/ShowWhenLockedApp/Android.bp tests/SmokeTest/Android.bp tests/SmokeTest/tests/Android.bp tests/SmokeTestApps/Android.bp tests/SoundTriggerTestApp/Android.bp tests/Split/Android.bp tests/StagedInstallTest/Android.bp tests/StatusBar/Android.bp tests/SurfaceComposition/Android.bp tests/SurfaceControlViewHostTest/Android.bp tests/SystemMemoryTest/device/Android.bp tests/SystemMemoryTest/host/Android.bp tests/SystemUIDemoModeController/Android.bp tests/TaskOrganizerTest/Android.bp tests/TelephonyCommonTests/Android.bp tests/TouchLatency/Android.bp tests/TransformTest/Android.bp tests/TtsTests/Android.bp tests/UiBench/Android.bp tests/UsageReportingTest/Android.bp tests/UsageStatsPerfTests/Android.bp tests/UsageStatsTest/Android.bp tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp tests/UsbManagerTests/Android.bp tests/UsbManagerTests/lib/Android.bp tests/UsbTests/Android.bp tests/UsesFeature2Test/Android.bp tests/VectorDrawableTest/Android.bp tests/VoiceEnrollment/Android.bp tests/VoiceInteraction/Android.bp tests/WallpaperTest/Android.bp tests/WindowAnimationJank/Android.bp tests/WindowInsetsTests/Android.bp tests/appwidgets/AppWidgetHostTest/Android.bp tests/appwidgets/AppWidgetProviderTest/Android.bp tests/backup/Android.mk tests/benchmarks/Android.bp tests/libs-permissions/Android.bp tests/net/Android.bp tests/net/common/Android.bp tests/net/deflake/Android.bp tests/net/integration/Android.bp tests/net/jni/Android.bp tests/net/smoketest/Android.bp tests/notification/Android.bp tests/permission/Android.bp tests/privapp-permissions/Android.bp tests/testables/Android.bp tests/testables/tests/Android.bp tests/utils/StubIME/Android.bp tests/utils/hostutils/Android.bp tests/utils/testutils/Android.bp tests/vcn/Android.bp tools/aapt/Android.bp tools/aapt2/Android.bp tools/aapt2/integration-tests/AutoVersionTest/Android.bp tools/aapt2/integration-tests/BasicTest/Android.bp tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk tools/aapt2/integration-tests/StaticLibTest/App/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp tools/aapt2/integration-tests/SymlinkTest/Android.bp tools/bit/Android.bp tools/codegen/Android.bp tools/dump-coverage/Android.bp tools/incident_report/Android.bp tools/incident_section_gen/Android.bp tools/lock_agent/Android.bp tools/locked_region_code_injection/Android.bp tools/obbtool/Android.bp tools/powermodel/Android.bp tools/preload-check/Android.bp tools/preload-check/device/Android.bp tools/preload/loadclass/Android.bp tools/processors/staledataclass/Android.bp tools/processors/view_inspector/Android.bp tools/protologtool/Android.bp tools/sdkparcelables/Android.bp tools/split-select/Android.bp tools/streaming_proto/Android.bp tools/validatekeymaps/Android.bp wifi/java/Android.bp wifi/tests/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD to: libs/hwui/Android.bp native/webview/plat_support/Android.bp obex/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD SPDX-license-identifier-CC-BY SPDX-license-identifier-CPL-1.0 SPDX-license-identifier-GPL SPDX-license-identifier-GPL-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS SPDX-license-identifier-W3C legacy_unencumbered to: Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_unencumbered to: core/java/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-CPL-1.0 to: test-base/Android.bp test-runner/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL to: core/res/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL-2.0 to: libs/usb/Android.bp libs/usb/tests/accessorytest/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT to: tools/preload/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS to: api/Android.bp boot/Android.bp cmds/device_config/Android.bp cmds/settings/Android.bp core/api/Android.bp core/tests/coretests/certs/Android.bp core/tests/overlaytests/remount/test-apps/certs/Android.bp core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp libs/tracingproxy/Android.bp services/startop/Android.bp test-legacy/Android.mk tests/ApkVerityTest/testdata/Android.bp tests/TransitionTests/Android.bp Bug: 68860345 Bug: 151177513 Bug: 151953481 Test: m all Exempt-From-Owner-Approval: janitorial work Change-Id: Ic44d662936d1ff0cae7fbe915932b37aa4e4869a Merged-in: Ic44d662936d1ff0cae7fbe915932b37aa4e4869a --- framework/tests/Android.bp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/tests/Android.bp b/framework/tests/Android.bp index 4b6f9db33af..a2e4dff8f82 100644 --- a/framework/tests/Android.bp +++ b/framework/tests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BluetoothTests", // Include all test java files. -- GitLab From dce3ab070dda29fcde4fe2614bafe1e5e145b34c Mon Sep 17 00:00:00 2001 From: More Kuo Date: Fri, 19 Feb 2021 18:00:41 +0800 Subject: [PATCH 1232/1408] Update BluetoothA2dp API - Rename setBufferMillis to setBufferLengthMillis - Rename getCodec to forCodec Bug: 179814567 Test: Build Change-Id: Iadb1b9e2b42ecab98f70b8bf6500e7a97aaf0aab --- framework/java/android/bluetooth/BluetoothA2dp.java | 7 ++++--- framework/java/android/bluetooth/BufferConstraints.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index cd91aa9b16b..53aaae0470e 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -943,12 +943,13 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { - if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")"); + public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, + int value) { + if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.setBufferMillis(codec, value); + return service.setBufferLengthMillis(codec, value); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/framework/java/android/bluetooth/BufferConstraints.java b/framework/java/android/bluetooth/BufferConstraints.java index 7e5ec1e7843..97d97232b7a 100644 --- a/framework/java/android/bluetooth/BufferConstraints.java +++ b/framework/java/android/bluetooth/BufferConstraints.java @@ -90,7 +90,7 @@ public final class BufferConstraints implements Parcelable { * @hide */ @SystemApi - public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) { + public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) { return mBufferConstraints.get(codec); } } -- GitLab From 1db9bd4e9a640c133d121f1d0a017059be2086a3 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Thu, 21 Jan 2021 11:22:44 -0800 Subject: [PATCH 1233/1408] 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 --- .../android/bluetooth/BluetoothMapClient.java | 101 +++++++++++++++--- .../android/bluetooth/BluetoothProfile.java | 1 + 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index ff6cffb272a..0312a2190a4 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) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index c31b04e8145..201d6c495d9 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -186,6 +186,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int MAP_CLIENT = 18; /** -- GitLab From bec67a5c678926b62fd1337316027e2fd0ff854f Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Fri, 19 Feb 2021 18:42:38 +0000 Subject: [PATCH 1234/1408] Make BluetoothConnectionCallback static Tag: #refactor Fixes: 180727461 Test: atest BluetoothInstrumentationTests Change-Id: I4bd084cb6c0371b649adb1d1a65b3327ae3e488a Merged-In: Id24a4b3a3510781d9105763b1722f44583a7fd7c --- framework/java/android/bluetooth/BluetoothAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b7203e3e36b..d254340f49c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3665,7 +3665,7 @@ public final class BluetoothAdapter { * * @hide */ - public abstract class BluetoothConnectionCallback { + public abstract static class BluetoothConnectionCallback { /** * Callback triggered when a bluetooth device (classic or BLE) is connected * @param device is the connected bluetooth device -- GitLab From 8ab39e54b5bcde03c762e7af325645da2a2d5652 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Thu, 14 Jan 2021 16:21:48 -0800 Subject: [PATCH 1235/1408] Surface ACL disconnect reasons from native to Java HCI disconnect commands and events have an accompanying "reason" parameter comprising of a HCI error code. This can be useful in both debugging and re-connection logic at the Java level. This CL grabs the HCI codes from native and passes it up to Java via an extra parameter to existing ACL connection callbacks. Tag: #feature Bug: 177668957 Test: atest BluetoothInstrumentationTests Change-Id: Ic729140b8ffb1273a9ca53cdfd39c9f5d71e1522 --- .../android/bluetooth/BluetoothAdapter.java | 153 +++++++++++++++++- 1 file changed, 150 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d254340f49c..cc0b22afe38 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3565,12 +3565,12 @@ public final class BluetoothAdapter { } @Override - public void onDeviceDisconnected(BluetoothDevice device) { + public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { for (Map.Entry callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); - executor.execute(() -> callback.onDeviceDisconnected(device)); + executor.execute(() -> callback.onDeviceDisconnected(device, hciReason)); } } }; @@ -3675,8 +3675,155 @@ public final class BluetoothAdapter { /** * Callback triggered when a bluetooth device (classic or BLE) is disconnected * @param device is the disconnected bluetooth device + * @param reason is the disconnect reason */ - public void onDeviceDisconnected(BluetoothDevice device) {} + public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {} + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "REASON_" }, value = { + REASON_UNKNOWN, + REASON_LOCAL_REQUEST, + REASON_REMOTE_REQUEST, + REASON_LOCAL_ERROR, + REASON_REMOTE_ERROR, + REASON_TIMEOUT, + REASON_SECURITY, + REASON_SYSTEM_POLICY, + REASON_RESOURCE_LIMIT_REACHED, + REASON_CONNECTION_EXISTS, + REASON_BAD_PARAMETERS}) + public @interface DisconnectReason {} + + /** + * Indicates that the ACL disconnected due to an unknown reason. + */ + public static final int REASON_UNKNOWN = 0; + + /** + * Indicates that the ACL disconnected due to an explicit request from the local device. + *

                + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + */ + public static final int REASON_LOCAL_REQUEST = 1; + + /** + * Indicates that the ACL disconnected due to an explicit request from the remote device. + *

                + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + *

                + * Example solution: The app can also prompt the user to check their remote device. + */ + public static final int REASON_REMOTE_REQUEST = 2; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the local + * device. + *

                + * Example solution: Prompt the user to check their local device (e.g., phone, car + * headunit). + */ + public static final int REASON_LOCAL_ERROR = 3; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the remote + * device. + *

                + * Example solution: Prompt the user to check their remote device (e.g., headset, car + * headunit, watch). + */ + public static final int REASON_REMOTE_ERROR = 4; + + /** + * Indicates that the ACL disconnected due to a timeout. + *

                + * Example cause: remote device might be out of range. + *

                + * Example solution: Prompt user to verify their remote device is on or in + * connection/pairing mode. + */ + public static final int REASON_TIMEOUT = 5; + + /** + * Indicates that the ACL disconnected due to link key issues. + *

                + * Example cause: Devices are either unpaired or remote device is refusing our pairing + * request. + *

                + * Example solution: Prompt user to unpair and pair again. + */ + public static final int REASON_SECURITY = 6; + + /** + * Indicates that the ACL disconnected due to the local device's system policy. + *

                + * Example cause: privacy policy, power management policy, permissions, etc. + *

                + * Example solution: Prompt the user to check settings, or check with their system + * administrator (e.g. some corp-managed devices do not allow OPP connection). + */ + public static final int REASON_SYSTEM_POLICY = 7; + + /** + * Indicates that the ACL disconnected due to resource constraints, either on the local + * device or the remote device. + *

                + * Example cause: controller is busy, memory limit reached, maximum number of connections + * reached. + *

                + * Example solution: The app should wait and try again. If still failing, prompt the user + * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. + */ + public static final int REASON_RESOURCE_LIMIT_REACHED = 8; + + /** + * Indicates that the ACL disconnected because another ACL connection already exists. + */ + public static final int REASON_CONNECTION_EXISTS = 9; + + /** + * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. + *

                + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + */ + public static final int REASON_BAD_PARAMETERS = 10; + + /** + * Returns human-readable strings corresponding to {@link DisconnectReason}. + */ + public static String disconnectReasonText(@DisconnectReason int reason) { + switch (reason) { + case REASON_UNKNOWN: + return "Reason unknown"; + case REASON_LOCAL_REQUEST: + return "Local request"; + case REASON_REMOTE_REQUEST: + return "Remote request"; + case REASON_LOCAL_ERROR: + return "Local error"; + case REASON_REMOTE_ERROR: + return "Remote error"; + case REASON_TIMEOUT: + return "Timeout"; + case REASON_SECURITY: + return "Security"; + case REASON_SYSTEM_POLICY: + return "System policy"; + case REASON_RESOURCE_LIMIT_REACHED: + return "Resource constrained"; + case REASON_CONNECTION_EXISTS: + return "Connection already exists"; + case REASON_BAD_PARAMETERS: + return "Bad parameters"; + default: + return "Unrecognized disconnect reason: " + reason; + } + } } /** -- GitLab From 779312a7e49794a739c11e7f6c4474ee148adc8d Mon Sep 17 00:00:00 2001 From: Bob Badour Date: Fri, 12 Feb 2021 17:07:05 -0800 Subject: [PATCH 1236/1408] [LSC] Add LOCAL_LICENSE_KINDS to frameworks/base Added SPDX-license-identifier-Apache-2.0 to: apct-tests/perftests/autofill/Android.bp apct-tests/perftests/blobstore/Android.bp apct-tests/perftests/core/Android.bp apct-tests/perftests/core/apps/overlay/Android.bp apct-tests/perftests/core/apps/reources_manager/Android.bp apct-tests/perftests/core/jni/Android.bp apct-tests/perftests/multiuser/Android.bp apct-tests/perftests/multiuser/apps/dummyapp/Android.bp apct-tests/perftests/packagemanager/Android.bp apct-tests/perftests/packagemanager/apps/query-all/Android.bp apct-tests/perftests/textclassifier/Android.bp apct-tests/perftests/utils/Android.bp apct-tests/perftests/windowmanager/Android.bp apex/Android.bp apex/blobstore/framework/Android.bp apex/blobstore/service/Android.bp apex/jobscheduler/framework/Android.bp apex/jobscheduler/service/Android.bp apex/media/Android.bp apex/media/aidl/Android.bp apex/media/framework/Android.bp cmds/am/Android.bp cmds/app_process/Android.bp cmds/appops/Android.bp cmds/appwidget/Android.bp cmds/backup/Android.bp cmds/bmgr/Android.bp cmds/bootanimation/Android.bp cmds/bu/Android.bp cmds/content/Android.bp cmds/dpm/Android.bp cmds/hid/Android.bp cmds/hid/jni/Android.bp cmds/idmap2/Android.bp cmds/ime/Android.bp cmds/incident/Android.bp cmds/incident_helper/Android.bp cmds/incidentd/Android.bp cmds/input/Android.bp cmds/interrupter/Android.bp cmds/locksettings/Android.bp cmds/pm/Android.bp cmds/requestsync/Android.bp cmds/screencap/Android.bp cmds/sm/Android.bp cmds/svc/Android.bp cmds/telecom/Android.bp cmds/uiautomator/Android.bp cmds/uiautomator/cmds/uiautomator/Android.bp cmds/uiautomator/instrumentation/Android.bp cmds/uiautomator/library/Android.bp cmds/vr/Android.bp cmds/wm/Android.bp config/Android.bp core/java/android/service/wallpaper/Android.bp core/jni/Android.bp core/sysprop/Android.bp core/tests/BroadcastRadioTests/Android.bp core/tests/ConnectivityManagerTest/Android.bp core/tests/PackageInstallerSessions/Android.bp core/tests/PlatformCompatFramework/Android.bp core/tests/bandwidthtests/Android.bp core/tests/benchmarks/Android.bp core/tests/bluetoothtests/Android.bp core/tests/bugreports/Android.bp core/tests/coretests/Android.bp core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp core/tests/coretests/BinderProxyCountingTestApp/Android.bp core/tests/coretests/BinderProxyCountingTestService/Android.bp core/tests/coretests/BstatsTestApp/Android.bp core/tests/coretests/DisabledTestApp/Android.bp core/tests/coretests/EnabledTestApp/Android.bp core/tests/coretests/aidl/Android.bp core/tests/coretests/apks/Android.bp core/tests/coretests/apks/install/Android.bp core/tests/coretests/apks/install_bad_dex/Android.bp core/tests/coretests/apks/install_complete_package_info/Android.bp core/tests/coretests/apks/install_decl_perm/Android.bp core/tests/coretests/apks/install_jni_lib/Android.bp core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp core/tests/coretests/apks/install_loc_auto/Android.bp core/tests/coretests/apks/install_loc_internal/Android.bp core/tests/coretests/apks/install_loc_sdcard/Android.bp core/tests/coretests/apks/install_loc_unspecified/Android.bp core/tests/coretests/apks/install_use_perm_good/Android.bp core/tests/coretests/apks/install_uses_feature/Android.bp core/tests/coretests/apks/install_verifier_bad/Android.bp core/tests/coretests/apks/install_verifier_good/Android.bp core/tests/coretests/apks/keyset/Android.bp core/tests/coretests/apks/locales/Android.bp core/tests/coretests/apks/overlay_config/Android.bp core/tests/coretests/apks/version/Android.bp core/tests/coretests/apks/version_nosys/Android.bp core/tests/featureflagtests/Android.bp core/tests/hdmitests/Android.bp core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp core/tests/hosttests/test-apps/NoLocTestApp/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp core/tests/hosttests/test-apps/SharedUid/32/Android.bp core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/64/Android.bp core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/Android.bp core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp core/tests/hosttests/test-apps/SimpleTestApp/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp core/tests/mockingcoretests/Android.bp core/tests/notificationtests/Android.bp core/tests/overlaytests/device/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp core/tests/overlaytests/host/Android.bp core/tests/overlaytests/remount/Android.bp core/tests/overlaytests/remount/test-apps/Overlay/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp core/tests/overlaytests/remount/test-apps/Target/Android.bp core/tests/packagemanagertests/Android.bp core/tests/privacytests/Android.bp core/tests/screenshothelpertests/Android.bp core/tests/systemproperties/Android.bp core/tests/utillib/Android.bp core/tests/utiltests/Android.bp core/tests/utiltests/jni/Android.bp core/tests/uwbtests/Android.bp core/xsd/Android.bp core/xsd/vts/Android.bp data/etc/Android.bp data/etc/car/Android.bp data/fonts/Android.bp data/keyboards/Android.mk drm/jni/Android.bp errorprone/Android.bp graphics/proto/Android.bp keystore/Android.bp keystore/tests/Android.bp libs/WindowManager/Jetpack/Android.bp libs/WindowManager/Shell/Android.bp libs/WindowManager/Shell/tests/Android.bp libs/androidfw/Android.bp libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp libs/hostgraphics/Android.bp libs/incident/Android.bp libs/input/Android.bp libs/input/tests/Android.bp libs/protoutil/Android.bp libs/services/Android.bp libs/storage/Android.bp libs/usb/tests/AccessoryChat/Android.bp libs/usb/tests/AccessoryChat/accessorychat/Android.bp location/lib/Android.bp location/tests/Android.bp location/tests/locationtests/Android.bp lowpan/tests/Android.bp media/Android.bp media/java/Android.bp media/java/android/media/tv/tunerresourcemanager/Android.bp media/jni/Android.bp media/jni/audioeffect/Android.bp media/jni/soundpool/Android.bp media/jni/soundpool/tests/Android.bp media/lib/remotedisplay/Android.bp media/lib/signer/Android.bp media/lib/tvremote/Android.bp media/lib/tvremote/tests/Android.bp media/mca/filterfw/Android.bp media/mca/filterfw/native/Android.bp media/mca/filterpacks/Android.bp media/mca/samples/CameraEffectsRecordingSample/Android.bp media/mca/tests/Android.bp media/native/midi/Android.bp media/packages/BluetoothMidiService/Android.bp media/packages/BluetoothMidiService/tests/unit/Android.bp media/tests/AudioPolicyTest/Android.bp media/tests/CameraBrowser/Android.bp media/tests/EffectsTest/Android.bp media/tests/MediaDump/Android.bp media/tests/MediaFrameworkTest/Android.bp media/tests/MediaRouter/Android.bp media/tests/MtpTests/Android.bp media/tests/ScoAudioTest/Android.bp media/tests/SoundPoolTest/Android.bp media/tests/TunerTest/Android.bp media/tests/audiotests/Android.bp media/tests/players/Android.bp mime/Android.bp native/android/Android.bp native/graphics/jni/Android.bp native/webview/loader/Android.bp nfc-extras/Android.bp nfc-extras/tests/Android.bp packages/AppPredictionLib/Android.bp packages/BackupEncryption/Android.bp packages/BackupEncryption/test/robolectric-integration/Android.bp packages/BackupEncryption/test/robolectric/Android.bp packages/BackupEncryption/test/unittest/Android.bp packages/BackupRestoreConfirmation/Android.bp packages/CarSystemUI/Android.bp packages/CarrierDefaultApp/Android.bp packages/CarrierDefaultApp/tests/unit/Android.bp packages/CompanionDeviceManager/Android.bp packages/Connectivity/framework/Android.bp packages/Connectivity/service/Android.bp packages/CtsShim/Android.bp packages/CtsShim/build/Android.bp packages/CtsShim/build/jni/Android.bp packages/DynamicSystemInstallationService/Android.bp packages/DynamicSystemInstallationService/tests/Android.bp packages/EasterEgg/Android.bp packages/EncryptedLocalTransport/Android.bp packages/ExtShared/Android.bp packages/ExternalStorageProvider/Android.bp packages/ExternalStorageProvider/tests/Android.bp packages/FakeOemFeatures/Android.bp packages/FusedLocation/Android.bp packages/InputDevices/Android.bp packages/LocalTransport/Android.bp packages/PackageInstaller/Android.bp packages/PrintRecommendationService/Android.bp packages/PrintSpooler/Android.bp packages/PrintSpooler/jni/Android.bp packages/PrintSpooler/tests/outofprocess/Android.bp packages/SettingsLib/ActionBarShadow/Android.bp packages/SettingsLib/ActionButtonsPreference/Android.bp packages/SettingsLib/AdaptiveIcon/Android.bp packages/SettingsLib/Android.bp packages/SettingsLib/AppPreference/Android.bp packages/SettingsLib/BarChartPreference/Android.bp packages/SettingsLib/DisplayDensityUtils/Android.bp packages/SettingsLib/EntityHeaderWidgets/Android.bp packages/SettingsLib/HelpUtils/Android.bp packages/SettingsLib/LayoutPreference/Android.bp packages/SettingsLib/ProgressBar/Android.bp packages/SettingsLib/RadioButtonPreference/Android.bp packages/SettingsLib/RestrictedLockUtils/Android.bp packages/SettingsLib/SchedulesProvider/Android.bp packages/SettingsLib/SearchProvider/Android.bp packages/SettingsLib/SearchWidget/Android.bp packages/SettingsLib/SettingsSpinner/Android.bp packages/SettingsLib/SettingsTheme/Android.bp packages/SettingsLib/Tile/Android.bp packages/SettingsLib/Utils/Android.bp packages/SettingsLib/search/Android.bp packages/SettingsLib/tests/integ/Android.bp packages/SettingsLib/tests/robotests/Android.bp packages/SettingsProvider/Android.bp packages/SharedStorageBackup/Android.bp packages/Shell/Android.bp packages/Shell/tests/Android.bp packages/SimAppDialog/Android.bp packages/SoundPicker/Android.bp packages/StatementService/Android.bp packages/SystemUI/Android.bp packages/SystemUI/plugin/Android.bp packages/SystemUI/plugin/ExamplePlugin/Android.bp packages/SystemUI/plugin_core/Android.bp packages/SystemUI/shared/Android.bp packages/VpnDialogs/Android.bp packages/WAPPushManager/Android.bp packages/WAPPushManager/tests/Android.bp packages/WallpaperBackup/Android.bp packages/WallpaperCropper/Android.bp packages/overlays/Android.mk packages/overlays/tests/Android.bp packages/services/PacProcessor/Android.bp packages/services/PacProcessor/jni/Android.bp packages/services/Proxy/Android.bp proto/Android.bp rs/jni/Android.mk samples/demo/haptic-assessment/Android.bp sax/tests/saxtests/Android.bp services/Android.bp services/accessibility/Android.bp services/appprediction/Android.bp services/appwidget/Android.bp services/autofill/Android.bp services/backup/Android.bp services/backup/backuplib/Android.bp services/companion/Android.bp services/contentcapture/Android.bp services/contentsuggestions/Android.bp services/core/Android.bp services/core/java/com/android/server/vcn/Android.bp services/core/jni/Android.bp services/core/xsd/Android.bp services/core/xsd/vts/Android.bp services/coverage/Android.bp services/devicepolicy/Android.bp services/incremental/Android.bp services/midi/Android.bp services/net/Android.bp services/people/Android.bp services/print/Android.bp services/profcollect/Android.bp services/restrictions/Android.bp services/robotests/Android.bp services/robotests/backup/Android.bp services/systemcaptions/Android.bp services/tests/PackageManagerComponentOverrideTests/Android.bp services/tests/PackageManagerServiceTests/host/Android.bp services/tests/PackageManagerServiceTests/host/test-apps/Android.bp services/tests/mockingservicestests/Android.bp services/tests/rescueparty/Android.bp services/tests/servicestests/Android.bp services/tests/servicestests/aidl/Android.bp services/tests/servicestests/apks/Android.bp services/tests/servicestests/apks/install-split-base/Android.bp services/tests/servicestests/apks/install-split-feature-a/Android.bp services/tests/servicestests/apks/install_intent_filters/Android.bp services/tests/servicestests/apks/install_uses_sdk/Android.bp services/tests/servicestests/test-apps/ConnTestApp/Android.bp services/tests/servicestests/test-apps/JobTestApp/Android.bp services/tests/servicestests/test-apps/PackageParserApp/Android.bp services/tests/servicestests/test-apps/PackageParsingTestManifests/Android.bp services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp services/tests/servicestests/test-apps/SuspendTestApp/Android.bp services/tests/shortcutmanagerutils/Android.bp services/tests/uiservicestests/Android.bp services/tests/wmtests/Android.bp services/usage/Android.bp services/usb/Android.bp services/voiceinteraction/Android.bp services/wifi/Android.bp startop/apps/test/Android.bp startop/iorap/Android.bp startop/iorap/functional_tests/Android.bp startop/iorap/stress/Android.bp startop/iorap/tests/Android.bp startop/view_compiler/Android.bp startop/view_compiler/dex_builder_test/Android.bp test-base/hiddenapi/Android.bp test-mock/Android.bp test-runner/tests/Android.bp tests/AccessibilityEventsLogger/Android.bp tests/AccessoryDisplay/common/Android.bp tests/AccessoryDisplay/sink/Android.bp tests/AccessoryDisplay/source/Android.bp tests/ActivityManagerPerfTests/stub-app/Android.bp tests/ActivityManagerPerfTests/test-app/Android.bp tests/ActivityManagerPerfTests/tests/Android.bp tests/ActivityManagerPerfTests/utils/Android.bp tests/ActivityTests/Android.bp tests/ActivityViewTest/Android.bp tests/AmSlam/Android.bp tests/ApkVerityTest/Android.bp tests/ApkVerityTest/ApkVerityTestApp/Android.bp tests/ApkVerityTest/block_device_writer/Android.bp tests/AppLaunch/Android.bp tests/AppLaunchWear/Android.bp tests/AppResourcesLoaders/Android.bp tests/AppResourcesLoaders/Overlay/Android.bp tests/Assist/Android.bp tests/AutoVerify/app1/Android.bp tests/AutoVerify/app2/Android.bp tests/AutoVerify/app3/Android.bp tests/AutoVerify/app4/Android.bp tests/BackgroundDexOptServiceIntegrationTests/Android.bp tests/BandwidthTests/Android.bp tests/BatteryWaster/Android.bp tests/BiDiTests/Android.bp tests/BlobStoreTestUtils/Android.bp tests/BootImageProfileTest/Android.bp tests/BrowserPowerTest/Android.bp tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp tests/CameraPrewarmTest/Android.bp tests/Codegen/Android.bp tests/Compatibility/Android.bp tests/CoreTests/android/Android.bp tests/DataIdleTest/Android.bp tests/DozeTest/Android.bp tests/DpiTest/Android.bp tests/DynamicCodeLoggerIntegrationTests/Android.mk tests/FeatureSplit/base/Android.bp tests/FeatureSplit/feature1/Android.bp tests/FeatureSplit/feature2/Android.bp tests/FixVibrateSetting/Android.bp tests/FlickerTests/Android.bp tests/FlickerTests/test-apps/Android.bp tests/FlickerTests/test-apps/flickerapp/Android.bp tests/FrameworkPerf/Android.bp tests/GamePerformance/Android.bp tests/GridLayoutTest/Android.bp tests/HierarchyViewerTest/Android.bp tests/HugeBackup/Android.bp tests/HwAccelerationTest/Android.bp tests/Internal/Android.bp tests/JankBench/Android.bp tests/JobSchedulerPerfTests/Android.bp tests/JobSchedulerTestApp/Android.bp tests/LargeAssetTest/Android.bp tests/LegacyAssistant/Android.bp tests/LocalizationTest/Android.bp tests/LocationTracker/Android.bp tests/LotsOfApps/Android.bp tests/LowStorageTest/Android.bp tests/ManagedProfileLifecycleStressTest/Android.bp tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp tests/MemoryUsage/Android.bp tests/MirrorSurfaceTest/Android.bp tests/NativeProcessesMemoryTest/Android.bp tests/NetworkSecurityConfigTest/Android.bp tests/NullHomeTest/Android.bp tests/OdmApps/Android.bp tests/OdmApps/app/Android.bp tests/OdmApps/priv-app/Android.bp tests/OneMedia/Android.bp tests/PackageWatchdog/Android.bp tests/PlatformCompatGating/Android.bp tests/PlatformCompatGating/test-rules/Android.bp tests/ProtoInputStreamTests/Android.bp tests/RemoteDisplayProvider/Android.bp tests/RenderThreadTest/Android.bp tests/RollbackTest/Android.bp tests/SerialChat/Android.bp tests/ServiceCrashTest/Android.bp tests/SharedLibrary/client/Android.bp tests/SharedLibrary/lib/Android.bp tests/ShowWhenLockedApp/Android.bp tests/SmokeTest/Android.bp tests/SmokeTest/tests/Android.bp tests/SmokeTestApps/Android.bp tests/SoundTriggerTestApp/Android.bp tests/Split/Android.bp tests/StagedInstallTest/Android.bp tests/StatusBar/Android.bp tests/SurfaceComposition/Android.bp tests/SurfaceControlViewHostTest/Android.bp tests/SystemMemoryTest/device/Android.bp tests/SystemMemoryTest/host/Android.bp tests/SystemUIDemoModeController/Android.bp tests/TaskOrganizerTest/Android.bp tests/TelephonyCommonTests/Android.bp tests/TouchLatency/Android.bp tests/TransformTest/Android.bp tests/TtsTests/Android.bp tests/UiBench/Android.bp tests/UsageReportingTest/Android.bp tests/UsageStatsPerfTests/Android.bp tests/UsageStatsTest/Android.bp tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp tests/UsbManagerTests/Android.bp tests/UsbManagerTests/lib/Android.bp tests/UsbTests/Android.bp tests/UsesFeature2Test/Android.bp tests/VectorDrawableTest/Android.bp tests/VoiceEnrollment/Android.bp tests/VoiceInteraction/Android.bp tests/WallpaperTest/Android.bp tests/WindowAnimationJank/Android.bp tests/WindowInsetsTests/Android.bp tests/appwidgets/AppWidgetHostTest/Android.bp tests/appwidgets/AppWidgetProviderTest/Android.bp tests/backup/Android.mk tests/benchmarks/Android.bp tests/libs-permissions/Android.bp tests/net/Android.bp tests/net/common/Android.bp tests/net/deflake/Android.bp tests/net/integration/Android.bp tests/net/jni/Android.bp tests/net/smoketest/Android.bp tests/notification/Android.bp tests/permission/Android.bp tests/privapp-permissions/Android.bp tests/testables/Android.bp tests/testables/tests/Android.bp tests/utils/StubIME/Android.bp tests/utils/hostutils/Android.bp tests/utils/testutils/Android.bp tests/vcn/Android.bp tools/aapt/Android.bp tools/aapt2/Android.bp tools/aapt2/integration-tests/AutoVersionTest/Android.bp tools/aapt2/integration-tests/BasicTest/Android.bp tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk tools/aapt2/integration-tests/StaticLibTest/App/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp tools/aapt2/integration-tests/SymlinkTest/Android.bp tools/bit/Android.bp tools/codegen/Android.bp tools/dump-coverage/Android.bp tools/incident_report/Android.bp tools/incident_section_gen/Android.bp tools/lock_agent/Android.bp tools/locked_region_code_injection/Android.bp tools/obbtool/Android.bp tools/powermodel/Android.bp tools/preload-check/Android.bp tools/preload-check/device/Android.bp tools/preload/loadclass/Android.bp tools/processors/staledataclass/Android.bp tools/processors/view_inspector/Android.bp tools/protologtool/Android.bp tools/sdkparcelables/Android.bp tools/split-select/Android.bp tools/streaming_proto/Android.bp tools/validatekeymaps/Android.bp wifi/java/Android.bp wifi/tests/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD to: libs/hwui/Android.bp native/webview/plat_support/Android.bp obex/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD SPDX-license-identifier-CC-BY SPDX-license-identifier-CPL-1.0 SPDX-license-identifier-GPL SPDX-license-identifier-GPL-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS SPDX-license-identifier-W3C legacy_unencumbered to: Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD legacy_unencumbered to: core/java/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-CPL-1.0 to: test-base/Android.bp test-runner/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL to: core/res/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-GPL-2.0 to: libs/usb/Android.bp libs/usb/tests/accessorytest/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT to: tools/preload/Android.bp Added SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS to: api/Android.bp boot/Android.bp cmds/device_config/Android.bp cmds/settings/Android.bp core/api/Android.bp core/tests/coretests/certs/Android.bp core/tests/overlaytests/remount/test-apps/certs/Android.bp core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp libs/tracingproxy/Android.bp services/startop/Android.bp test-legacy/Android.mk tests/ApkVerityTest/testdata/Android.bp tests/TransitionTests/Android.bp Bug: 68860345 Bug: 151177513 Bug: 151953481 Test: m all Exempt-From-Owner-Approval: janitorial work Change-Id: Ib9737d8fb5ef5b90a2c14fe71f1a571079edcf02 Merged-In: Ib9737d8fb5ef5b90a2c14fe71f1a571079edcf02 --- framework/tests/Android.bp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/tests/Android.bp b/framework/tests/Android.bp index 4b6f9db33af..a2e4dff8f82 100644 --- a/framework/tests/Android.bp +++ b/framework/tests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + android_test { name: "BluetoothTests", // Include all test java files. -- GitLab From 72a90b04db12e62378c252aabea437db4f5f72c7 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Fri, 19 Feb 2021 16:52:42 +0800 Subject: [PATCH 1237/1408] Additional metadata keys for Fast Pair Tag: #feature Bug: 171387275 Test: atest BluetoothInstrumentationTests Change-Id: I98f4cbd214437d1cd3681be03108c824ad087260 --- .../android/bluetooth/BluetoothDevice.java | 126 +++++++++++++++++- 1 file changed, 124 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0f864a81c14..89030bcf12e 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -354,6 +354,35 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SDP_RECORD = "android.bluetooth.device.action.SDP_RECORD"; + /** @hide */ + @IntDef(prefix = "METADATA_", value = { + METADATA_MANUFACTURER_NAME, + METADATA_MODEL_NAME, + METADATA_SOFTWARE_VERSION, + METADATA_HARDWARE_VERSION, + METADATA_COMPANION_APP, + METADATA_MAIN_ICON, + METADATA_IS_UNTETHERED_HEADSET, + METADATA_UNTETHERED_LEFT_ICON, + METADATA_UNTETHERED_RIGHT_ICON, + METADATA_UNTETHERED_CASE_ICON, + METADATA_UNTETHERED_LEFT_BATTERY, + METADATA_UNTETHERED_RIGHT_BATTERY, + METADATA_UNTETHERED_CASE_BATTERY, + METADATA_UNTETHERED_LEFT_CHARGING, + METADATA_UNTETHERED_RIGHT_CHARGING, + METADATA_UNTETHERED_CASE_CHARGING, + METADATA_ENHANCED_SETTINGS_UI_URI, + METADATA_DEVICE_TYPE, + METADATA_MAIN_BATTERY, + METADATA_MAIN_CHARGING, + METADATA_MAIN_LOW_BATTERY_THRESHOLD, + METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD, + METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD, + METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD}) + @Retention(RetentionPolicy.SOURCE) + public @interface MetadataKey{} + /** * Maximum length of a metadata entry, this is to avoid exploding Bluetooth * disk usage @@ -502,6 +531,89 @@ public final class BluetoothDevice implements Parcelable { @SystemApi public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; + /** + * Type of the Bluetooth device, must be within the list of + * BluetoothDevice.DEVICE_TYPE_* + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_DEVICE_TYPE = 17; + + /** + * Battery level of the Bluetooth device, use when the Bluetooth device + * does not support HFP battery indicator. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_MAIN_BATTERY = 18; + + /** + * Whether the device is charging. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_MAIN_CHARGING = 19; + + /** + * The battery threshold of the Bluetooth device to show low battery icon. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20; + + /** + * The battery threshold of the left headset to show low battery icon. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21; + + /** + * The battery threshold of the right headset to show low battery icon. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22; + + /** + * The battery threshold of the case to show low battery icon. + * Data type should be {@String} as {@link Byte} array. + * @hide + */ + @SystemApi + public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23; + + /** + * Device type which is used in METADATA_DEVICE_TYPE + * Indicates this Bluetooth device is a standard Bluetooth accessory or + * not listed in METADATA_DEVICE_TYPE_*. + * @hide + */ + @SystemApi + public static final String DEVICE_TYPE_DEFAULT = "Default"; + + /** + * Device type which is used in METADATA_DEVICE_TYPE + * Indicates this Bluetooth device is a watch. + * @hide + */ + @SystemApi + public static final String DEVICE_TYPE_WATCH = "Watch"; + + /** + * Device type which is used in METADATA_DEVICE_TYPE + * Indicates this Bluetooth device is an untethered headset. + * @hide + */ + @SystemApi + public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset"; + /** * Broadcast Action: This intent is used to broadcast the {@link UUID} * wrapped as a {@link android.os.ParcelUuid} of the remote device after it @@ -2258,7 +2370,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setMetadata(int key, @NonNull byte[] value) { + public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); @@ -2286,7 +2398,7 @@ public final class BluetoothDevice implements Parcelable { @SystemApi @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public byte[] getMetadata(int key) { + public byte[] getMetadata(@MetadataKey int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); @@ -2299,4 +2411,14 @@ public final class BluetoothDevice implements Parcelable { return null; } } + + /** + * Get the maxinum metadata key ID. + * + * @return the last supported metadata key + * @hide + */ + public static @MetadataKey int getMaxMetadataKey() { + return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD; + } } -- GitLab From 54d0cf786c565625dd5211b0bb22de99165f63bc Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 3 Mar 2021 13:58:34 +0100 Subject: [PATCH 1238/1408] Fix comment typo in PeriodicAdvertisingParameters Bug: 176967118 Test: m ds-docs-java Exempt-From-Owner-Approval: Docs-only change Change-Id: I1777ca23ba6f395eeb2ec98b970ccbf920661663 --- .../android/bluetooth/le/PeriodicAdvertisingParameters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java index e3a130c4b43..4e64dbed701 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingParameters.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic * advertising preferences for each Bluetooth LE advertising set. Use {@link - * AdvertisingSetParameters.Builder} to create an instance of this class. + * PeriodicAdvertisingParameters.Builder} to create an instance of this class. */ public final class PeriodicAdvertisingParameters implements Parcelable { -- GitLab From ab1f25e5dfedf2463e577b3d32ab3c5e720d5f65 Mon Sep 17 00:00:00 2001 From: Mathew Inwood Date: Tue, 9 Mar 2021 09:35:23 +0000 Subject: [PATCH 1239/1408] Derestrict some APIs. They were retricted because we thought they were unused, but it turns out that they are needed afterall. NoNonSdkCheck: 170729553 Bug: 181103983 Test: N/A Change-Id: Iddf7916456be27d60d2a7520d7cadcda1d04cac6 --- framework/java/android/bluetooth/BluetoothA2dp.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 53aaae0470e..16413e1a1db 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -139,7 +139,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 181103983) public static final String ACTION_CODEC_CONFIG_CHANGED = "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; @@ -684,7 +684,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return the current codec status * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 181103983) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { @@ -713,7 +713,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param codecConfig the codec configuration preference * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 181103983) @RequiresPermission(Manifest.permission.BLUETOOTH) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { -- GitLab From 6a20b26a6704ea9ab9d9084f4493c88763f3c074 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Thu, 21 Jan 2021 10:29:05 -0800 Subject: [PATCH 1240/1408] Bluetooth: Modify and append to the Out-of-Band API - Modify createOutOfBand to be a SystemApi, and accept p192 and p256 data objects. - Modify OobData to become a SystemApi and to provide a Builder pattern for creation. CTS-Coverage-Bug: 182420103 Bug: 178007935 Test: compiles and runs Tag: #feature Change-Id: I46aec8c2cb64a8da8957d01d32b879d60df7a31c --- .../android/bluetooth/BluetoothDevice.java | 52 +- framework/java/android/bluetooth/OobData.java | 983 +++++++++++++++++- 2 files changed, 968 insertions(+), 67 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index ec94faa544d..07dbdce3984 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1311,7 +1311,6 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

                Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @param transport The transport to use for the pairing procedure. * @return false on immediate error, true if bonding will begin @@ -1319,8 +1318,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond(int transport) { - return createBondOutOfBand(transport, null); + return createBondInternal(transport, null, null); } /** @@ -1334,22 +1334,39 @@ public final class BluetoothDevice implements Parcelable { *

                Android system services will handle the necessary user interactions * to confirm and complete the bonding process. * - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

                There are two possible versions of OOB Data. This data can come in as + * P192 or P256. This is a reference to the cryptography used to generate the key. + * The caller may pass one or both. If both types of data are passed, then the + * P256 data will be preferred, and thus used. * * @param transport - Transport to use - * @param oobData - Out Of Band data + * @param remoteP192Data - Out Of Band data (P192) or null + * @param remoteP256Data - Out Of Band data (P256) or null * @return false on immediate error, true if bonding will begin * @hide */ - public boolean createBondOutOfBand(int transport, OobData oobData) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, + @Nullable OobData remoteP256Data) { + if (remoteP192Data == null && remoteP256Data == null) { + throw new IllegalArgumentException( + "One or both arguments for the OOB data types are required to not be null." + + " Please use createBond() instead if you do not have OOB data to pass."); + } + return createBondInternal(transport, remoteP192Data, remoteP256Data); + } + + private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, + @Nullable OobData remoteP256Data) { final IBluetooth service = sService; if (service == null) { Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); return false; } try { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - return service.createBond(this, transport, oobData, adapter.getOpPackageName()); + return service.createBond(this, transport, remoteP192Data, remoteP256Data, + BluetoothAdapter.getDefaultAdapter().getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1379,27 +1396,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** - * Set the Out Of Band data for a remote device to be used later - * in the pairing mechanism. Users can obtain this data through other - * trusted channels - * - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. - * - * @param hash Simple Secure pairing hash - * @param randomizer The random key obtained using OOB - * @return false on error; true otherwise - * @hide - */ - public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) { - //TODO(BT) - /* - try { - return sService.setDeviceOutOfBandData(this, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);} */ - return false; - } - /** * Cancel an in-progress bonding request started with {@link #createBond}. * diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 0d0c6ab2efa..98107461fb4 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,88 +16,950 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +import java.lang.IllegalArgumentException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Out Of Band Data for Bluetooth device pairing. * *

                This object represents optional data obtained from a remote device through - * an out-of-band channel (eg. NFC). + * an out-of-band channel (eg. NFC, QR). + * + *

                References: + * NFC AD Forum SSP 1.1 (AD) + * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf} + * Core Specification Supplement (CSS) V9 + * + *

                There are several BR/EDR Examples + * + *

                Negotiated Handover: + * Bluetooth Carrier Configuration Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Simple Pairing Hash C + * - Simple Pairing Randomizer R + * - Service Class UUID + * - Bluetooth Local Name + * + *

                Static Handover: + * Bluetooth Carrier Configuration Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Service Class UUID + * - Bluetooth Local Name + * + *

                Simplified Tag Format for Single BT Carrier: + * Bluetooth OOB Data Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Service Class UUID + * - Bluetooth Local Name * * @hide */ -public class OobData implements Parcelable { - private byte[] mLeBluetoothDeviceAddress; - private byte[] mSecurityManagerTk; - private byte[] mLeSecureConnectionsConfirmation; - private byte[] mLeSecureConnectionsRandom; +@SystemApi +public final class OobData implements Parcelable { + + private static final String TAG = "OobData"; + /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ + @SystemApi + private static final int OOB_LENGTH_OCTETS = 2; + /** + * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). + * (AD 3.1.2) (CSS 1.6.2) + * @hide + */ + @SystemApi + public static final int DEVICE_ADDRESS_OCTETS = 7; + /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */ + @SystemApi + public static final int CLASS_OF_DEVICE_OCTETS = 3; + /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */ + @SystemApi + public static final int CONFIRMATION_OCTETS = 16; + /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */ + @SystemApi + public static final int RANDOMIZER_OCTETS = 16; + /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_OCTETS = 1; + /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_TK_OCTETS = 16; + /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_APPEARANCE_OCTETS = 2; + /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value. + + // Le Roles + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "LE_DEVICE_ROLE_" }, + value = { + LE_DEVICE_ROLE_PERIPHERAL_ONLY, + LE_DEVICE_ROLE_CENTRAL_ONLY, + LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL, + LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL + } + ) + public @interface LeRole {} + + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03; + + // Le Flags + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "LE_FLAG_" }, + value = { + LE_FLAG_LIMITED_DISCOVERY_MODE, + LE_FLAG_GENERAL_DISCOVERY_MODE, + LE_FLAG_BREDR_NOT_SUPPORTED, + LE_FLAG_SIMULTANEOUS_CONTROLLER, + LE_FLAG_SIMULTANEOUS_HOST + } + ) + public @interface LeFlag {} + + /** @hide */ + @SystemApi + public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00; + /** @hide */ + @SystemApi + public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01; + /** @hide */ + @SystemApi + public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02; + /** @hide */ + @SystemApi + public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03; + /** @hide */ + @SystemApi + public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04; + + /** + * Main creation method for creating a Classic version of {@link OobData}. + * + *

                This object will allow the caller to call {@link ClassicBuilder#build()} + * to build the data object or add any option information to the builder. + * + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * @param classicLength byte array representing the length of data from 8-65535 across 2 + * octets (0xXXXX). + * @param deviceAddressWithType byte array representing the Bluetooth Address of the device + * that owns the OOB data. (i.e. the originator) [6 octets] + * + * @return a Classic Builder instance with all the given data set or null. + * + * @throws IllegalArgumentException if any of the values fail to be set. + * @throws NullPointerException if any argument is null. + * + * @hide + */ + @NonNull + @SystemApi + public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash, + @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { + return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType); + } + + /** + * Main creation method for creating a LE version of {@link OobData}. + * + *

                This object will allow the caller to call {@link LeBuilder#build()} + * to build the data object or add any option information to the builder. + * + * @param deviceAddressWithType the LE device address plus the address type (7 octets); + * not null. + * @param leDeviceRole whether the device supports Peripheral, Central, + * Both including preference; not null. (1 octet) + * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets + * of data. Data is derived from controller/host stack and is + * required for pairing OOB. + * + *

                Possible LE Device Role Values: + * 0x00 Only Peripheral supported + * 0x01 Only Central supported + * 0x02 Central & Peripheral supported; Peripheral Preferred + * 0x03 Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @return a LeBuilder instance with all the given data set or null. + * + * @throws IllegalArgumentException if any of the values fail to be set. + * @throws NullPointerException if any argument is null. + * + * @hide + */ + @NonNull + @SystemApi + public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash, + @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { + return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole); + } + + /** + * Builds an {@link OobData} object and validates that the required combination + * of values are present to create the LE specific OobData type. + * + * @hide + */ + @SystemApi + public static final class LeBuilder { + + /** + * It is recommended that this Hash C is generated anew for each + * pairing. + * + *

                It should be noted that on passive NFC this isn't possible as the data is static + * and immutable. + */ + private byte[] mConfirmationHash = null; + + /** + * Optional, but adds more validity to the pairing. + * + *

                If not present a value of 0 is assumed. + */ + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + + /** + * The Bluetooth Device user-friendly name presented over Bluetooth Technology. + * + *

                This is the name that may be displayed to the device user as part of the UI. + */ + private byte[] mDeviceName = null; + + /** + * Sets the Bluetooth Device name to be used for UI purposes. + * + *

                Optional attribute. + * + * @param deviceName byte array representing the name, may be 0 in length, not null. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws NullPointerException if deviceName is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setDeviceName(@NonNull byte[] deviceName) { + Preconditions.checkNotNull(deviceName); + this.mDeviceName = deviceName; + return this; + } + + /** + * The Bluetooth Device Address is the address to which the OOB data belongs. + * + *

                The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. + * + *

                Address is encoded in Little Endian order. + * + *

                e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 + */ + private final byte[] mDeviceAddressWithType; + + /** + * During an LE connection establishment, one must be in the Peripheral mode and the other + * in the Central role. + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + */ + private final @LeRole int mLeDeviceRole; + + /** + * Temporary key value from the Security Manager. + * + *

                Must be {@link LE_TK_OCTETS} in size + */ + private byte[] mLeTemporaryKey = null; + + /** + * Defines the representation of the external appearance of the device. + * + *

                For example, a mouse, remote control, or keyboard. + * + *

                Used for visual on discovering device to represent icon/string/etc... + */ + private byte[] mLeAppearance = null; + + /** + * Contains which discoverable mode to use, BR/EDR support and capability. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller). + * Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + */ + private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default + + /** + * Constructing an OobData object for use with LE requires + * a LE Device Address and LE Device Role as well as the Confirmation + * and optionally, the Randomizer, however it is recommended to use. + * + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} + * octets of data. Data is derived from controller/host stack and is required for + * pairing OOB. + * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte + * address type. + * @param leDeviceRole indicating device's role and preferences (Central or Peripheral) + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @throws IllegalArgumentException if deviceAddressWithType is not + * {@link LE_DEVICE_ADDRESS_OCTETS} octets + * @throws NullPointerException if any argument is null. + */ + private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, + @LeRole int leDeviceRole) { + Preconditions.checkNotNull(confirmationHash); + Preconditions.checkNotNull(deviceAddressWithType); + if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.CONFIRMATION_OCTETS + " octets in length."); + } + this.mConfirmationHash = confirmationHash; + if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length."); + } + this.mDeviceAddressWithType = deviceAddressWithType; + if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY + || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) { + throw new IllegalArgumentException("leDeviceRole must be a valid value."); + } + this.mLeDeviceRole = leDeviceRole; + } + + /** + * Sets the Temporary Key value to be used by the LE Security Manager during + * LE pairing. + * + * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6, + * Part A 1.8 for a detailed description. + * + * @return {@link OobData#Builder} + * + * @throws IllegalArgumentException if the leTemporaryKey is an invalid format. + * @throws NullinterException if leTemporaryKey is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) { + Preconditions.checkNotNull(leTemporaryKey); + if (leTemporaryKey.length != LE_TK_OCTETS) { + throw new IllegalArgumentException("leTemporaryKey must be " + + LE_TK_OCTETS + " octets in length."); + } + this.mLeTemporaryKey = leTemporaryKey; + return this; + } + + /** + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. + * + * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. + * @throws NullPointerException if randomizerHash is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { + Preconditions.checkNotNull(randomizerHash); + if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { + throw new IllegalArgumentException("randomizerHash must be " + + OobData.RANDOMIZER_OCTETS + " octets in length."); + } + this.mRandomizerHash = randomizerHash; + return this; + } + + /** + * Sets the LE Flags necessary for the pairing scenario or discovery mode. + * + * @param leFlags enum value representing the 1 octet of data about discovery modes. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + * + * @throws IllegalArgumentException for invalid flag + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setLeFlags(@LeFlag int leFlags) { + if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) { + throw new IllegalArgumentException("leFlags must be a valid value."); + } + this.mLeFlags = leFlags; + return this; + } + + /** + * Validates and builds the {@link OobData} object for LE Security. + * + * @return {@link OobData} with given builder values + * + * @throws IllegalStateException if either of the 2 required fields were not set. + * + * @hide + */ + @NonNull + @SystemApi + public OobData build() { + final OobData oob = + new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole, + this.mConfirmationHash); + + // If we have values, set them, otherwise use default + oob.mLeTemporaryKey = + (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey; + oob.mLeAppearance = (this.mLeAppearance != null) + ? this.mLeAppearance : oob.mLeAppearance; + oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags; + oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; + oob.mRandomizerHash = this.mRandomizerHash; + return oob; + } + } + + /** + * Builds an {@link OobData} object and validates that the required combination + * of values are present to create the Classic specific OobData type. + * + * @hide + */ + @SystemApi + public static final class ClassicBuilder { + // Used by both Classic and LE + /** + * It is recommended that this Hash C is generated anew for each + * pairing. + * + *

                It should be noted that on passive NFC this isn't possible as the data is static + * and immutable. + * + * @hide + */ + private byte[] mConfirmationHash = null; + + /** + * Optional, but adds more validity to the pairing. + * + *

                If not present a value of 0 is assumed. + * + * @hide + */ + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + + /** + * The Bluetooth Device user-friendly name presented over Bluetooth Technology. + * + *

                This is the name that may be displayed to the device user as part of the UI. + * + * @hide + */ + private byte[] mDeviceName = null; + + /** + * This length value provides the absolute length of total OOB data block used for + * Bluetooth BR/EDR + * + *

                OOB communication, which includes the length field itself and the Bluetooth + * Device Address. + * + *

                The minimum length that may be represented in this field is 8. + * + * @hide + */ + private final byte[] mClassicLength; + + /** + * The Bluetooth Device Address is the address to which the OOB data belongs. + * + *

                The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. + * + *

                Address is encoded in Little Endian order. + * + *

                e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 + * + * @hide + */ + private final byte[] mDeviceAddressWithType; + + /** + * Class of Device information is to be used to provide a graphical representation + * to the user as part of UI involving operations. + * + *

                This is not to be used to determine a particular service can be used. + * + *

                The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * + * @hide + */ + private byte[] mClassOfDevice = null; + + /** + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} + * octets of data. Data is derived from controller/host stack and is required for pairing + * OOB. + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required + * for pairing OOB. Also, randomizerHash may be all 0s or null in which case + * it becomes all 0s. + * @param classicLength byte array representing the length of data from 8-65535 across 2 + * octets (0xXXXX). Inclusive of this value in the length. + * @param deviceAddressWithType byte array representing the Bluetooth Address of the device + * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type + * as the last octet. + * + * @throws IllegalArgumentException if any value is not the correct length + * @throws NullPointerException if anything passed is null + * + * @hide + */ + @SystemApi + private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, + @NonNull byte[] deviceAddressWithType) { + Preconditions.checkNotNull(confirmationHash); + Preconditions.checkNotNull(classicLength); + Preconditions.checkNotNull(deviceAddressWithType); + if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.CONFIRMATION_OCTETS + " octets in length."); + } + this.mConfirmationHash = confirmationHash; + if (classicLength.length != OOB_LENGTH_OCTETS) { + throw new IllegalArgumentException("classicLength must be " + + OOB_LENGTH_OCTETS + " octets in length."); + } + this.mClassicLength = classicLength; + if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) { + throw new IllegalArgumentException("deviceAddressWithType must be " + + DEVICE_ADDRESS_OCTETS + " octets in length."); + } + this.mDeviceAddressWithType = deviceAddressWithType; + } - public byte[] getLeBluetoothDeviceAddress() { - return mLeBluetoothDeviceAddress; + /** + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. + * + * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. + * @throws NullPointerException if randomizerHash is null. + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { + Preconditions.checkNotNull(randomizerHash); + if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { + throw new IllegalArgumentException("randomizerHash must be " + + OobData.RANDOMIZER_OCTETS + " octets in length."); + } + this.mRandomizerHash = randomizerHash; + return this; + } + + /** + * Sets the Bluetooth Device name to be used for UI purposes. + * + *

                Optional attribute. + * + * @param deviceName byte array representing the name, may be 0 in length, not null. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws NullPointerException if deviceName is null + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) { + Preconditions.checkNotNull(deviceName); + this.mDeviceName = deviceName; + return this; + } + + /** + * Sets the Bluetooth Class of Device; used for UI purposes only. + * + *

                Not an indicator of available services! + * + *

                Optional attribute. + * + * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws IllegalArgumentException if length is not equal to + * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * @throws NullPointerException if classOfDevice is null. + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) { + Preconditions.checkNotNull(classOfDevice); + if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) { + throw new IllegalArgumentException("classOfDevice must be " + + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length."); + } + this.mClassOfDevice = classOfDevice; + return this; + } + + /** + * Validates and builds the {@link OobDat object for Classic Security. + * + * @return {@link OobData} with previously given builder values. + * + * @hide + */ + @NonNull + @SystemApi + public OobData build() { + final OobData oob = + new OobData(this.mClassicLength, this.mDeviceAddressWithType, + this.mConfirmationHash); + // If we have values, set them, otherwise use default + oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; + oob.mClassOfDevice = (this.mClassOfDevice != null) + ? this.mClassOfDevice : oob.mClassOfDevice; + oob.mRandomizerHash = this.mRandomizerHash; + return oob; + } + } + + // Members (Defaults for Optionals must be set or Parceling fails on NPE) + // Both + private final byte[] mDeviceAddressWithType; + private final byte[] mConfirmationHash; + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + // Default the name to "Bluetooth Device" + private byte[] mDeviceName = new byte[] { + // Bluetooth + 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, + // Device + 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65 + }; + + // Classic + private final byte[] mClassicLength; + private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS]; + + // LE + private final @LeRole int mLeDeviceRole; + private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS]; + private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS]; + private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE; + + /** + * @return byte array representing the MAC address of a bluetooth device. + * The Address is 6 octets long with a 1 octet address type associated with the address. + * + *

                For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type. + * For LE there are more choices for Address Type. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getDeviceAddressWithType() { + return mDeviceAddressWithType; + } + + /** + * @return byte array representing the confirmationHash value + * which is used to confirm the identity to the controller. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getConfirmationHash() { + return mConfirmationHash; } /** - * Sets the LE Bluetooth Device Address value to be used during LE pairing. - * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for - * a detailed description. + * @return byte array representing the randomizerHash value + * which is used to verify the identity of the controller. + * + * @hide */ - public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { - mLeBluetoothDeviceAddress = leBluetoothDeviceAddress; + @NonNull + @SystemApi + public byte[] getRandomizerHash() { + return mRandomizerHash; } - public byte[] getSecurityManagerTk() { - return mSecurityManagerTk; + /** + * @return Device Name used for displaying name in UI. + * + *

                Also, this will be populated with the LE Local Name if the data is for LE. + * + * @hide + */ + @Nullable + @SystemApi + public byte[] getDeviceName() { + return mDeviceName; + } + + /** + * @return byte array representing the oob data length which is the length + * of all of the data including these octets. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getClassicLength() { + return mClassicLength; + } + + /** + * @return byte array representing the class of device for UI display. + * + *

                Does not indicate services available; for display only. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getClassOfDevice() { + return mClassOfDevice; } /** - * Sets the Temporary Key value to be used by the LE Security Manager during - * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6, - * Part A 1.8 for a detailed description. + * @return Temporary Key used for LE pairing. + * + * @hide */ - public void setSecurityManagerTk(byte[] securityManagerTk) { - mSecurityManagerTk = securityManagerTk; + @Nullable + @SystemApi + public byte[] getLeTemporaryKey() { + return mLeTemporaryKey; } - public byte[] getLeSecureConnectionsConfirmation() { - return mLeSecureConnectionsConfirmation; + /** + * @return Appearance used for LE pairing. For use in UI situations + * when determining what sort of icons or text to display regarding + * the device. + * + * @hide + */ + @Nullable + @SystemApi + public byte[] getLeAppearance() { + return mLeTemporaryKey; } - public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) { - mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation; + /** + * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller). + * Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + * + * @hide + */ + @NonNull + @SystemApi + @LeFlag + public int getLeFlags() { + return mLeFlags; } - public byte[] getLeSecureConnectionsRandom() { - return mLeSecureConnectionsRandom; + /** + * @return the supported and preferred roles of the LE device. + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @hide + */ + @NonNull + @SystemApi + @LeRole + public int getLeDeviceRole() { + return mLeDeviceRole; } - public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) { - mLeSecureConnectionsRandom = leSecureConnectionsRandom; + /** + * Classic Security Constructor + */ + private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType, + @NonNull byte[] confirmationHash) { + mClassicLength = classicLength; + mDeviceAddressWithType = deviceAddressWithType; + mConfirmationHash = confirmationHash; + mLeDeviceRole = -1; // Satisfy final } - public OobData() { + /** + * LE Security Constructor + */ + private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole, + @NonNull byte[] confirmationHash) { + mDeviceAddressWithType = deviceAddressWithType; + mLeDeviceRole = leDeviceRole; + mConfirmationHash = confirmationHash; + mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final } private OobData(Parcel in) { - mLeBluetoothDeviceAddress = in.createByteArray(); - mSecurityManagerTk = in.createByteArray(); - mLeSecureConnectionsConfirmation = in.createByteArray(); - mLeSecureConnectionsRandom = in.createByteArray(); + // Both + mDeviceAddressWithType = in.createByteArray(); + mConfirmationHash = in.createByteArray(); + mRandomizerHash = in.createByteArray(); + mDeviceName = in.createByteArray(); + + // Classic + mClassicLength = in.createByteArray(); + mClassOfDevice = in.createByteArray(); + + // LE + mLeDeviceRole = in.readInt(); + mLeTemporaryKey = in.createByteArray(); + mLeAppearance = in.createByteArray(); + mLeFlags = in.readInt(); } + /** + * @hide + */ @Override public int describeContents() { return 0; } + /** + * @hide + */ @Override - public void writeToParcel(Parcel out, int flags) { - out.writeByteArray(mLeBluetoothDeviceAddress); - out.writeByteArray(mSecurityManagerTk); - out.writeByteArray(mLeSecureConnectionsConfirmation); - out.writeByteArray(mLeSecureConnectionsRandom); + public void writeToParcel(@NonNull Parcel out, int flags) { + // Both + // Required + out.writeByteArray(mDeviceAddressWithType); + // Required + out.writeByteArray(mConfirmationHash); + // Optional + out.writeByteArray(mRandomizerHash); + // Optional + out.writeByteArray(mDeviceName); + + // Classic + // Required + out.writeByteArray(mClassicLength); + // Optional + out.writeByteArray(mClassOfDevice); + + // LE + // Required + out.writeInt(mLeDeviceRole); + // Required + out.writeByteArray(mLeTemporaryKey); + // Optional + out.writeByteArray(mLeAppearance); + // Optional + out.writeInt(mLeFlags); } + // For Parcelable public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public OobData createFromParcel(Parcel in) { @@ -108,4 +970,47 @@ public class OobData implements Parcelable { return new OobData[size]; } }; + + /** + * @return a {@link String} representation of the OobData object. + * + * @hide + */ + @Override + @NonNull + public String toString() { + return "OobData: \n\t" + // Both + + "Device Address With Type: " + toHexString(mDeviceAddressWithType) + "\n\t" + + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t" + + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t" + + "Device Name: " + toHexString(mDeviceName) + "\n\t" + // Classic + + "OobData Length: " + toHexString(mClassicLength) + "\n\t" + + "Class of Device: " + toHexString(mClassOfDevice) + "\n\t" + // LE + + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t" + + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t" + + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t" + + "LE Flags: " + toHexString(mLeFlags) + "\n\t"; + } + + @NonNull + private String toHexString(@NonNull int b) { + return toHexString(new byte[] {(byte) b}); + } + + @NonNull + private String toHexString(@NonNull byte b) { + return toHexString(new byte[] {b}); + } + + @NonNull + private String toHexString(@NonNull byte[] array) { + StringBuilder builder = new StringBuilder(array.length * 2); + for (byte b: array) { + builder.append(String.format("%02x", b)); + } + return builder.toString(); + } } -- GitLab From 464a18849a09b9579b34862be34779069aa6df30 Mon Sep 17 00:00:00 2001 From: Chienyuan Date: Fri, 12 Mar 2021 20:46:17 +0800 Subject: [PATCH 1241/1408] Le Scan: Add ambient discovery mode (1/2) Bug: 177466875 Test: manual Change-Id: I4a5c8a0768903ef0838dcf55cf5cfba9a0a18eef --- .../android/bluetooth/le/ScanSettings.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 504118ec5da..368d1eecade 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -51,6 +51,16 @@ public final class ScanSettings implements Parcelable { */ public static final int SCAN_MODE_LOW_LATENCY = 2; + /** + * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more + * aggressive scan interval than balanced mode that provides a good trade-off between scan + * latency and power consumption. + * + * @hide + */ + @SystemApi + public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3; + /** * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. * If no filter is active, all advertisement packets are reported. @@ -276,10 +286,17 @@ public final class ScanSettings implements Parcelable { * @throws IllegalArgumentException If the {@code scanMode} is invalid. */ public Builder setScanMode(int scanMode) { - if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("invalid scan mode " + scanMode); + switch (scanMode) { + case SCAN_MODE_OPPORTUNISTIC: + case SCAN_MODE_LOW_POWER: + case SCAN_MODE_BALANCED: + case SCAN_MODE_LOW_LATENCY: + case SCAN_MODE_AMBIENT_DISCOVERY: + mScanMode = scanMode; + break; + default: + throw new IllegalArgumentException("invalid scan mode " + scanMode); } - mScanMode = scanMode; return this; } -- GitLab From 8c1bad91e18d3c18e22e3297d27bffde2f69ae99 Mon Sep 17 00:00:00 2001 From: Jack He Date: Tue, 16 Mar 2021 16:05:42 -0700 Subject: [PATCH 1242/1408] Bluetooth: Wait for provisioning after init flag change * Wait for device provisioning to be completed before restart stack after an init flag change Bug: 181226310 Test: make, setup wizard Change-Id: I1128d9ef25b7703da31cfe55a812aaa745ab5f41 --- .../BluetoothDeviceConfigListener.java | 16 ++++++- .../bluetooth/BluetoothManagerService.java | 46 ++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java b/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java index 2dcf82ff941..611a37de70f 100644 --- a/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothDeviceConfigListener.java @@ -17,6 +17,9 @@ package com.android.server; import android.provider.DeviceConfig; +import android.util.Slog; + +import java.util.ArrayList; /** * The BluetoothDeviceConfigListener handles system device config change callback and checks @@ -30,10 +33,12 @@ import android.provider.DeviceConfig; class BluetoothDeviceConfigListener { private static final String TAG = "BluetoothDeviceConfigListener"; - BluetoothManagerService mService; + private final BluetoothManagerService mService; + private final boolean mLogDebug; - BluetoothDeviceConfigListener(BluetoothManagerService service) { + BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) { mService = service; + mLogDebug = logDebug; DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_BLUETOOTH, (Runnable r) -> r.run(), @@ -47,6 +52,13 @@ class BluetoothDeviceConfigListener { if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { return; } + if (mLogDebug) { + ArrayList flags = new ArrayList<>(); + for (String name : properties.getKeyset()) { + flags.add(name + "='" + properties.getString(name, "") + "'"); + } + Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags)); + } boolean foundInit = false; for (String name : properties.getKeyset()) { if (name.startsWith("INIT_")) { diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index aab05532e2b..992ef2657a6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -453,6 +453,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) && state == BluetoothProfile.STATE_DISCONNECTED && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + Slog.i(TAG, "Device disconnected, reactivating pending flag changes"); onInitFlagsChanged(); } } @@ -810,6 +811,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return enabledProfiles; } + private boolean isDeviceProvisioned() { + return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, + 0) != 0; + } + + // Monitor change of BLE scan only mode settings. + private void registerForProvisioningStateChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + if (!isDeviceProvisioned()) { + if (DBG) { + Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not " + + "provisioned"); + } + return; + } + if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) { + Slog.i(TAG, "Device provisioned, reactivating pending flag changes"); + onInitFlagsChanged(); + } + } + }; + + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false, + contentObserver); + } + // Monitor change of BLE scan only mode settings. private void registerForBleScanModeChange() { ContentObserver contentObserver = new ContentObserver(null) { @@ -1375,7 +1405,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetoothAirplaneModeListener != null) { mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper); } - mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this); + registerForProvisioningStateChange(); + mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG); } /** @@ -2219,12 +2250,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " + + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS + + " ms due to existing connections"); + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); + break; + } + if (!isDeviceProvisioned()) { + Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " + + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS + + "ms because device is not provisioned"); mHandler.sendEmptyMessageDelayed( MESSAGE_INIT_FLAGS_CHANGED, DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); break; } if (mBluetooth != null && isEnabled()) { + Slog.i(TAG, "Restarting Bluetooth due to init flag change"); restartForReason( BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); } -- GitLab From 089e35e81711096b04f7ca56d484ef1536a54012 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Tue, 16 Mar 2021 16:22:55 -0700 Subject: [PATCH 1243/1408] Fix CTS Failure Bug: 182849735 Test: atest CtsSystemApiAnnotationTestCases:android.signature.cts.api.AnnotationTest#testAnnotation -- --abi x86_64 Change-Id: I1b9e77b05cd23299bf179c854e136fa341c81566 --- framework/java/android/bluetooth/OobData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 98107461fb4..08d694eb93e 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -76,7 +76,7 @@ public final class OobData implements Parcelable { private static final String TAG = "OobData"; /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ @SystemApi - private static final int OOB_LENGTH_OCTETS = 2; + public static final int OOB_LENGTH_OCTETS = 2; /** * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). * (AD 3.1.2) (CSS 1.6.2) @@ -590,7 +590,6 @@ public final class OobData implements Parcelable { * * @hide */ - @SystemApi private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { Preconditions.checkNotNull(confirmationHash); -- GitLab From 51a3e5da8872ad550ae66d2a127a18eca2d9d990 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Thu, 21 Jan 2021 10:29:05 -0800 Subject: [PATCH 1244/1408] Bluetooth: Modify and append to the Out-of-Band API - Modify createOutOfBand to be a SystemApi, and accept p192 and p256 data objects. - Modify OobData to become a SystemApi and to provide a Builder pattern for creation. CTS-Coverage-Bug: 182420103 Bug: 178007935 Test: compiles and runs Tag: #feature Change-Id: I46aec8c2cb64a8da8957d01d32b879d60df7a31c Merged-In: I46aec8c2cb64a8da8957d01d32b879d60df7a31c --- .../android/bluetooth/BluetoothDevice.java | 50 +- framework/java/android/bluetooth/OobData.java | 983 +++++++++++++++++- 2 files changed, 967 insertions(+), 66 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 89030bcf12e..41bc77651db 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1279,7 +1279,6 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. *

                Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @param transport The transport to use for the pairing procedure. * @return false on immediate error, true if bonding will begin @@ -1287,8 +1286,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond(int transport) { - return createBondOutOfBand(transport, null); + return createBondInternal(transport, null, null); } /** @@ -1302,21 +1302,38 @@ public final class BluetoothDevice implements Parcelable { *

                Android system services will handle the necessary user interactions * to confirm and complete the bonding process. * - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

                There are two possible versions of OOB Data. This data can come in as + * P192 or P256. This is a reference to the cryptography used to generate the key. + * The caller may pass one or both. If both types of data are passed, then the + * P256 data will be preferred, and thus used. * * @param transport - Transport to use - * @param oobData - Out Of Band data + * @param remoteP192Data - Out Of Band data (P192) or null + * @param remoteP256Data - Out Of Band data (P256) or null * @return false on immediate error, true if bonding will begin * @hide */ - public boolean createBondOutOfBand(int transport, OobData oobData) { + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, + @Nullable OobData remoteP256Data) { + if (remoteP192Data == null && remoteP256Data == null) { + throw new IllegalArgumentException( + "One or both arguments for the OOB data types are required to not be null." + + " Please use createBond() instead if you do not have OOB data to pass."); + } + return createBondInternal(transport, remoteP192Data, remoteP256Data); + } + + private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, + @Nullable OobData remoteP256Data) { final IBluetooth service = sService; if (service == null) { Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); return false; } try { - return service.createBond(this, transport, oobData); + return service.createBond(this, transport, remoteP192Data, remoteP256Data); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1346,27 +1363,6 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** - * Set the Out Of Band data for a remote device to be used later - * in the pairing mechanism. Users can obtain this data through other - * trusted channels - * - *

                Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. - * - * @param hash Simple Secure pairing hash - * @param randomizer The random key obtained using OOB - * @return false on error; true otherwise - * @hide - */ - public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) { - //TODO(BT) - /* - try { - return sService.setDeviceOutOfBandData(this, hash, randomizer); - } catch (RemoteException e) {Log.e(TAG, "", e);} */ - return false; - } - /** * Cancel an in-progress bonding request started with {@link #createBond}. * diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 0d0c6ab2efa..98107461fb4 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,88 +16,950 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +import java.lang.IllegalArgumentException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Out Of Band Data for Bluetooth device pairing. * *

                This object represents optional data obtained from a remote device through - * an out-of-band channel (eg. NFC). + * an out-of-band channel (eg. NFC, QR). + * + *

                References: + * NFC AD Forum SSP 1.1 (AD) + * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf} + * Core Specification Supplement (CSS) V9 + * + *

                There are several BR/EDR Examples + * + *

                Negotiated Handover: + * Bluetooth Carrier Configuration Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Simple Pairing Hash C + * - Simple Pairing Randomizer R + * - Service Class UUID + * - Bluetooth Local Name + * + *

                Static Handover: + * Bluetooth Carrier Configuration Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Service Class UUID + * - Bluetooth Local Name + * + *

                Simplified Tag Format for Single BT Carrier: + * Bluetooth OOB Data Record: + * - OOB Data Length + * - Device Address + * - Class of Device + * - Service Class UUID + * - Bluetooth Local Name * * @hide */ -public class OobData implements Parcelable { - private byte[] mLeBluetoothDeviceAddress; - private byte[] mSecurityManagerTk; - private byte[] mLeSecureConnectionsConfirmation; - private byte[] mLeSecureConnectionsRandom; +@SystemApi +public final class OobData implements Parcelable { + + private static final String TAG = "OobData"; + /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ + @SystemApi + private static final int OOB_LENGTH_OCTETS = 2; + /** + * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). + * (AD 3.1.2) (CSS 1.6.2) + * @hide + */ + @SystemApi + public static final int DEVICE_ADDRESS_OCTETS = 7; + /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */ + @SystemApi + public static final int CLASS_OF_DEVICE_OCTETS = 3; + /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */ + @SystemApi + public static final int CONFIRMATION_OCTETS = 16; + /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */ + @SystemApi + public static final int RANDOMIZER_OCTETS = 16; + /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_OCTETS = 1; + /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_TK_OCTETS = 16; + /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_APPEARANCE_OCTETS = 2; + /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */ + @SystemApi + public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value. + + // Le Roles + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "LE_DEVICE_ROLE_" }, + value = { + LE_DEVICE_ROLE_PERIPHERAL_ONLY, + LE_DEVICE_ROLE_CENTRAL_ONLY, + LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL, + LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL + } + ) + public @interface LeRole {} + + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02; + /** @hide */ + @SystemApi + public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03; + + // Le Flags + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "LE_FLAG_" }, + value = { + LE_FLAG_LIMITED_DISCOVERY_MODE, + LE_FLAG_GENERAL_DISCOVERY_MODE, + LE_FLAG_BREDR_NOT_SUPPORTED, + LE_FLAG_SIMULTANEOUS_CONTROLLER, + LE_FLAG_SIMULTANEOUS_HOST + } + ) + public @interface LeFlag {} + + /** @hide */ + @SystemApi + public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00; + /** @hide */ + @SystemApi + public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01; + /** @hide */ + @SystemApi + public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02; + /** @hide */ + @SystemApi + public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03; + /** @hide */ + @SystemApi + public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04; + + /** + * Main creation method for creating a Classic version of {@link OobData}. + * + *

                This object will allow the caller to call {@link ClassicBuilder#build()} + * to build the data object or add any option information to the builder. + * + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * @param classicLength byte array representing the length of data from 8-65535 across 2 + * octets (0xXXXX). + * @param deviceAddressWithType byte array representing the Bluetooth Address of the device + * that owns the OOB data. (i.e. the originator) [6 octets] + * + * @return a Classic Builder instance with all the given data set or null. + * + * @throws IllegalArgumentException if any of the values fail to be set. + * @throws NullPointerException if any argument is null. + * + * @hide + */ + @NonNull + @SystemApi + public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash, + @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { + return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType); + } + + /** + * Main creation method for creating a LE version of {@link OobData}. + * + *

                This object will allow the caller to call {@link LeBuilder#build()} + * to build the data object or add any option information to the builder. + * + * @param deviceAddressWithType the LE device address plus the address type (7 octets); + * not null. + * @param leDeviceRole whether the device supports Peripheral, Central, + * Both including preference; not null. (1 octet) + * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets + * of data. Data is derived from controller/host stack and is + * required for pairing OOB. + * + *

                Possible LE Device Role Values: + * 0x00 Only Peripheral supported + * 0x01 Only Central supported + * 0x02 Central & Peripheral supported; Peripheral Preferred + * 0x03 Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @return a LeBuilder instance with all the given data set or null. + * + * @throws IllegalArgumentException if any of the values fail to be set. + * @throws NullPointerException if any argument is null. + * + * @hide + */ + @NonNull + @SystemApi + public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash, + @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { + return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole); + } + + /** + * Builds an {@link OobData} object and validates that the required combination + * of values are present to create the LE specific OobData type. + * + * @hide + */ + @SystemApi + public static final class LeBuilder { + + /** + * It is recommended that this Hash C is generated anew for each + * pairing. + * + *

                It should be noted that on passive NFC this isn't possible as the data is static + * and immutable. + */ + private byte[] mConfirmationHash = null; + + /** + * Optional, but adds more validity to the pairing. + * + *

                If not present a value of 0 is assumed. + */ + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + + /** + * The Bluetooth Device user-friendly name presented over Bluetooth Technology. + * + *

                This is the name that may be displayed to the device user as part of the UI. + */ + private byte[] mDeviceName = null; + + /** + * Sets the Bluetooth Device name to be used for UI purposes. + * + *

                Optional attribute. + * + * @param deviceName byte array representing the name, may be 0 in length, not null. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws NullPointerException if deviceName is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setDeviceName(@NonNull byte[] deviceName) { + Preconditions.checkNotNull(deviceName); + this.mDeviceName = deviceName; + return this; + } + + /** + * The Bluetooth Device Address is the address to which the OOB data belongs. + * + *

                The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. + * + *

                Address is encoded in Little Endian order. + * + *

                e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 + */ + private final byte[] mDeviceAddressWithType; + + /** + * During an LE connection establishment, one must be in the Peripheral mode and the other + * in the Central role. + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + */ + private final @LeRole int mLeDeviceRole; + + /** + * Temporary key value from the Security Manager. + * + *

                Must be {@link LE_TK_OCTETS} in size + */ + private byte[] mLeTemporaryKey = null; + + /** + * Defines the representation of the external appearance of the device. + * + *

                For example, a mouse, remote control, or keyboard. + * + *

                Used for visual on discovering device to represent icon/string/etc... + */ + private byte[] mLeAppearance = null; + + /** + * Contains which discoverable mode to use, BR/EDR support and capability. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller). + * Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + */ + private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default + + /** + * Constructing an OobData object for use with LE requires + * a LE Device Address and LE Device Role as well as the Confirmation + * and optionally, the Randomizer, however it is recommended to use. + * + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} + * octets of data. Data is derived from controller/host stack and is required for + * pairing OOB. + * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte + * address type. + * @param leDeviceRole indicating device's role and preferences (Central or Peripheral) + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @throws IllegalArgumentException if deviceAddressWithType is not + * {@link LE_DEVICE_ADDRESS_OCTETS} octets + * @throws NullPointerException if any argument is null. + */ + private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, + @LeRole int leDeviceRole) { + Preconditions.checkNotNull(confirmationHash); + Preconditions.checkNotNull(deviceAddressWithType); + if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.CONFIRMATION_OCTETS + " octets in length."); + } + this.mConfirmationHash = confirmationHash; + if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length."); + } + this.mDeviceAddressWithType = deviceAddressWithType; + if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY + || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) { + throw new IllegalArgumentException("leDeviceRole must be a valid value."); + } + this.mLeDeviceRole = leDeviceRole; + } + + /** + * Sets the Temporary Key value to be used by the LE Security Manager during + * LE pairing. + * + * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6, + * Part A 1.8 for a detailed description. + * + * @return {@link OobData#Builder} + * + * @throws IllegalArgumentException if the leTemporaryKey is an invalid format. + * @throws NullinterException if leTemporaryKey is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) { + Preconditions.checkNotNull(leTemporaryKey); + if (leTemporaryKey.length != LE_TK_OCTETS) { + throw new IllegalArgumentException("leTemporaryKey must be " + + LE_TK_OCTETS + " octets in length."); + } + this.mLeTemporaryKey = leTemporaryKey; + return this; + } + + /** + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. + * + * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. + * @throws NullPointerException if randomizerHash is null. + * + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { + Preconditions.checkNotNull(randomizerHash); + if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { + throw new IllegalArgumentException("randomizerHash must be " + + OobData.RANDOMIZER_OCTETS + " octets in length."); + } + this.mRandomizerHash = randomizerHash; + return this; + } + + /** + * Sets the LE Flags necessary for the pairing scenario or discovery mode. + * + * @param leFlags enum value representing the 1 octet of data about discovery modes. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + * + * @throws IllegalArgumentException for invalid flag + * @hide + */ + @NonNull + @SystemApi + public LeBuilder setLeFlags(@LeFlag int leFlags) { + if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) { + throw new IllegalArgumentException("leFlags must be a valid value."); + } + this.mLeFlags = leFlags; + return this; + } + + /** + * Validates and builds the {@link OobData} object for LE Security. + * + * @return {@link OobData} with given builder values + * + * @throws IllegalStateException if either of the 2 required fields were not set. + * + * @hide + */ + @NonNull + @SystemApi + public OobData build() { + final OobData oob = + new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole, + this.mConfirmationHash); + + // If we have values, set them, otherwise use default + oob.mLeTemporaryKey = + (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey; + oob.mLeAppearance = (this.mLeAppearance != null) + ? this.mLeAppearance : oob.mLeAppearance; + oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags; + oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; + oob.mRandomizerHash = this.mRandomizerHash; + return oob; + } + } + + /** + * Builds an {@link OobData} object and validates that the required combination + * of values are present to create the Classic specific OobData type. + * + * @hide + */ + @SystemApi + public static final class ClassicBuilder { + // Used by both Classic and LE + /** + * It is recommended that this Hash C is generated anew for each + * pairing. + * + *

                It should be noted that on passive NFC this isn't possible as the data is static + * and immutable. + * + * @hide + */ + private byte[] mConfirmationHash = null; + + /** + * Optional, but adds more validity to the pairing. + * + *

                If not present a value of 0 is assumed. + * + * @hide + */ + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + + /** + * The Bluetooth Device user-friendly name presented over Bluetooth Technology. + * + *

                This is the name that may be displayed to the device user as part of the UI. + * + * @hide + */ + private byte[] mDeviceName = null; + + /** + * This length value provides the absolute length of total OOB data block used for + * Bluetooth BR/EDR + * + *

                OOB communication, which includes the length field itself and the Bluetooth + * Device Address. + * + *

                The minimum length that may be represented in this field is 8. + * + * @hide + */ + private final byte[] mClassicLength; + + /** + * The Bluetooth Device Address is the address to which the OOB data belongs. + * + *

                The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. + * + *

                Address is encoded in Little Endian order. + * + *

                e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 + * + * @hide + */ + private final byte[] mDeviceAddressWithType; + + /** + * Class of Device information is to be used to provide a graphical representation + * to the user as part of UI involving operations. + * + *

                This is not to be used to determine a particular service can be used. + * + *

                The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * + * @hide + */ + private byte[] mClassOfDevice = null; + + /** + * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} + * octets of data. Data is derived from controller/host stack and is required for pairing + * OOB. + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required + * for pairing OOB. Also, randomizerHash may be all 0s or null in which case + * it becomes all 0s. + * @param classicLength byte array representing the length of data from 8-65535 across 2 + * octets (0xXXXX). Inclusive of this value in the length. + * @param deviceAddressWithType byte array representing the Bluetooth Address of the device + * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type + * as the last octet. + * + * @throws IllegalArgumentException if any value is not the correct length + * @throws NullPointerException if anything passed is null + * + * @hide + */ + @SystemApi + private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, + @NonNull byte[] deviceAddressWithType) { + Preconditions.checkNotNull(confirmationHash); + Preconditions.checkNotNull(classicLength); + Preconditions.checkNotNull(deviceAddressWithType); + if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { + throw new IllegalArgumentException("confirmationHash must be " + + OobData.CONFIRMATION_OCTETS + " octets in length."); + } + this.mConfirmationHash = confirmationHash; + if (classicLength.length != OOB_LENGTH_OCTETS) { + throw new IllegalArgumentException("classicLength must be " + + OOB_LENGTH_OCTETS + " octets in length."); + } + this.mClassicLength = classicLength; + if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) { + throw new IllegalArgumentException("deviceAddressWithType must be " + + DEVICE_ADDRESS_OCTETS + " octets in length."); + } + this.mDeviceAddressWithType = deviceAddressWithType; + } - public byte[] getLeBluetoothDeviceAddress() { - return mLeBluetoothDeviceAddress; + /** + * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets + * of data. Data is derived from controller/host stack and is required for pairing OOB. + * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. + * + * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. + * @throws NullPointerException if randomizerHash is null. + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { + Preconditions.checkNotNull(randomizerHash); + if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { + throw new IllegalArgumentException("randomizerHash must be " + + OobData.RANDOMIZER_OCTETS + " octets in length."); + } + this.mRandomizerHash = randomizerHash; + return this; + } + + /** + * Sets the Bluetooth Device name to be used for UI purposes. + * + *

                Optional attribute. + * + * @param deviceName byte array representing the name, may be 0 in length, not null. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws NullPointerException if deviceName is null + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) { + Preconditions.checkNotNull(deviceName); + this.mDeviceName = deviceName; + return this; + } + + /** + * Sets the Bluetooth Class of Device; used for UI purposes only. + * + *

                Not an indicator of available services! + * + *

                Optional attribute. + * + * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * + * @return {@link OobData#ClassicBuilder} + * + * @throws IllegalArgumentException if length is not equal to + * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. + * @throws NullPointerException if classOfDevice is null. + * + * @hide + */ + @NonNull + @SystemApi + public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) { + Preconditions.checkNotNull(classOfDevice); + if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) { + throw new IllegalArgumentException("classOfDevice must be " + + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length."); + } + this.mClassOfDevice = classOfDevice; + return this; + } + + /** + * Validates and builds the {@link OobDat object for Classic Security. + * + * @return {@link OobData} with previously given builder values. + * + * @hide + */ + @NonNull + @SystemApi + public OobData build() { + final OobData oob = + new OobData(this.mClassicLength, this.mDeviceAddressWithType, + this.mConfirmationHash); + // If we have values, set them, otherwise use default + oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; + oob.mClassOfDevice = (this.mClassOfDevice != null) + ? this.mClassOfDevice : oob.mClassOfDevice; + oob.mRandomizerHash = this.mRandomizerHash; + return oob; + } + } + + // Members (Defaults for Optionals must be set or Parceling fails on NPE) + // Both + private final byte[] mDeviceAddressWithType; + private final byte[] mConfirmationHash; + private byte[] mRandomizerHash = new byte[] { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }; + // Default the name to "Bluetooth Device" + private byte[] mDeviceName = new byte[] { + // Bluetooth + 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, + // Device + 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65 + }; + + // Classic + private final byte[] mClassicLength; + private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS]; + + // LE + private final @LeRole int mLeDeviceRole; + private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS]; + private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS]; + private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE; + + /** + * @return byte array representing the MAC address of a bluetooth device. + * The Address is 6 octets long with a 1 octet address type associated with the address. + * + *

                For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type. + * For LE there are more choices for Address Type. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getDeviceAddressWithType() { + return mDeviceAddressWithType; + } + + /** + * @return byte array representing the confirmationHash value + * which is used to confirm the identity to the controller. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getConfirmationHash() { + return mConfirmationHash; } /** - * Sets the LE Bluetooth Device Address value to be used during LE pairing. - * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for - * a detailed description. + * @return byte array representing the randomizerHash value + * which is used to verify the identity of the controller. + * + * @hide */ - public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) { - mLeBluetoothDeviceAddress = leBluetoothDeviceAddress; + @NonNull + @SystemApi + public byte[] getRandomizerHash() { + return mRandomizerHash; } - public byte[] getSecurityManagerTk() { - return mSecurityManagerTk; + /** + * @return Device Name used for displaying name in UI. + * + *

                Also, this will be populated with the LE Local Name if the data is for LE. + * + * @hide + */ + @Nullable + @SystemApi + public byte[] getDeviceName() { + return mDeviceName; + } + + /** + * @return byte array representing the oob data length which is the length + * of all of the data including these octets. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getClassicLength() { + return mClassicLength; + } + + /** + * @return byte array representing the class of device for UI display. + * + *

                Does not indicate services available; for display only. + * + * @hide + */ + @NonNull + @SystemApi + public byte[] getClassOfDevice() { + return mClassOfDevice; } /** - * Sets the Temporary Key value to be used by the LE Security Manager during - * LE pairing. The value shall be 16 bytes. Please see Bluetooth CSSv6, - * Part A 1.8 for a detailed description. + * @return Temporary Key used for LE pairing. + * + * @hide */ - public void setSecurityManagerTk(byte[] securityManagerTk) { - mSecurityManagerTk = securityManagerTk; + @Nullable + @SystemApi + public byte[] getLeTemporaryKey() { + return mLeTemporaryKey; } - public byte[] getLeSecureConnectionsConfirmation() { - return mLeSecureConnectionsConfirmation; + /** + * @return Appearance used for LE pairing. For use in UI situations + * when determining what sort of icons or text to display regarding + * the device. + * + * @hide + */ + @Nullable + @SystemApi + public byte[] getLeAppearance() { + return mLeTemporaryKey; } - public void setLeSecureConnectionsConfirmation(byte[] leSecureConnectionsConfirmation) { - mLeSecureConnectionsConfirmation = leSecureConnectionsConfirmation; + /** + * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability. + * + *

                Possible LE Flags: + * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. + * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. + * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of + * LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to + * Same Device Capable (Controller). + * Bit 49 of LMP Feature Mask Definitions. + * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to + * Same Device Capable (Host). + * Bit 55 of LMP Feature Mask Definitions. + * 0x05- 0x07 Reserved + * + * @hide + */ + @NonNull + @SystemApi + @LeFlag + public int getLeFlags() { + return mLeFlags; } - public byte[] getLeSecureConnectionsRandom() { - return mLeSecureConnectionsRandom; + /** + * @return the supported and preferred roles of the LE device. + * + *

                Possible Values: + * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported + * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported + * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; + * Peripheral Preferred + * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred + * 0x04 - 0xFF Reserved + * + * @hide + */ + @NonNull + @SystemApi + @LeRole + public int getLeDeviceRole() { + return mLeDeviceRole; } - public void setLeSecureConnectionsRandom(byte[] leSecureConnectionsRandom) { - mLeSecureConnectionsRandom = leSecureConnectionsRandom; + /** + * Classic Security Constructor + */ + private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType, + @NonNull byte[] confirmationHash) { + mClassicLength = classicLength; + mDeviceAddressWithType = deviceAddressWithType; + mConfirmationHash = confirmationHash; + mLeDeviceRole = -1; // Satisfy final } - public OobData() { + /** + * LE Security Constructor + */ + private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole, + @NonNull byte[] confirmationHash) { + mDeviceAddressWithType = deviceAddressWithType; + mLeDeviceRole = leDeviceRole; + mConfirmationHash = confirmationHash; + mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final } private OobData(Parcel in) { - mLeBluetoothDeviceAddress = in.createByteArray(); - mSecurityManagerTk = in.createByteArray(); - mLeSecureConnectionsConfirmation = in.createByteArray(); - mLeSecureConnectionsRandom = in.createByteArray(); + // Both + mDeviceAddressWithType = in.createByteArray(); + mConfirmationHash = in.createByteArray(); + mRandomizerHash = in.createByteArray(); + mDeviceName = in.createByteArray(); + + // Classic + mClassicLength = in.createByteArray(); + mClassOfDevice = in.createByteArray(); + + // LE + mLeDeviceRole = in.readInt(); + mLeTemporaryKey = in.createByteArray(); + mLeAppearance = in.createByteArray(); + mLeFlags = in.readInt(); } + /** + * @hide + */ @Override public int describeContents() { return 0; } + /** + * @hide + */ @Override - public void writeToParcel(Parcel out, int flags) { - out.writeByteArray(mLeBluetoothDeviceAddress); - out.writeByteArray(mSecurityManagerTk); - out.writeByteArray(mLeSecureConnectionsConfirmation); - out.writeByteArray(mLeSecureConnectionsRandom); + public void writeToParcel(@NonNull Parcel out, int flags) { + // Both + // Required + out.writeByteArray(mDeviceAddressWithType); + // Required + out.writeByteArray(mConfirmationHash); + // Optional + out.writeByteArray(mRandomizerHash); + // Optional + out.writeByteArray(mDeviceName); + + // Classic + // Required + out.writeByteArray(mClassicLength); + // Optional + out.writeByteArray(mClassOfDevice); + + // LE + // Required + out.writeInt(mLeDeviceRole); + // Required + out.writeByteArray(mLeTemporaryKey); + // Optional + out.writeByteArray(mLeAppearance); + // Optional + out.writeInt(mLeFlags); } + // For Parcelable public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public OobData createFromParcel(Parcel in) { @@ -108,4 +970,47 @@ public class OobData implements Parcelable { return new OobData[size]; } }; + + /** + * @return a {@link String} representation of the OobData object. + * + * @hide + */ + @Override + @NonNull + public String toString() { + return "OobData: \n\t" + // Both + + "Device Address With Type: " + toHexString(mDeviceAddressWithType) + "\n\t" + + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t" + + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t" + + "Device Name: " + toHexString(mDeviceName) + "\n\t" + // Classic + + "OobData Length: " + toHexString(mClassicLength) + "\n\t" + + "Class of Device: " + toHexString(mClassOfDevice) + "\n\t" + // LE + + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t" + + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t" + + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t" + + "LE Flags: " + toHexString(mLeFlags) + "\n\t"; + } + + @NonNull + private String toHexString(@NonNull int b) { + return toHexString(new byte[] {(byte) b}); + } + + @NonNull + private String toHexString(@NonNull byte b) { + return toHexString(new byte[] {b}); + } + + @NonNull + private String toHexString(@NonNull byte[] array) { + StringBuilder builder = new StringBuilder(array.length * 2); + for (byte b: array) { + builder.append(String.format("%02x", b)); + } + return builder.toString(); + } } -- GitLab From ceb1bd3c9c7f8b0f27391276f64a3fa2236818b5 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Tue, 16 Mar 2021 16:22:55 -0700 Subject: [PATCH 1245/1408] Fix CTS Failure Bug: 182849735 Test: atest CtsSystemApiAnnotationTestCases:android.signature.cts.api.AnnotationTest#testAnnotation -- --abi x86_64 Change-Id: I1b9e77b05cd23299bf179c854e136fa341c81566 --- framework/java/android/bluetooth/OobData.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 98107461fb4..08d694eb93e 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -76,7 +76,7 @@ public final class OobData implements Parcelable { private static final String TAG = "OobData"; /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ @SystemApi - private static final int OOB_LENGTH_OCTETS = 2; + public static final int OOB_LENGTH_OCTETS = 2; /** * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). * (AD 3.1.2) (CSS 1.6.2) @@ -590,7 +590,6 @@ public final class OobData implements Parcelable { * * @hide */ - @SystemApi private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { Preconditions.checkNotNull(confirmationHash); -- GitLab From ebd19b814db49cd1dc5ee5b66f0a91c969135914 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 18 Mar 2021 12:26:09 -0600 Subject: [PATCH 1246/1408] Implement per-field matching of ScanRecord. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As part of building out support for robustly matching Bluetooth LE devices in the wild, this change checks all "fields" contained in a ScanRecord against a given BytesMatcher. To support matching variable-length Eddystone beacons, this change also expands BytesMatcher to support both exact length and prefix based rules, which are then used with rules that verify that example Eddystone and iBeacon values can be detected with these rules: Eddystone: ⊆0016AAFE/00FFFFFF iBeacon: ⊆00FF4C0002/00FFFFFFFF Expands testing to confirm all newly added capabilities are working. Bug: 181812624 Test: atest BluetoothTests:android.bluetooth.le Test: atest FrameworksCoreTests:android.os.BytesMatcherTest Change-Id: I1cff8e08604436f4bba6f55aad64c3ce5969bf56 --- .../java/android/bluetooth/le/ScanRecord.java | 22 ++++++ framework/tests/AndroidTest.xml | 32 ++++++++ .../android/bluetooth/le/ScanRecordTest.java | 77 ++++++++++++++++++- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 framework/tests/AndroidTest.xml diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index c0c1aa1634f..794b512772f 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.Predicate; /** * Represents a scan record from Bluetooth LE scan. @@ -168,6 +169,27 @@ public final class ScanRecord { return mBytes; } + /** + * Test if any fields contained inside this scan record are matched by the + * given matcher. + * + * @hide + */ + public boolean matchesAnyField(@NonNull Predicate matcher) { + int pos = 0; + while (pos < mBytes.length) { + final int length = mBytes[pos] & 0xFF; + if (length == 0) { + break; + } + if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) { + return true; + } + pos += length + 1; + } + return false; + } + private ScanRecord(List serviceUuids, List serviceSolicitationUuids, SparseArray manufacturerData, diff --git a/framework/tests/AndroidTest.xml b/framework/tests/AndroidTest.xml new file mode 100644 index 00000000000..f93c4ebf5bf --- /dev/null +++ b/framework/tests/AndroidTest.xml @@ -0,0 +1,32 @@ + + + + diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index 8b3db7e4d93..c287ea9d856 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -16,13 +16,18 @@ package android.bluetooth.le; -import android.bluetooth.le.ScanRecord; +import android.os.BytesMatcher; import android.os.ParcelUuid; import android.test.suitebuilder.annotation.SmallTest; +import com.android.internal.util.HexDump; + import junit.framework.TestCase; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; /** * Unit test cases for {@link ScanRecord}. @@ -31,6 +36,66 @@ import java.util.Arrays; * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' */ public class ScanRecordTest extends TestCase { + /** + * Example raw beacons captured from a Blue Charm BC011 + */ + private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000"; + private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000"; + private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000"; + private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000"; + + @SmallTest + public void testMatchesAnyField_Eddystone_Parser() { + final List found = new ArrayList<>(); + final Predicate matcher = (v) -> { + found.add(HexDump.toHexString(v)); + return false; + }; + ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL)) + .matchesAnyField(matcher); + + assertEquals(Arrays.asList( + "020106", + "0303AAFE", + "1716AAFE10EE01626C7565636861726D626561636F6E7300", + "09168020691E0EFE1355", + "1109426C7565436861726D5F313639363835"), found); + } + + @SmallTest + public void testMatchesAnyField_Eddystone() { + final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF"); + assertMatchesAnyField(RECORD_URL, matcher); + assertMatchesAnyField(RECORD_UUID, matcher); + assertMatchesAnyField(RECORD_TLM, matcher); + assertNotMatchesAnyField(RECORD_IBEACON, matcher); + } + + @SmallTest + public void testMatchesAnyField_iBeacon_Parser() { + final List found = new ArrayList<>(); + final Predicate matcher = (v) -> { + found.add(HexDump.toHexString(v)); + return false; + }; + ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON)) + .matchesAnyField(matcher); + + assertEquals(Arrays.asList( + "020106", + "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5", + "09168020691E0EFE1355", + "1109426C7565436861726D5F313639363835"), found); + } + + @SmallTest + public void testMatchesAnyField_iBeacon() { + final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF"); + assertNotMatchesAnyField(RECORD_URL, matcher); + assertNotMatchesAnyField(RECORD_UUID, matcher); + assertNotMatchesAnyField(RECORD_TLM, matcher); + assertMatchesAnyField(RECORD_IBEACON, matcher); + } @SmallTest public void testParser() { @@ -70,4 +135,14 @@ public class ScanRecordTest extends TestCase { } } + + private static void assertMatchesAnyField(String record, BytesMatcher matcher) { + assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record)) + .matchesAnyField(matcher)); + } + + private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) { + assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record)) + .matchesAnyField(matcher)); + } } -- GitLab From 6eb9300814c54c1096c67b124bbfc245495f9588 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Tue, 16 Mar 2021 21:14:40 -0700 Subject: [PATCH 1247/1408] Add new @SystemApi for specifying AddressType and IRK Bug: 178234318 Test: compiles and runs Tag: #feature Change-Id: Ib67e681af01260df98602003b2aca47963494c6f --- .../android/bluetooth/BluetoothAdapter.java | 21 +++ .../android/bluetooth/BluetoothDevice.java | 20 +++ .../java/android/bluetooth/le/ScanFilter.java | 163 +++++++++++++++++- 3 files changed, 199 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cc0b22afe38..38863c2c8cc 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -52,6 +52,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.util.Preconditions; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -3118,6 +3120,25 @@ public final class BluetoothAdapter { return true; } + /** + * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0" + * is a RANDOM STATIC address. + * + * RANDOM STATIC: (addr & 0b11) == 0b11 + * RANDOM RESOLVABLE: (addr & 0b11) == 0b10 + * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00 + * + * @param address Bluetooth address as string + * @return true if the 2 Least Significant Bits of the address equals 0b11. + * + * @hide + */ + public static boolean isAddressRandomStatic(@NonNull String address) { + Preconditions.checkNotNull(address); + return checkBluetoothAddress(address) + && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; + } + @UnsupportedAppUsage /*package*/ IBluetoothManager getBluetoothManager() { return mManagerService; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 41bc77651db..c30b8af3da5 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1002,6 +1002,24 @@ public final class BluetoothDevice implements Parcelable { public static final String EXTRA_MAS_INSTANCE = "android.bluetooth.device.extra.MAS_INSTANCE"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "ADDRESS_TYPE_" }, + value = { + /** Hardware MAC Address */ + ADDRESS_TYPE_PUBLIC, + /** Address is either resolvable, non-resolvable or static.*/ + ADDRESS_TYPE_RANDOM, + } + ) + public @interface AddressType {} + + /** Hardware MAC Address of the device */ + public static final int ADDRESS_TYPE_PUBLIC = 0; + /** Address is either resolvable, non-resolvable or static. */ + public static final int ADDRESS_TYPE_RANDOM = 1; + /** * Lazy initialization. Guaranteed final after first object constructed, or * getService() called. @@ -1010,6 +1028,7 @@ public final class BluetoothDevice implements Parcelable { private static volatile IBluetooth sService; private final String mAddress; + @AddressType private final int mAddressType; /*package*/ @UnsupportedAppUsage @@ -1064,6 +1083,7 @@ public final class BluetoothDevice implements Parcelable { } mAddress = address; + mAddressType = ADDRESS_TYPE_PUBLIC; } @Override diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 7511fd051e4..86721502d40 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,15 +16,19 @@ package android.bluetooth.le; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDevice.AddressType; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import com.android.internal.util.BitUtils; +import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.List; @@ -53,6 +57,11 @@ public final class ScanFilter implements Parcelable { @Nullable private final String mDeviceAddress; + private final @AddressType int mAddressType; + + @Nullable + private final byte[] mIrk; + @Nullable private final ParcelUuid mServiceUuid; @Nullable @@ -79,12 +88,12 @@ public final class ScanFilter implements Parcelable { /** @hide */ public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); - private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask, ParcelUuid solicitationUuid, ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask, - int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { + int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, + @AddressType int addressType, @Nullable byte[] irk) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; @@ -97,6 +106,8 @@ public final class ScanFilter implements Parcelable { mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; + mAddressType = addressType; + mIrk = irk; } @Override @@ -280,6 +291,23 @@ public final class ScanFilter implements Parcelable { return mDeviceAddress; } + /** + * @hide + */ + @SystemApi + public @AddressType int getAddressType() { + return mAddressType; + } + + /** + * @hide + */ + @SystemApi + @Nullable + public byte[] getIrk() { + return mIrk; + } + @Nullable public byte[] getServiceData() { return mServiceData; @@ -516,8 +544,16 @@ public final class ScanFilter implements Parcelable { */ public static final class Builder { + /** + * @hide + */ + @SystemApi + public static final int LEN_IRK_OCTETS = 16; + private String mDeviceName; private String mDeviceAddress; + private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC; + private byte[] mIrk; private ParcelUuid mServiceUuid; private ParcelUuid mUuidMask; @@ -546,14 +582,130 @@ public final class ScanFilter implements Parcelable { * * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link - * BluetoothAdapter#checkBluetoothAddress}. + * BluetoothAdapter#checkBluetoothAddress}. The @AddressType is defaulted to {@link + * BluetoothDevice#ADDRESS_TYPE_PUBLIC} * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { - if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { + return setDeviceAddress(mDeviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); + } + + /** + * Set filter on Address with AddressType + * + *

                This key is used to resolve a private address from a public address. + * + * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the + * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link + * BluetoothAdapter#checkBluetoothAddress}. May be any type of address. + * @param addressType indication of the type of address + * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} + * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} + * + * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. + * @throws IllegalArgumentException If the {@code addressType} is invalid length + * @throws NullPointerException if {@code deviceAddress} is null. + * + * @hide + */ + @NonNull + @SystemApi + public Builder setDeviceAddress(@NonNull String deviceAddress, + @AddressType int addressType) { + return setDeviceAddressInternal(deviceAddress, addressType, null); + } + + /** + * Set filter on Address with AddressType and the Identity Resolving Key (IRK). + * + *

                The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from + * a PRIVATE_ADDRESS type. + * + * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the + * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link + * BluetoothAdapter#checkBluetoothAddress}. This Address type must only be PUBLIC OR RANDOM + * STATIC. + * @param addressType indication of the type of address + * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} + * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} + * @param irk non-null byte array representing the Identity Resolving Key + * + * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. + * @throws IllegalArgumentException if the {@code irk} is invalid length. + * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not + * PUBLIC or RANDOM STATIC when an IRK is present. + * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null. + * + * @hide + */ + @NonNull + @SystemApi + public Builder setDeviceAddress(@NonNull String deviceAddress, + @AddressType int addressType, + @NonNull byte[] irk) { + Preconditions.checkNotNull(irk); + if (irk.length != LEN_IRK_OCTETS) { + throw new IllegalArgumentException("'irk' is invalid length!"); + } + return setDeviceAddressInternal(deviceAddress, addressType, irk); + } + + /** + * Set filter on Address with AddressType and the Identity Resolving Key (IRK). + * + *

                Internal setter for the device address + * + * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the + * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link + * BluetoothAdapter#checkBluetoothAddress}. + * @param addressType indication of the type of address + * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} + * @param irk non-null byte array representing the Identity Resolving Address; nullable + * internally. + * + * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. + * @throws IllegalArgumentException If the {@code addressType} is invalid length. + * @throws NullPointerException if {@code deviceAddress} is null. + * + * @hide + */ + @NonNull + private Builder setDeviceAddressInternal(@NonNull String deviceAddress, + @AddressType int addressType, + @Nullable byte[] irk) { + + // Make sure our deviceAddress is valid! + Preconditions.checkNotNull(deviceAddress); + if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { throw new IllegalArgumentException("invalid device address " + deviceAddress); } + + // Verify type range + if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC + || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) { + throw new IllegalArgumentException("'addressType' is invalid!"); + } + + // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address. + if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) { + // Don't want a bad combination of address and irk! + if (irk != null) { + // Since there are 3 possible RANDOM subtypes we must check to make sure + // the correct type of address is used. + if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) { + throw new IllegalArgumentException( + "Invalid combination: IRK requires either a PUBLIC or " + + "RANDOM (STATIC) Address"); + } + } + } + + // PUBLIC doesn't require extra work + // Without an IRK any address may be accepted + mDeviceAddress = deviceAddress; + mAddressType = addressType; + mIrk = irk; return this; } @@ -727,7 +879,8 @@ public final class ScanFilter implements Parcelable { mServiceUuid, mUuidMask, mServiceSolicitationUuid, mServiceSolicitationUuidMask, mServiceDataUuid, mServiceData, mServiceDataMask, - mManufacturerId, mManufacturerData, mManufacturerDataMask); + mManufacturerId, mManufacturerData, mManufacturerDataMask, + mAddressType, mIrk); } } } -- GitLab From 99424db11fffecf78a2e91155844bc2a2914b867 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 22 Mar 2021 15:34:02 -0600 Subject: [PATCH 1248/1408] Request new Bluetooth runtime permissions. An upcoming platform change is introducing a new "Nearby devices" runtime permission which contains the new BLUETOOTH_CONNECT and BLUETOOTH_SCAN permissions. We have logic in place to use to translate the older BLUETOOTH and BLUETOOTH_ADMIN permissions into these new runtime permissions, but modern apps will need to pivot to requesting them directly as part of targeting Android S. This change requests both the old and new permissions to avoid breakage while the new permission enforcement is being phased in. Bug: 181813006 Test: atest CtsPermission2TestCases Test: atest CtsPermission3TestCases Test: atest CtsStatsdAtomHostTestCases Change-Id: I39f45e7d22d132d44c84017cd98e6d9e98533c7f --- framework/tests/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index 6849a90f501..f8c69ac17bb 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -20,6 +20,8 @@ + + -- GitLab From 3780ca4911d229bbcf194f70af4050e13ae61ae0 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 11 Mar 2021 12:17:44 -0800 Subject: [PATCH 1249/1408] Makes BluetoothDevice#setAlias a public API Tag: #feature Bug: 181093329 Test: atest BluetoothDeviceTest Change-Id: Ib94bedab5d6d4c63a19096f61187f58dd8937b55 --- .../android/bluetooth/BluetoothDevice.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 07dbdce3984..efdbc762bb7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; +import android.companion.AssociationRequest; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; @@ -1211,8 +1212,7 @@ public final class BluetoothDevice implements Parcelable { } /** - * Get the Bluetooth alias of the remote device. - *

                Alias is the locally modified name of a remote device. + * Get the locally modifiable name (alias) of the remote Bluetooth device. * * @return the Bluetooth alias, the friendly device name if no alias, or * null if there was a problem @@ -1238,25 +1238,35 @@ public final class BluetoothDevice implements Parcelable { } /** - * Set the Bluetooth alias of the remote device. - *

                Alias is the locally modified name of a remote device. - *

                This methoid overwrites the alias. The changed - * alias is saved in the local storage so that the change - * is preserved over power cycle. + * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method + * overwrites the previously stored alias. The new alias is saved in local + * storage so that the change is preserved over power cycles. * - * @return true on success, false on error - * @hide + *

                This method requires the calling app to be associated with Companion Device Manager (see + * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest, + * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the {@link + * android.Manifest.permission#BLUETOOTH} permission. Alternatively, if the caller has the + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can bypass the + * Companion Device Manager association requirement. + * + * @param alias is the new locally modifiable name for the remote Bluetooth device which must be + * non-null and not the empty string. + * @return {@code true} if the alias is successfully set, {@code false} on error + * @throws IllegalArgumentException if the alias is {@code null} or the empty string */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String alias) { + if (alias == null || alias.isEmpty()) { + throw new IllegalArgumentException("Cannot set the alias to null or the empty string"); + } final IBluetooth service = sService; if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); return false; } try { - return service.setRemoteAlias(this, alias); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + return service.setRemoteAlias(this, alias, adapter.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 2f2ffe3c9f2b623d6eeafab0aceacb873a058b3d Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Mon, 22 Mar 2021 17:31:06 -0700 Subject: [PATCH 1250/1408] ScanFilter.setDeviceAddress: Should send deviceAddress instead of mDeviceAddress to the next level Bug: 183409081 Test: build and run Change-Id: I2d25d21b0f143fc7362679e0094455c9132fda9d --- framework/java/android/bluetooth/le/ScanFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 86721502d40..3c20dcac8ca 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -587,7 +587,7 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { - return setDeviceAddress(mDeviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); + return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); } /** -- GitLab From 9095a45ee67189d1a1bd2e007676e1fd5aff57d8 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 24 Mar 2021 00:19:00 -0700 Subject: [PATCH 1251/1408] Remove BluetoothHeadset#setPriority which was deprecated in Android 11 Tag: #feature Bug: 183551808 Test: Manual Change-Id: I88745589ec66d3060d24b530fe49fea8926726c6 --- framework/java/android/bluetooth/BluetoothHeadset.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 4fb557780d0..632572dea3c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -567,6 +567,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if priority is set, false on error * @hide * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)} + * @removed */ @Deprecated @SystemApi -- GitLab From 4e1e3f7fcaf5ec6392dc869e683bbd7be2ec2e8d Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Wed, 24 Mar 2021 17:37:07 +0000 Subject: [PATCH 1252/1408] Replace BLUETOOTH and BLUETOOTH_ADMIN permission checks in BluetoothManagerService. This replaces the checks for the legacy bluetooth permissions with checks for the new BLUETOOTH_CONNECT permission. All of the new checks are done as ForPreflight in this CL, i.e. app ops will be checked but not noted. Changes to make some of the checks ForDataDelivery (i.e. noting the app op) will be made in follow-ups - this requires additional work as the calling package and attribution tag must be plumbed through from the app side. Bug: 183203469 Test: builds Change-Id: I7b99a203ac25a8dcc0d33f442b08203244bff534 --- .../bluetooth/BluetoothManagerService.java | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f0c9ba93b08..09cfac00567 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -16,7 +16,10 @@ package com.android.server; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; @@ -43,6 +46,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -95,8 +99,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; - private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; - private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String BLUETOOTH_PRIVILEGED = android.Manifest.permission.BLUETOOTH_PRIVILEGED; @@ -696,14 +698,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Callback is null in unregisterAdapter"); return; } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return; + } synchronized (mCallbacks) { mCallbacks.unregister(callback); } } public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return; + } if (callback == null) { Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); return; @@ -714,7 +720,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return; + } if (callback == null) { Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); return; @@ -945,8 +953,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return false; + } } return true; } @@ -1672,7 +1681,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String getAddress() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return null; + } if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { Slog.w(TAG, "getAddress(): not allowed for non-active and non system user"); @@ -1704,7 +1715,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String getName() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!checkConnectPermissionForPreflight(mContext)) { + return null; + } if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { Slog.w(TAG, "getName(): not allowed for non-active and non system user"); @@ -2459,7 +2472,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); } private void bluetoothStateChangeHandler(int prevState, int newState) { @@ -2538,7 +2551,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); } } @@ -2827,4 +2840,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub { default: return "UNKNOWN[" + reason + "]"; } } + + /** + * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns + * false if the result is a soft denial. Throws SecurityException if the result is a hard + * denial. + * + *

                Should be used in situations where the app op should not be noted. + */ + private static boolean checkConnectPermissionForPreflight(Context context) { + int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight( + context, BLUETOOTH_CONNECT); + if (permissionCheckResult == PERMISSION_HARD_DENIED) { + throw new SecurityException("Need BLUETOOTH_CONNECT permission"); + } + return permissionCheckResult == PERMISSION_GRANTED; + } } -- GitLab From d7da98e70a62da10b6f20ea9cbadb788cd28f9d5 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 9 Dec 2020 14:25:15 -0800 Subject: [PATCH 1253/1408] Bluetooth airplane listener: Check for null Check mAirplaneHelper != null before accessing it. Note that this can be null if Settings.Global.RADIO_BLUETOOTH is enabled in AIRPLANE_MODE_RADIOS. We don't need to disable Bluetooth if it's allowed in airplane mode. Test: atest FrameworksServicesTests Bug: 174254527 Change-Id: I93ae4ec6b75b7ffb8e75848d7779ab64593221bd (cherry picked from commit a107eae7e921aa56d271bc531dbc11d55b4abd0b) --- .../server/bluetooth/BluetoothAirplaneModeListener.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index 31cd5d519d8..0b2cc889092 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -127,7 +127,9 @@ class BluetoothAirplaneModeListener { } return; } - mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + if (mAirplaneHelper != null) { + mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + } } @VisibleForTesting -- GitLab From 723962b9f7bc5da3c3ac18e735e4a1f6fa100e7c Mon Sep 17 00:00:00 2001 From: Svet Ganov Date: Wed, 24 Feb 2021 04:09:05 +0000 Subject: [PATCH 1254/1408] Runtime permission attribution improvements When an app is proxying access to runtime permission protected data it needs to check whether the calling app has a permission to the data it is about to proxy which leaves a trace in app ops that the requesting app perofmed a data access. However, then the app doing the work needs to get the protected data itself from the OS which access gets attributed only to itself. As a result there are two data accesses in app ops where only the first one is a proxy one that app A got access to Foo through app B - that is the one we want to show in the permission tracking UIs - and one for the data access - that is the one we would want to blame on the calling app, and in fact, these two accesses should be one - that app A accessed Foo though B. This limitation requires fragile one off workarounds where both accesses use the same attribution tag and sys UI has hardcoded rules to dedupe. Since this is not documented we cannot expect that the ecosystem would reliably do this workaround in apps that that the workaround in the OS would be respected by every OEM. This change adds a mechaism to resolve this issue. It allows for an app to create an attribution context for another app and then any private data access thorugh this context would result in a single app op blame that A accessed Foo though B, i.e. we no longer have double accounting. Also this can be nested through apps, e.g. app A asks app B which asks app C for contacts. In this case app B creates an attribution context for app A and calls into app C which creates an attribution context for app B. When app C gets contacts the entire attribution chain would get a porper, single blame: that C accessed the data, that B got the data from C, and that A got the data form B. Furthermore, this mechanism ensures that apps cannot forget to check permissions for the caller before proxying private data. In our example B and C don't need to check the permisisons for A and B, respectively, since the permisisons for the entire attribution chain are checked before data delivery. Attribution chains are not forgeable preventing a bad actor to create an arbitrary one - each attribution is created by the app it refers to and points to a chain of attributions created by their corresponding apps. This change also fixes a bug where all content provider accesses were double counted in app ops due to double noting. While at this it also fixes that apps can now access their own last ops. There was a bug where one could not pass null getting the attributed ops from a historical package ops while this is a valid use case since if there is no attribution everything is mapped to the null tag. There were some app op APIs not being piped thorough the app ops delegate and by extension through the app ops policy. Also now that we have nice way to express the permission chain in a call we no longer need the special casing in activity manager to handle content provider accesses through the OS. Fixed a bug where we don't properly handle the android.os.shell calls with an invlaid tag which was failing while the shell can do any tag. Finally, to ensure the mechanims is validated and works end-to-end we are adding support for a voice recognizer to blame the client app for the mic access. The recognition service can create a blaming context when opening the mic and if the mic is open, which would do all permission checks, we would not do so again. Since changes to PermissionChercker for handling attribution sources were made the CL also hooks up renounced permissoins in the request permission flow and in the permission checks. bug:158792096 bug:180647319 Test:atest CtsPermissionsTestCases atest CtsPermissions2TestCases atest CtsPermissions3TestCases atest CtsPermissions4TestCases atest CtsPermissions5TestCases atest CtsAppOpsTestCases atest CtsAppOps2TestCases Change-Id: Ib04585515d3dc3956966005ae9d94955b2f3ee08 --- framework/java/android/bluetooth/BluetoothAdapter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index a3d19ca6425..0be7b732b4b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -745,7 +745,6 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService) { - if (managerService == null) { throw new IllegalArgumentException("bluetooth manager service is null"); } -- GitLab From 5131226e72c1933c210a0b30635bf8f636363235 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Thu, 1 Apr 2021 11:00:16 +0100 Subject: [PATCH 1255/1408] Pass AttributionSource to bluetooth scanning methods. This passes the AttributionSource to AdapterService and GattService methods that perform scanning or discovery. Bug: 183203469 Test: atest GattServiceTest Test: atest AdapterServiceTest Test: atest CtsPermissionTestCases:android.permission.cts.NearbyDevicesPermissionTest Change-Id: Id68558624fbae69eac3a8613b9536eb6e0df75bf --- .../android/bluetooth/BluetoothAdapter.java | 16 ++++++++-------- .../bluetooth/le/BluetoothLeScanner.java | 17 +++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0be7b732b4b..5446deb5605 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -38,12 +38,14 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -853,8 +855,8 @@ public final class BluetoothAdapter { } synchronized (mLock) { if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(), - getAttributionTag()); + sBluetoothLeScanner = + new BluetoothLeScanner(mManagerService, getAttributionSource()); } } return sBluetoothLeScanner; @@ -1664,13 +1666,11 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private String getAttributionTag() { - // Workaround for legacy API for getting a BluetoothAdapter not - // passing a context + private AttributionSource getAttributionSource() { if (mContext != null) { - return mContext.getAttributionTag(); + return mContext.getAttributionSource(); } - return null; + return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(), null); } /** @@ -1710,7 +1710,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getOpPackageName(), getAttributionTag()); + return mService.startDiscovery(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2888fbd8a36..2601cd4300e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -26,6 +26,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -82,9 +83,7 @@ public final class BluetoothLeScanner { private final Handler mHandler; private BluetoothAdapter mBluetoothAdapter; private final Map mLeScanClients; - - private final String mOpPackageName; - private final String mFeatureId; + private final AttributionSource mAttributionSource; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. @@ -95,13 +94,12 @@ public final class BluetoothLeScanner { * @hide */ public BluetoothLeScanner(IBluetoothManager bluetoothManager, - @NonNull String opPackageName, @Nullable String featureId) { + @NonNull AttributionSource attributionSource) { mBluetoothManager = bluetoothManager; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap(); - mOpPackageName = opPackageName; - mFeatureId = featureId; + mAttributionSource = attributionSource; } /** @@ -256,8 +254,7 @@ public final class BluetoothLeScanner { wrapper.startRegistration(); } else { try { - gatt.startScanForIntent(callbackIntent, settings, filters, mOpPackageName, - mFeatureId); + gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } @@ -298,7 +295,7 @@ public final class BluetoothLeScanner { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent, mOpPackageName); + gatt.stopScanForIntent(callbackIntent); } catch (RemoteException e) { } } @@ -458,7 +455,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mResultStorages, mOpPackageName, mFeatureId); + mResultStorages, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); -- GitLab From c382c0edc39e4dc6e286a4bc07ea69a7ee32fb02 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Fri, 19 Mar 2021 00:07:02 -0700 Subject: [PATCH 1256/1408] OOB: Implement generateLocalOutOfBand API CTS-Coverage-Bug: 184395281 Bug: 178007935 Tag: #feature Test: manual Change-Id: I5bc11ac13d9cbb8f76f422aa4aea8295ebec95b4 --- .../android/bluetooth/BluetoothAdapter.java | 163 ++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 19 ++ 2 files changed, 182 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 0be7b732b4b..b6ca8d81314 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -28,6 +28,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; +import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; @@ -3046,6 +3047,168 @@ public final class BluetoothAdapter { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "OOB_ERROR_" }, value = { + OOB_ERROR_UNKNOWN, + OOB_ERROR_ANOTHER_ACTIVE_REQUEST, + OOB_ERROR_ADAPTER_DISABLED + }) + public @interface OobError {} + + /** + * An unknown error has occurred in the controller, stack, or callback pipeline. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_UNKNOWN = 0; + + /** + * If another application has already requested {@link OobData} then another fetch will be + * disallowed until the callback is removed. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; + + /** + * The adapter is currently disabled, please enable it. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_ADAPTER_DISABLED = 2; + + /** + * Provides callback methods for receiving {@link OobData} from the host stack, as well as an + * error interface in order to allow the caller to determine next steps based on the {@link + * ErrorCode}. + * + * @hide + */ + @SystemApi + public interface OobDataCallback { + /** + * Handles the {@link OobData} received from the host stack. + * + * @param transport - whether the {@link OobData} is generated for LE or Classic. + * @param oobData - data generated in the host stack(LE) or controller (Classic) + * + * @hide + */ + void onOobData(@Transport int transport, @Nullable OobData oobData); + + /** + * Provides feedback when things don't go as expected. + * + * @param errorCode - the code descibing the type of error that occurred. + * + * @hide + */ + void onError(@OobError int errorCode); + } + + /** + * Wraps an AIDL interface around an {@link OobDataCallback} interface. + * + * @see {@link IBluetoothOobDataCallback} for interface definition. + * + * @hide + */ + public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub { + private final OobDataCallback mCallback; + private final Executor mExecutor; + + /** + * @param callback - object to receive {@link OobData} must be a non null argument + * + * @throws NullPointerException if the callback is null. + */ + WrappedOobDataCallback(@NonNull OobDataCallback callback, + @NonNull @CallbackExecutor Executor executor) { + Preconditions.checkNotNull(callback); + Preconditions.checkNotNull(executor); + mCallback = callback; + mExecutor = executor; + } + /** + * Wrapper function to relay to the {@link OobDataCallback#onOobData} + * + * @param transport - whether the {@link OobData} is generated for LE or Classic. + * @param oobData - data generated in the host stack(LE) or controller (Classic) + * + * @hide + */ + public void onOobData(@Transport int transport, OobData oobData) { + mExecutor.execute(new Runnable() { + public void run() { + mCallback.onOobData(transport, oobData); + } + }); + } + /** + * Wrapper function to relay to the {@link OobDataCallback#onError} + * + * @param errorCode - the code descibing the type of error that occurred. + * + * @hide + */ + public void onError(@OobError int errorCode) { + mExecutor.execute(new Runnable() { + public void run() { + mCallback.onError(errorCode); + } + }); + } + } + + /** + * Fetches a secret data value that can be used for a secure and simple pairing experience. + * + *

                This is the Local Out of Band data the comes from the + * + *

                This secret is the local Out of Band data. This data is used to securely and quickly + * pair two devices with minimal user interaction. + * + *

                For example, this secret can be transferred to a remote device out of band (meaning any + * other way besides using bluetooth). Once the remote device finds this device using the + * information given in the data, such as the PUBLIC ADDRESS, the remote device could then + * connect to this device using this secret when the pairing sequenece asks for the secret. + * This device will respond by automatically accepting the pairing due to the secret being so + * trustworthy. + * + * @param transport - provide type of transport (e.g. LE or Classic). + * @param callback - target object to receive the {@link OobData} value. + * + * @throws NullPointerException if callback is null. + * @throws IllegalArgumentException if the transport is not valid. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void generateLocalOobData(@Transport int transport, + @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { + if (transport != BluetoothDevice.TRANSPORT_BREDR && transport + != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); + } + Preconditions.checkNotNull(callback); + if (!isEnabled()) { + Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); + callback.onError(OOB_ERROR_ADAPTER_DISABLED); + } else { + try { + mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, + executor)); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + /** * Enable control of the Bluetooth Adapter for a single application. * diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a96c14f216f..0c208fd71ae 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -952,6 +952,21 @@ public final class BluetoothDevice implements Parcelable { @SystemApi public static final int ACCESS_REJECTED = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "TRANSPORT_" }, + value = { + /** Allow host to automatically select a transport (dual-mode only) */ + TRANSPORT_AUTO, + /** Use Classic or BR/EDR transport.*/ + TRANSPORT_BREDR, + /** Use Low Energy transport.*/ + TRANSPORT_LE, + } + ) + public @interface Transport {} + /** * No preference of physical transport for GATT connections to remote dual-mode devices */ @@ -1084,6 +1099,10 @@ public final class BluetoothDevice implements Parcelable { public void onBrEdrDown() { if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); } + + public void onOobData(@Transport int transport, OobData oobData) { + if (DBG) Log.d(TAG, "onOobData: got data"); + } }; /** -- GitLab From e47bc17c5796bad3f28347c9965f4e5204058255 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Fri, 19 Mar 2021 00:07:02 -0700 Subject: [PATCH 1257/1408] OOB: Implement getLocalOutOfBand API CTS-Coverage-Bug: 184395281 Bug: 178007935 Tag: #feature Test: manual Change-Id: I5bc11ac13d9cbb8f76f422aa4aea8295ebec95b4 Merged-In: I5bc11ac13d9cbb8f76f422aa4aea8295ebec95b4 --- .../android/bluetooth/BluetoothAdapter.java | 163 ++++++++++++++++++ .../android/bluetooth/BluetoothDevice.java | 19 ++ 2 files changed, 182 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 38863c2c8cc..7d62327738d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -28,6 +28,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; +import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; @@ -3013,6 +3014,168 @@ public final class BluetoothAdapter { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "OOB_ERROR_" }, value = { + OOB_ERROR_UNKNOWN, + OOB_ERROR_ANOTHER_ACTIVE_REQUEST, + OOB_ERROR_ADAPTER_DISABLED + }) + public @interface OobError {} + + /** + * An unknown error has occurred in the controller, stack, or callback pipeline. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_UNKNOWN = 0; + + /** + * If another application has already requested {@link OobData} then another fetch will be + * disallowed until the callback is removed. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; + + /** + * The adapter is currently disabled, please enable it. + * + * @hide + */ + @SystemApi + public static final int OOB_ERROR_ADAPTER_DISABLED = 2; + + /** + * Provides callback methods for receiving {@link OobData} from the host stack, as well as an + * error interface in order to allow the caller to determine next steps based on the {@link + * ErrorCode}. + * + * @hide + */ + @SystemApi + public interface OobDataCallback { + /** + * Handles the {@link OobData} received from the host stack. + * + * @param transport - whether the {@link OobData} is generated for LE or Classic. + * @param oobData - data generated in the host stack(LE) or controller (Classic) + * + * @hide + */ + void onOobData(@Transport int transport, @Nullable OobData oobData); + + /** + * Provides feedback when things don't go as expected. + * + * @param errorCode - the code descibing the type of error that occurred. + * + * @hide + */ + void onError(@OobError int errorCode); + } + + /** + * Wraps an AIDL interface around an {@link OobDataCallback} interface. + * + * @see {@link IBluetoothOobDataCallback} for interface definition. + * + * @hide + */ + public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub { + private final OobDataCallback mCallback; + private final Executor mExecutor; + + /** + * @param callback - object to receive {@link OobData} must be a non null argument + * + * @throws NullPointerException if the callback is null. + */ + WrappedOobDataCallback(@NonNull OobDataCallback callback, + @NonNull @CallbackExecutor Executor executor) { + Preconditions.checkNotNull(callback); + Preconditions.checkNotNull(executor); + mCallback = callback; + mExecutor = executor; + } + /** + * Wrapper function to relay to the {@link OobDataCallback#onOobData} + * + * @param transport - whether the {@link OobData} is generated for LE or Classic. + * @param oobData - data generated in the host stack(LE) or controller (Classic) + * + * @hide + */ + public void onOobData(@Transport int transport, OobData oobData) { + mExecutor.execute(new Runnable() { + public void run() { + mCallback.onOobData(transport, oobData); + } + }); + } + /** + * Wrapper function to relay to the {@link OobDataCallback#onError} + * + * @param errorCode - the code descibing the type of error that occurred. + * + * @hide + */ + public void onError(@OobError int errorCode) { + mExecutor.execute(new Runnable() { + public void run() { + mCallback.onError(errorCode); + } + }); + } + } + + /** + * Fetches a secret data value that can be used for a secure and simple pairing experience. + * + *

                This is the Local Out of Band data the comes from the + * + *

                This secret is the local Out of Band data. This data is used to securely and quickly + * pair two devices with minimal user interaction. + * + *

                For example, this secret can be transferred to a remote device out of band (meaning any + * other way besides using bluetooth). Once the remote device finds this device using the + * information given in the data, such as the PUBLIC ADDRESS, the remote device could then + * connect to this device using this secret when the pairing sequenece asks for the secret. + * This device will respond by automatically accepting the pairing due to the secret being so + * trustworthy. + * + * @param transport - provide type of transport (e.g. LE or Classic). + * @param callback - target object to receive the {@link OobData} value. + * + * @throws NullPointerException if callback is null. + * @throws IllegalArgumentException if the transport is not valid. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void generateLocalOobData(@Transport int transport, + @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { + if (transport != BluetoothDevice.TRANSPORT_BREDR && transport + != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); + } + Preconditions.checkNotNull(callback); + if (!isEnabled()) { + Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); + callback.onError(OOB_ERROR_ADAPTER_DISABLED); + } else { + try { + mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, + executor)); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + /** * Enable control of the Bluetooth Adapter for a single application. * diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c30b8af3da5..a40bf343239 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -931,6 +931,21 @@ public final class BluetoothDevice implements Parcelable { @SystemApi public static final int ACCESS_REJECTED = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = { "TRANSPORT_" }, + value = { + /** Allow host to automatically select a transport (dual-mode only) */ + TRANSPORT_AUTO, + /** Use Classic or BR/EDR transport.*/ + TRANSPORT_BREDR, + /** Use Low Energy transport.*/ + TRANSPORT_LE, + } + ) + public @interface Transport {} + /** * No preference of physical transport for GATT connections to remote dual-mode devices */ @@ -1063,6 +1078,10 @@ public final class BluetoothDevice implements Parcelable { public void onBrEdrDown() { if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); } + + public void onOobData(@Transport int transport, OobData oobData) { + if (DBG) Log.d(TAG, "onOobData: got data"); + } }; /** -- GitLab From 01085dcfecd9a0d16427c123360991871878d5a7 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 7 Apr 2021 19:43:57 -0600 Subject: [PATCH 1258/1408] New BLUETOOTH_ADVERTISE manifest permission. This change is part of defining a distinct BLUETOOTH_ADVERTISE permission to guard the BluetoothLeAdvertiser APIs, since that's a distinct enough of an operation from SCAN and CONNECT. It'll continue to be covered under the general "Nearby devices" runtime permission group. Bug: 181813006 Test: atest CtsPermission2TestCases Test: atest CtsPermission3TestCases Change-Id: I8b62e4d625df1e201f12a73025cd29c431feea79 --- framework/tests/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/tests/AndroidManifest.xml b/framework/tests/AndroidManifest.xml index f8c69ac17bb..75583d5298c 100644 --- a/framework/tests/AndroidManifest.xml +++ b/framework/tests/AndroidManifest.xml @@ -20,6 +20,7 @@ + -- GitLab From c8353bb6d89b2fb286e02907570d4dcc4169d4ff Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Tue, 13 Apr 2021 11:53:56 -0700 Subject: [PATCH 1259/1408] Bluetooth OOB: Fix getLeAppearance Bug: 185196125 Test: Use OOB pairing Change-Id: I1cb1c33b0b17f2fd242f6579844996c2ccf09e62 --- framework/java/android/bluetooth/OobData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 08d694eb93e..d6868e0ffd5 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -830,7 +830,7 @@ public final class OobData implements Parcelable { @Nullable @SystemApi public byte[] getLeAppearance() { - return mLeTemporaryKey; + return mLeAppearance; } /** -- GitLab From 63a11e8871f9aed864dbafd55cfe4c3646b01288 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Wed, 14 Apr 2021 11:45:35 -0700 Subject: [PATCH 1260/1408] OOB generateLocalOobData unhide @SystemApi callback methods CTS-Coverage-Bug: 184395281 Bug: 178007935 Tag: #feature Test: compiles Change-Id: I2d4167a6c92ee0cc24da12df206838161c8f3318 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 79fd8072f9f..236185e2a28 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3095,8 +3095,6 @@ public final class BluetoothAdapter { * * @param transport - whether the {@link OobData} is generated for LE or Classic. * @param oobData - data generated in the host stack(LE) or controller (Classic) - * - * @hide */ void onOobData(@Transport int transport, @Nullable OobData oobData); @@ -3104,8 +3102,6 @@ public final class BluetoothAdapter { * Provides feedback when things don't go as expected. * * @param errorCode - the code descibing the type of error that occurred. - * - * @hide */ void onError(@OobError int errorCode); } -- GitLab From 8f80e4a05b3f1b227f40de5ec0e9a6297154ffc0 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 2 Apr 2021 08:06:09 -0600 Subject: [PATCH 1261/1408] 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/BluetoothA2dp.java | 100 +++++-- .../android/bluetooth/BluetoothA2dpSink.java | 20 +- .../android/bluetooth/BluetoothAdapter.java | 269 +++++++++++++----- .../bluetooth/BluetoothAvrcpController.java | 10 +- .../android/bluetooth/BluetoothDevice.java | 226 +++++++++++---- .../java/android/bluetooth/BluetoothGatt.java | 113 +++++--- .../BluetoothGattCharacteristic.java | 2 - .../bluetooth/BluetoothGattDescriptor.java | 4 - .../bluetooth/BluetoothGattServer.java | 57 ++-- .../bluetooth/BluetoothGattService.java | 8 +- .../android/bluetooth/BluetoothHeadset.java | 149 +++++++--- .../bluetooth/BluetoothHeadsetClient.java | 35 ++- .../android/bluetooth/BluetoothHealth.java | 53 ++-- .../bluetooth/BluetoothHearingAid.java | 47 ++- .../android/bluetooth/BluetoothHidDevice.java | 23 +- .../android/bluetooth/BluetoothHidHost.java | 54 ++-- .../android/bluetooth/BluetoothLeAudio.java | 40 +-- .../android/bluetooth/BluetoothManager.java | 18 +- .../java/android/bluetooth/BluetoothMap.java | 33 ++- .../android/bluetooth/BluetoothMapClient.java | 53 +++- .../java/android/bluetooth/BluetoothPan.java | 42 ++- .../java/android/bluetooth/BluetoothPbap.java | 13 +- .../bluetooth/BluetoothPbapClient.java | 4 + .../android/bluetooth/BluetoothProfile.java | 6 - .../bluetooth/BluetoothProfileConnector.java | 3 + .../java/android/bluetooth/BluetoothSap.java | 36 ++- .../bluetooth/BluetoothServerSocket.java | 3 - .../android/bluetooth/BluetoothSocket.java | 9 +- .../RequiresBluetoothAdvertisePermission.java | 39 +++ .../RequiresBluetoothConnectPermission.java | 39 +++ .../RequiresBluetoothLocationPermission.java | 41 +++ .../RequiresBluetoothScanPermission.java | 39 +++ ...equiresLegacyBluetoothAdminPermission.java | 39 +++ .../RequiresLegacyBluetoothPermission.java | 39 +++ .../android/bluetooth/le/AdvertisingSet.java | 32 ++- .../bluetooth/le/BluetoothLeAdvertiser.java | 59 +++- .../bluetooth/le/BluetoothLeScanner.java | 58 +++- .../le/PeriodicAdvertisingManager.java | 18 +- .../BluetoothAirplaneModeListener.java | 2 + .../bluetooth/BluetoothManagerService.java | 46 ++- .../bluetooth/BluetoothModeChangeHelper.java | 2 + 41 files changed, 1446 insertions(+), 437 deletions(-) create mode 100644 framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java create mode 100644 framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java create mode 100644 framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java create mode 100644 framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java create mode 100644 framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java create mode 100644 framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 16413e1a1db..a268e168ae7 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -23,6 +23,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -69,10 +72,10 @@ public final class BluetoothA2dp implements BluetoothProfile { *

                {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

                Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; @@ -90,10 +93,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * *

                {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, - * - *

                Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PLAYING_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; @@ -112,11 +115,11 @@ public final class BluetoothA2dp implements BluetoothProfile { * be null if no device is active. *

              * - *

              Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = @@ -133,11 +136,11 @@ public final class BluetoothA2dp implements BluetoothProfile { * connected, otherwise it is not included. *

            * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage(trackingBug = 181103983) public static final String ACTION_CODEC_CONFIG_CHANGED = @@ -307,7 +310,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -347,7 +352,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -368,6 +375,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { @@ -387,6 +396,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { @@ -406,6 +417,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { @@ -441,7 +454,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); @@ -468,7 +483,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @UnsupportedAppUsage(trackingBug = 171933273) @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { @@ -495,7 +512,10 @@ public final class BluetoothA2dp 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -514,7 +534,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -546,7 +569,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); @@ -620,6 +645,8 @@ public final class BluetoothA2dp implements BluetoothProfile { * @param volume Absolute volume to be set on AVRCP side * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); try { @@ -636,10 +663,11 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Check if A2DP profile is streaming music. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device BluetoothDevice device */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isA2dpPlaying(BluetoothDevice device) { try { final IBluetoothA2dp service = getService(); @@ -662,6 +690,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean shouldSendVolumeKeys(BluetoothDevice device) { if (isEnabled() && isValidDevice(device)) { ParcelUuid[] uuids = device.getUuids(); @@ -686,7 +715,9 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @UnsupportedAppUsage(trackingBug = 181103983) @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); verifyDeviceNotNull(device, "getCodecStatus"); @@ -714,7 +745,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(trackingBug = 181103983) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setCodecConfigPreference(@NonNull BluetoothDevice device, @NonNull BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); @@ -744,7 +777,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void enableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "enableOptionalCodecs"); @@ -759,7 +794,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disableOptionalCodecs(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); verifyDeviceNotNull(device, "disableOptionalCodecs"); @@ -773,6 +810,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * active A2DP Bluetooth device. * @param enable if true, enable the optional codecs, other disable them */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { final IBluetoothA2dp service = getService(); @@ -800,7 +838,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { verifyDeviceNotNull(device, "isOptionalCodecsSupported"); @@ -826,7 +866,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); @@ -853,7 +895,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 67f3d7b5d71..d81316e357d 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -21,6 +21,9 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -160,7 +163,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -243,8 +248,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Get the current audio configuration for the A2DP source device, * or null if the device has no audio configuration * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Remote bluetooth device. * @return audio configuration for the device, or null * @@ -252,6 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * * @hide */ + @RequiresLegacyBluetoothPermission public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -278,7 +282,10 @@ public final class BluetoothA2dpSink 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -297,7 +304,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 236185e2a28..972e9e6d73b 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -17,7 +17,6 @@ package android.bluetooth; -import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,11 +24,18 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; @@ -98,11 +104,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. *

            *

            This class is thread safe.

            - *

            Note: - * Most methods require the {@link android.Manifest.permission#BLUETOOTH} - * permission and some also require the - * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - *

            *
            *

            Developer Guides

            *

            @@ -144,8 +145,8 @@ public final class BluetoothAdapter { *

            Always contains the extra fields {@link #EXTRA_STATE} and {@link * #EXTRA_PREVIOUS_STATE} containing the new and old states * respectively. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"; @@ -278,8 +279,10 @@ public final class BluetoothAdapter { *

            Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} * for global notification whenever the scan mode changes. For example, an * application can be notified when the device has ended discoverability. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; @@ -305,8 +308,10 @@ public final class BluetoothAdapter { * has rejected the request or an error has occurred. *

            Applications can also listen for {@link #ACTION_STATE_CHANGED} * for global notification whenever Bluetooth is turned on or off. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; @@ -325,10 +330,12 @@ public final class BluetoothAdapter { * has rejected the request or an error has occurred. *

            Applications can also listen for {@link #ACTION_STATE_CHANGED} * for global notification whenever Bluetooth is turned on or off. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; @@ -355,8 +362,10 @@ public final class BluetoothAdapter { *

            Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes * respectively. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; @@ -508,15 +517,19 @@ public final class BluetoothAdapter { * progress, and existing connections will experience limited bandwidth * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing * discovery. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; /** * Broadcast Action: The local Bluetooth adapter has finished the device * discovery process. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; @@ -526,8 +539,10 @@ public final class BluetoothAdapter { *

            This name is visible to remote Bluetooth devices. *

            Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing * the name. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; /** @@ -559,9 +574,10 @@ public final class BluetoothAdapter { * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE} * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; @@ -870,7 +886,7 @@ public final class BluetoothAdapter { * * @return true if the local adapter is turned on */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission public boolean isEnabled() { return getState() == BluetoothAdapter.STATE_ON; } @@ -921,6 +937,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE() { if (!isBleScanAlwaysAvailable()) { return false; @@ -966,6 +983,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) { return false; @@ -986,6 +1004,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache( 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Void query) { try { return mService.getState(); @@ -1039,7 +1058,7 @@ public final class BluetoothAdapter { * * @return current state of Bluetooth adapter */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission @AdapterState public int getState() { int state = getStateInternal(); @@ -1075,7 +1094,7 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission @AdapterState @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") @@ -1122,7 +1141,9 @@ public final class BluetoothAdapter { * * @return true to indicate adapter startup has begun, or false on immediate error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enable() { if (isEnabled()) { if (DBG) { @@ -1159,7 +1180,9 @@ public final class BluetoothAdapter { * * @return true to indicate adapter shutdown has begun, or false on immediate error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable() { try { return mManagerService.disable(ActivityThread.currentPackageName(), true); @@ -1172,13 +1195,13 @@ public final class BluetoothAdapter { /** * Turn off the local Bluetooth adapter and don't persist the setting. * - *

            Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission - * * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ @UnsupportedAppUsage(trackingBug = 171933273) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable(boolean persist) { try { @@ -1195,7 +1218,12 @@ public final class BluetoothAdapter { * * @return Bluetooth hardware address as string */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.LOCAL_MAC_ADDRESS, + }) public String getAddress() { try { return mManagerService.getAddress(); @@ -1208,10 +1236,12 @@ public final class BluetoothAdapter { /** * Get the friendly Bluetooth name of the local Bluetooth adapter. *

            This name is visible to remote Bluetooth devices. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @return the Bluetooth name, or null on error */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName() { try { return mManagerService.getName(); @@ -1228,7 +1258,7 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset() { try { mServiceLock.readLock().lock(); @@ -1253,7 +1283,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @Nullable ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; @@ -1285,7 +1317,9 @@ public final class BluetoothAdapter { * @param name a valid Bluetooth name * @return true if the name was set, false otherwise */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String name) { if (getState() != STATE_ON) { return false; @@ -1311,7 +1345,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothClass getBluetoothClass() { if (getState() != STATE_ON) { return null; @@ -1340,7 +1376,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBluetoothClass(BluetoothClass bluetoothClass) { if (getState() != STATE_ON) { return false; @@ -1367,7 +1403,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @IoCapability public int getIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; @@ -1395,7 +1433,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { @@ -1418,7 +1456,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @IoCapability public int getLeIoCapability() { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; @@ -1446,7 +1486,7 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setLeIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { @@ -1475,7 +1515,9 @@ public final class BluetoothAdapter { * * @return scan mode */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @ScanMode public int getScanMode() { if (getState() != STATE_ON) { @@ -1522,7 +1564,9 @@ public final class BluetoothAdapter { */ @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + "shows UI that confirms the user wants to go into discoverable mode.") - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean setScanMode(@ScanMode int mode, long durationMillis) { if (getState() != STATE_ON) { return false; @@ -1571,7 +1615,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean setScanMode(@ScanMode int mode) { if (getState() != STATE_ON) { return false; @@ -1591,6 +1637,7 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getDiscoverableTimeout() { if (getState() != STATE_ON) { return -1; @@ -1610,6 +1657,7 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) { return; @@ -1635,7 +1683,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); @@ -1703,7 +1751,10 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery() { if (getState() != STATE_ON) { return false; @@ -1737,7 +1788,9 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean cancelDiscovery() { if (getState() != STATE_ON) { return false; @@ -1773,7 +1826,9 @@ public final class BluetoothAdapter { * * @return true if discovering */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering() { if (getState() != STATE_ON) { return false; @@ -1805,7 +1860,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean removeActiveDevice(@ActiveDeviceUse int profiles) { if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL && profiles != ACTIVE_DEVICE_ALL) { @@ -1845,7 +1904,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean setActiveDevice(@NonNull BluetoothDevice device, @ActiveDeviceUse int profiles) { if (device == null) { @@ -1889,7 +1952,11 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -1917,7 +1984,10 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -1938,6 +2008,7 @@ public final class BluetoothAdapter { * * @return true if Multiple Advertisement feature is supported */ + @RequiresLegacyBluetoothPermission public boolean isMultipleAdvertisementSupported() { if (getState() != STATE_ON) { return false; @@ -1981,6 +2052,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache( 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Boolean recompute(Void query) { try { mServiceLock.readLock().lock(); @@ -2012,6 +2084,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports on-chip filtering */ + @RequiresLegacyBluetoothPermission public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) { return false; @@ -2024,6 +2097,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports on-chip scan batching */ + @RequiresLegacyBluetoothPermission public boolean isOffloadedScanBatchingSupported() { if (!getLeAccess()) { return false; @@ -2046,6 +2120,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE 2M PHY feature */ + @RequiresLegacyBluetoothPermission public boolean isLe2MPhySupported() { if (!getLeAccess()) { return false; @@ -2068,6 +2143,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Coded PHY feature */ + @RequiresLegacyBluetoothPermission public boolean isLeCodedPhySupported() { if (!getLeAccess()) { return false; @@ -2090,6 +2166,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Extended Advertising feature */ + @RequiresLegacyBluetoothPermission public boolean isLeExtendedAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2112,6 +2189,7 @@ public final class BluetoothAdapter { * * @return true if chipset supports LE Periodic Advertising feature */ + @RequiresLegacyBluetoothPermission public boolean isLePeriodicAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2135,6 +2213,7 @@ public final class BluetoothAdapter { * * @return the maximum LE advertising data length. */ + @RequiresLegacyBluetoothPermission public int getLeMaximumAdvertisingDataLength() { if (!getLeAccess()) { return 0; @@ -2172,7 +2251,9 @@ public final class BluetoothAdapter { * @return the maximum number of connected audio devices * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getMaxConnectedAudioDevices() { try { mServiceLock.readLock().lock(); @@ -2193,6 +2274,7 @@ public final class BluetoothAdapter { * @return true if there are hw entries available for matching beacons * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isHardwareTrackingFiltersAvailable() { if (!getLeAccess()) { return false; @@ -2223,6 +2305,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { SynchronousResultReceiver receiver = new SynchronousResultReceiver(); requestControllerActivityEnergyInfo(receiver); @@ -2248,6 +2331,7 @@ public final class BluetoothAdapter { * @param result The callback to which to send the activity info. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { mServiceLock.readLock().lock(); @@ -2275,7 +2359,9 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getMostRecentlyConnectedDevices() { if (getState() != STATE_ON) { return new ArrayList<>(); @@ -2303,7 +2389,9 @@ public final class BluetoothAdapter { * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Set getBondedDevices() { if (getState() != STATE_ON) { return toDeviceSet(new BluetoothDevice[0]); @@ -2368,6 +2456,7 @@ public final class BluetoothAdapter { * This method must not be called when mService is null. */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Void query) { try { return mService.getAdapterConnectionState(); @@ -2401,6 +2490,7 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothPermission public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2429,6 +2519,7 @@ public final class BluetoothAdapter { new PropertyInvalidatedCache( 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Integer query) { try { mServiceLock.readLock().lock(); @@ -2471,7 +2562,10 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) { return BluetoothProfile.STATE_DISCONNECTED; @@ -2486,7 +2580,6 @@ public final class BluetoothAdapter { *

            Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. *

            Valid RFCOMM channels are in range 1 to 30. - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket @@ -2494,6 +2587,9 @@ public final class BluetoothAdapter { * permissions, or channel in use. * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { return listenUsingRfcommOn(channel, false, false); } @@ -2505,7 +2601,6 @@ public final class BluetoothAdapter { *

            Use {@link BluetoothServerSocket#accept} to retrieve incoming * connections from a listening {@link BluetoothServerSocket}. *

            Valid RFCOMM channels are in range 1 to 30. - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} *

            To auto assign a channel without creating a SDP record use * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. * @@ -2519,6 +2614,9 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2559,7 +2657,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or channel in use. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, true, true); @@ -2591,7 +2691,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or channel in use. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, false); @@ -2622,7 +2724,6 @@ public final class BluetoothAdapter { * closed, or if this application closes unexpectedly. *

            Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to * connect to this socket from another device using the same {@link UUID}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @param name service name for SDP record * @param uuid uuid for SDP record @@ -2632,12 +2733,15 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, true); } - + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { BluetoothServerSocket socket; @@ -2663,6 +2767,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port); @@ -2694,6 +2799,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) throws IOException { BluetoothServerSocket socket = @@ -2726,11 +2832,11 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { return listenUsingL2capOn(port, false, false); } - /** * Construct an insecure L2CAP server socket. * Call #accept to retrieve connections to this socket. @@ -2743,6 +2849,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); BluetoothServerSocket socket = @@ -2769,11 +2876,14 @@ public final class BluetoothAdapter { /** * Read the local Out of Band Pairing Data - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @return Pair of Hash and Randomizer * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public Pair readOutOfBandData() { return null; } @@ -2863,6 +2973,7 @@ public final class BluetoothAdapter { * @param profile * @param proxy Profile proxy object */ + @SuppressLint("AndroidFrameworkRequiresPermission") public void closeProfileProxy(int profile, BluetoothProfile proxy) { if (proxy == null) { return; @@ -2937,6 +3048,7 @@ public final class BluetoothAdapter { private final IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { + @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); @@ -3031,7 +3143,9 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableNoAutoConnect() { if (isEnabled()) { if (DBG) { @@ -3184,7 +3298,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void generateLocalOobData(@Transport int transport, @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { if (transport != BluetoothDevice.TRANSPORT_BREDR && transport @@ -3228,12 +3342,14 @@ public final class BluetoothAdapter { * reason. If Bluetooth is already on and if this function is called to turn * it on, the api will return true and a callback will be called. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} - * * @param on True for on, false for off. * @param callback The callback to notify changes to the state. * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean changeApplicationBluetoothState(boolean on, BluetoothStateChangeCallback callback) { return false; @@ -3252,6 +3368,7 @@ public final class BluetoothAdapter { /** * @hide */ + @SuppressLint("AndroidFrameworkRequiresPermission") public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { private BluetoothStateChangeCallback mCallback; @@ -3443,7 +3560,10 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(LeScanCallback callback) { return startLeScan(null, callback); } @@ -3462,7 +3582,10 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { if (DBG) { Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); @@ -3559,7 +3682,9 @@ public final class BluetoothAdapter { * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ @Deprecated - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan(LeScanCallback callback) { if (DBG) { Log.d(TAG, "stopLeScan()"); @@ -3600,7 +3725,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull BluetoothServerSocket listenUsingL2capChannel() throws IOException { BluetoothServerSocket socket = @@ -3646,7 +3773,9 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions, or unable to start this CoC */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel() throws IOException { BluetoothServerSocket socket = @@ -3691,7 +3820,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); @@ -3764,7 +3893,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); @@ -3853,6 +3982,7 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if the callback is already registered * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); @@ -3895,6 +4025,7 @@ public final class BluetoothAdapter { * @return true if the callback was unregistered successfully, false otherwise * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterBluetoothConnectionCallback( @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 4e7e4415c54..887cf3f08b9 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,6 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -54,10 +58,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile { *

            {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0c208fd71ae..1201663d1d1 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,6 +25,11 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PropertyInvalidatedCache; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.companion.AssociationRequest; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -66,9 +70,6 @@ import java.util.UUID; * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using * {@link #createL2capChannel(int)} over Bluetooth LE. * - *

            Note: - * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. - * *

            *

            Developer Guides

            *

            @@ -108,10 +109,12 @@ public final class BluetoothDevice implements Parcelable { *

            Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or * {@link #EXTRA_RSSI} if they are available. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} and - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive. */ // TODO: Change API to not broadcast RSSI if not available (incoming connection) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND"; @@ -120,9 +123,11 @@ public final class BluetoothDevice implements Parcelable { * Broadcast Action: Bluetooth class of a remote device has changed. *

            Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. * {@see BluetoothClass} */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED"; @@ -133,8 +138,10 @@ public final class BluetoothDevice implements Parcelable { *

            Always contains the extra field {@link #EXTRA_DEVICE}. *

            ACL connections are managed automatically by the Android Bluetooth * stack. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED"; @@ -146,8 +153,10 @@ public final class BluetoothDevice implements Parcelable { * this intent as a hint to immediately terminate higher level connections * (RFCOMM, L2CAP, or profile connections) to the remote device. *

            Always contains the extra field {@link #EXTRA_DEVICE}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; @@ -158,8 +167,10 @@ public final class BluetoothDevice implements Parcelable { *

            Always contains the extra field {@link #EXTRA_DEVICE}. *

            ACL connections are managed automatically by the Android Bluetooth * stack. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED"; @@ -169,8 +180,10 @@ public final class BluetoothDevice implements Parcelable { * been retrieved for the first time, or changed since the last retrieval. *

            Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_NAME}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"; @@ -179,9 +192,11 @@ public final class BluetoothDevice implements Parcelable { * Broadcast Action: Indicates the alias of a remote device has been * changed. *

            Always contains the extra field {@link #EXTRA_DEVICE}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ @SuppressLint("ActionValue") + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED"; @@ -191,10 +206,12 @@ public final class BluetoothDevice implements Parcelable { * device. For example, if a device is bonded (paired). *

            Always contains the extra fields {@link #EXTRA_DEVICE}, {@link * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. */ // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also // contain a hidden extra field EXTRA_REASON with the result code. + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED"; @@ -204,10 +221,12 @@ public final class BluetoothDevice implements Parcelable { * been retrieved for the first time, or changed since the last retrieval *

            Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_BATTERY_LEVEL}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BATTERY_LEVEL_CHANGED = "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"; @@ -642,8 +661,10 @@ public final class BluetoothDevice implements Parcelable { * device are requested to be fetched using Service Discovery Protocol *

            Always contains the extra field {@link #EXTRA_DEVICE} *

            Always contains the extra field {@link #EXTRA_UUID} - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; @@ -657,20 +678,23 @@ public final class BluetoothDevice implements Parcelable { * Broadcast Action: Indicates a failure to retrieve the name of a remote * device. *

            Always contains the extra field {@link #EXTRA_DEVICE}. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} to receive. * * @hide */ //TODO: is this actually useful? + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_NAME_FAILED = "android.bluetooth.device.action.NAME_FAILED"; /** * Broadcast Action: This intent is used to broadcast PAIRING REQUEST - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to - * receive. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; @@ -1206,7 +1230,9 @@ public final class BluetoothDevice implements Parcelable { * * @return the Bluetooth name, or null if there was a problem. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName() { final IBluetooth service = sService; if (service == null) { @@ -1235,7 +1261,9 @@ public final class BluetoothDevice implements Parcelable { * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getType() { final IBluetooth service = sService; if (service == null) { @@ -1257,7 +1285,9 @@ public final class BluetoothDevice implements Parcelable { * null if there was a problem */ @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -1293,7 +1323,9 @@ public final class BluetoothDevice implements Parcelable { * @return {@code true} if the alias is successfully set, {@code false} on error * @throws IllegalArgumentException if the alias is {@code null} or the empty string */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setAlias(@NonNull String alias) { if (alias == null || alias.isEmpty()) { throw new IllegalArgumentException("Cannot set the alias to null or the empty string"); @@ -1321,7 +1353,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getBatteryLevel() { final IBluetooth service = sService; if (service == null) { @@ -1346,7 +1380,9 @@ public final class BluetoothDevice implements Parcelable { * * @return false on immediate error, true if bonding will begin */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond() { return createBond(TRANSPORT_AUTO); } @@ -1367,7 +1403,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond(int transport) { return createBondInternal(transport, null, null); } @@ -1395,7 +1433,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data) { if (remoteP192Data == null && remoteP256Data == null) { @@ -1406,6 +1444,7 @@ public final class BluetoothDevice implements Parcelable { return createBondInternal(transport, remoteP192Data, remoteP256Data); } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, @Nullable OobData remoteP256Data) { final IBluetooth service = sService; @@ -1430,7 +1469,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isBondingInitiatedLocally() { final IBluetooth service = sService; if (service == null) { @@ -1452,7 +1493,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess() { final IBluetooth service = sService; if (service == null) { @@ -1480,7 +1521,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond() { final IBluetooth service = sService; if (service == null) { @@ -1504,6 +1545,7 @@ public final class BluetoothDevice implements Parcelable { new PropertyInvalidatedCache( 8, BLUETOOTH_BONDING_CACHE_PROPERTY) { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(BluetoothDevice query) { try { return sService.getBondState(query); @@ -1532,7 +1574,10 @@ public final class BluetoothDevice implements Parcelable { * * @return the bond state */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public int getBondState() { final IBluetooth service = sService; if (service == null) { @@ -1560,7 +1605,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog() { final IBluetooth service = sService; if (service == null) { @@ -1583,7 +1628,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected() { final IBluetooth service = sService; if (service == null) { @@ -1606,7 +1653,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted() { final IBluetooth service = sService; if (service == null) { @@ -1626,7 +1675,9 @@ public final class BluetoothDevice implements Parcelable { * * @return Bluetooth class object, or null on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothClass getBluetoothClass() { final IBluetooth service = sService; if (service == null) { @@ -1653,7 +1704,9 @@ public final class BluetoothDevice implements Parcelable { * * @return the supported features (UUIDs) of the remote device, or null on error */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public ParcelUuid[] getUuids() { final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { @@ -1681,7 +1734,9 @@ public final class BluetoothDevice implements Parcelable { * @return False if the check fails, True if the process of initiating an ACL connection * to the remote device was started. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean fetchUuidsWithSdp() { final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { @@ -1707,8 +1762,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. * Detailed status error codes can be found by members of the Bluetooth package in * the AbstractionLayer class. - *

            Requires {@link android.Manifest.permission#BLUETOOTH}. - * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. + *

            The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. * The object type will match one of the SdpXxxRecord types, depending on the UUID searched * for. * @@ -1717,6 +1771,9 @@ public final class BluetoothDevice implements Parcelable { * was started. */ /** @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sdpSearch(ParcelUuid uuid) { final IBluetooth service = sService; if (service == null) { @@ -1733,10 +1790,12 @@ public final class BluetoothDevice implements Parcelable { /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true pin has been set false for error */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPin(byte[] pin) { final IBluetooth service = sService; if (service == null) { @@ -1758,7 +1817,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPin(@NonNull String pin) { byte[] pinBytes = convertPinToBytes(pin); if (pinBytes == null) { @@ -1772,7 +1833,7 @@ public final class BluetoothDevice implements Parcelable { * * @return true confirmation has been sent out false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { final IBluetooth service = sService; if (service == null) { @@ -1795,7 +1856,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelPairing() { final IBluetooth service = sService; if (service == null) { @@ -1827,7 +1890,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @AccessPermission int getPhonebookAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1859,8 +1924,6 @@ public final class BluetoothDevice implements Parcelable { * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot * enter silence mode. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * * @param silence true to enter silence mode, false to exit * @return true on success, false on error. * @throws IllegalStateException if Bluetooth is not turned ON. @@ -1884,8 +1947,6 @@ public final class BluetoothDevice implements Parcelable { /** * Check whether the {@link BluetoothDevice} is in silence mode * - *

            Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. - * * @return true on device in silence mode, otherwise false. * @throws IllegalStateException if Bluetooth is not turned ON. * @hide @@ -1935,7 +1996,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @AccessPermission int getMessageAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -1959,7 +2022,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(@AccessPermission int value) { // Validates param value is one of the accepted constants if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) { @@ -1984,7 +2047,9 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @AccessPermission int getSimAccessPermission() { final IBluetooth service = sService; if (service == null) { @@ -2008,7 +2073,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { @@ -2039,7 +2104,6 @@ public final class BluetoothDevice implements Parcelable { *

            Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

            Valid RFCOMM channels are in range 1 to 30. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @param channel RFCOMM channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection @@ -2048,6 +2112,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createRfcommSocket(int channel) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -2074,7 +2142,6 @@ public final class BluetoothDevice implements Parcelable { *

            Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

            Valid L2CAP PSM channels are in range 1 to 2^16. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @param channel L2cap PSM/channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection @@ -2082,6 +2149,10 @@ public final class BluetoothDevice implements Parcelable { * permissions * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createL2capSocket(int channel) throws IOException { return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel, null); @@ -2095,7 +2166,6 @@ public final class BluetoothDevice implements Parcelable { *

            Use {@link BluetoothSocket#connect} to initiate the outgoing * connection. *

            Valid L2CAP PSM channels are in range 1 to 2^16. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} * * @param channel L2cap PSM/channel to connect to * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection @@ -2103,6 +2173,10 @@ public final class BluetoothDevice implements Parcelable { * permissions * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, null); @@ -2138,7 +2212,10 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -2176,7 +2253,10 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -2192,7 +2272,6 @@ public final class BluetoothDevice implements Parcelable { * Call #connect on the returned #BluetoothSocket to begin the connection. * The remote device will not be authenticated and communication on this * socket will not be encrypted. - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param port remote port * @return An RFCOMM BluetoothSocket @@ -2202,6 +2281,10 @@ public final class BluetoothDevice implements Parcelable { */ @UnsupportedAppUsage(publicAlternatives = "Use " + "{@link #createInsecureRfcommSocketToServiceRecord} instead.") + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -2214,7 +2297,6 @@ public final class BluetoothDevice implements Parcelable { /** * Construct a SCO socket ready to start an outgoing connection. * Call #connect on the returned #BluetoothSocket to begin the connection. - *

            Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @return a SCO BluetoothSocket * @throws IOException on error, for example Bluetooth not available, or insufficient @@ -2222,6 +2304,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public BluetoothSocket createScoSocket() throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); @@ -2269,6 +2355,8 @@ public final class BluetoothDevice implements Parcelable { * automatically connect as soon as the remote device becomes available (true). * @throws IllegalArgumentException if callback is null */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) { return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO)); @@ -2289,6 +2377,8 @@ public final class BluetoothDevice implements Parcelable { * BluetoothDevice#TRANSPORT_LE} * @throws IllegalArgumentException if callback is null */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) { return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK)); @@ -2313,6 +2403,8 @@ public final class BluetoothDevice implements Parcelable { * is set to true. * @throws NullPointerException if callback is null */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy) { return connectGatt(context, autoConnect, callback, transport, phy, null); @@ -2339,6 +2431,8 @@ public final class BluetoothDevice implements Parcelable { * an un-specified background thread. * @throws NullPointerException if callback is null */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, int phy, Handler handler) { @@ -2372,6 +2466,8 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport, boolean opportunistic, int phy, Handler handler) { @@ -2416,7 +2512,10 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "createL2capChannel: Bluetooth is not enabled"); @@ -2444,7 +2543,10 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or insufficient * permissions */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled"); @@ -2472,7 +2574,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { @@ -2500,7 +2602,7 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(@MetadataKey int key) { final IBluetooth service = sService; if (service == null) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 381318b26da..942f8432639 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,14 @@ package android.bluetooth; import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; @@ -157,6 +165,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onClientRegistered(int status, int clientIf) { if (DBG) { Log.d(TAG, "onClientRegistered() - status=" + status @@ -347,6 +356,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onCharacteristicRead(String address, int status, int handle, byte[] value) { if (VDBG) { @@ -404,6 +414,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onCharacteristicWrite(String address, int status, int handle) { if (VDBG) { Log.d(TAG, "onCharacteristicWrite() - Device=" + address @@ -487,6 +498,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onDescriptorRead(String address, int status, int handle, byte[] value) { if (VDBG) { Log.d(TAG, @@ -538,6 +550,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onDescriptorWrite(String address, int status, int handle) { if (VDBG) { Log.d(TAG, @@ -734,6 +747,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Application should call this method as early as possible after it is done with * this GATT client. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close() { if (DBG) Log.d(TAG, "close()"); @@ -817,12 +831,13 @@ public final class BluetoothGatt implements BluetoothProfile { *

            This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} * is used to notify success or failure if the function returns true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param callback GATT callback handler that will receive asynchronous callbacks. * @return If true, the callback will be called to notify success or failure, false on immediate * error */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private boolean registerApp(BluetoothGattCallback callback, Handler handler) { return registerApp(callback, handler, false); } @@ -833,14 +848,15 @@ public final class BluetoothGatt implements BluetoothProfile { *

            This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} * is used to notify success or failure if the function returns true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param callback GATT callback handler that will receive asynchronous callbacks. * @param eatt_support indicate to allow for eatt support * @return If true, the callback will be called to notify success or failure, false on immediate * error * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private boolean registerApp(BluetoothGattCallback callback, Handler handler, boolean eatt_support) { if (DBG) Log.d(TAG, "registerApp()"); @@ -865,6 +881,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Unregister the current application and callbacks. */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); if (mService == null || mClientIf == 0) return; @@ -893,14 +910,15 @@ public final class BluetoothGatt implements BluetoothProfile { * subsequent connections to known devices should be invoked with the * autoConnect parameter set to true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Remote device to connect to * @param autoConnect Whether to directly connect to the remote device (false) or to * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { if (DBG) { @@ -931,9 +949,10 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. - * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disconnect() { if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; @@ -954,6 +973,7 @@ public final class BluetoothGatt implements BluetoothProfile { * * @return true, if the connection attempt was initiated successfully */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, @@ -983,6 +1003,7 @@ public final class BluetoothGatt implements BluetoothProfile { * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or * {@link BluetoothDevice#PHY_OPTION_S8} */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { try { mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, @@ -996,6 +1017,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Read the current transmitter PHY and receiver PHY of the connection. The values are returned * in {@link BluetoothGattCallback#onPhyRead} */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy() { try { mService.clientReadPhy(mClientIf, mDevice.getAddress()); @@ -1022,10 +1044,11 @@ public final class BluetoothGatt implements BluetoothProfile { * triggered. If the discovery was successful, the remote services can be * retrieved using the {@link #getServices} function. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the remote service discovery has been started */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServices() { if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1047,11 +1070,12 @@ public final class BluetoothGatt implements BluetoothProfile { * It should never be used by real applications. The service is not searched * for characteristics and descriptors, or returned in any callback. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the remote service discovery has been started * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServiceByUuid(UUID uuid) { if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1073,11 +1097,10 @@ public final class BluetoothGatt implements BluetoothProfile { *

            This function requires that service discovery has been completed * for the given device. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return List of services on the remote device. Returns an empty list if service discovery has * not yet been performed. */ + @RequiresLegacyBluetoothPermission public List getServices() { List result = new ArrayList(); @@ -1101,12 +1124,11 @@ public final class BluetoothGatt implements BluetoothProfile { *

            If multiple instances of the same service (as identified by UUID) * exist, the first instance of the service is returned. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param uuid UUID of the requested service * @return BluetoothGattService if supported, or null if the requested service is not offered by * the remote device. */ + @RequiresLegacyBluetoothPermission public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { @@ -1124,11 +1146,12 @@ public final class BluetoothGatt implements BluetoothProfile { * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} * callback. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param characteristic Characteristic to read from the remote device * @return true, if the read operation was initiated successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) { return false; @@ -1167,12 +1190,13 @@ public final class BluetoothGatt implements BluetoothProfile { * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} * callback. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param uuid UUID of characteristic to read from the remote device * @return true, if the read operation was initiated successfully * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); if (mService == null || mClientIf == 0) return false; @@ -1202,11 +1226,12 @@ public final class BluetoothGatt implements BluetoothProfile { * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, * reporting the result of the operation. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param characteristic Characteristic to write on the remote device * @return true, if the write operation was initiated successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 && (characteristic.getProperties() @@ -1248,11 +1273,12 @@ public final class BluetoothGatt implements BluetoothProfile { * {@link BluetoothGattCallback#onDescriptorRead} callback is * triggered, signaling the result of the operation. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param descriptor Descriptor value to read from the remote device * @return true, if the read operation was initiated successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readDescriptor(BluetoothGattDescriptor descriptor) { if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); if (mService == null || mClientIf == 0) return false; @@ -1289,11 +1315,12 @@ public final class BluetoothGatt implements BluetoothProfile { *

            A {@link BluetoothGattCallback#onDescriptorWrite} callback is * triggered to report the result of the write operation. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param descriptor Descriptor to write to the associated remote device * @return true, if the write operation was initiated successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false; @@ -1340,10 +1367,11 @@ public final class BluetoothGatt implements BluetoothProfile { * cancel the current transaction without committing any values on the * remote device. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the reliable write transaction has been initiated */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean beginReliableWrite() { if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1367,10 +1395,11 @@ public final class BluetoothGatt implements BluetoothProfile { *

            A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is * invoked to indicate whether the transaction has been executed correctly. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the request to execute the transaction has been sent */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean executeReliableWrite() { if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1396,9 +1425,10 @@ public final class BluetoothGatt implements BluetoothProfile { * *

            Calling this function will discard all queued characteristic write * operations for a given remote device. - * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite() { if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; @@ -1414,6 +1444,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @deprecated Use {@link #abortReliableWrite()} */ @Deprecated + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite(BluetoothDevice mDevice) { abortReliableWrite(); } @@ -1426,12 +1457,13 @@ public final class BluetoothGatt implements BluetoothProfile { * triggered if the remote device indicates that the given characteristic * has changed. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param characteristic The characteristic for which to enable notifications * @param enable Set to true to enable notifications/indications * @return true, if the requested notification status was set successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) { if (DBG) { @@ -1464,6 +1496,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean refresh() { if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1484,10 +1517,11 @@ public final class BluetoothGatt implements BluetoothProfile { *

            The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be * invoked when the RSSI value has been read. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the RSSI value has been requested successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readRemoteRssi() { if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; @@ -1512,10 +1546,11 @@ public final class BluetoothGatt implements BluetoothProfile { *

            A {@link BluetoothGattCallback#onMtuChanged} callback will indicate * whether this operation was successful. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true, if the new MTU value has been requested successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int mtu) { if (DBG) { Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() @@ -1544,6 +1579,7 @@ public final class BluetoothGatt implements BluetoothProfile { * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. * @throws IllegalArgumentException If the parameters are outside of their specified range. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestConnectionPriority(int connectionPriority) { if (connectionPriority < CONNECTION_PRIORITY_BALANCED || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { @@ -1571,6 +1607,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the request is send to the Bluetooth stack. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, int minConnectionEventLen, int maxConnectionEventLen) { diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 8f1b59cf69e..8a7d4baf5ad 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -237,7 +237,6 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Create a new BluetoothGattCharacteristic. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param uuid The UUID for this characteristic * @param properties Properties of this characteristic @@ -344,7 +343,6 @@ public class BluetoothGattCharacteristic implements Parcelable { /** * Adds a descriptor to this characteristic. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param descriptor Descriptor to be added to this characteristic. * @return true, if the descriptor was added to the characteristic diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index 49ba281e2eb..ed5ea087302 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -128,7 +128,6 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Create a new BluetoothGattDescriptor. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param uuid The UUID for this descriptor * @param permissions Permissions for this descriptor @@ -139,7 +138,6 @@ public class BluetoothGattDescriptor implements Parcelable { /** * Create a new BluetoothGattDescriptor. - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param characteristic The characteristic this descriptor belongs to * @param uuid The UUID for this descriptor @@ -228,8 +226,6 @@ public class BluetoothGattDescriptor implements Parcelable { *

            If a remote device offers multiple descriptors with the same UUID, * the instance ID is used to distuinguish between descriptors. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Instance ID of this descriptor * @hide */ diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 088b0169b63..fdb801850e8 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -16,6 +16,9 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -425,6 +428,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Application should call this method as early as possible after it is done with * this GATT server. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close() { if (DBG) Log.d(TAG, "close()"); unregisterCallback(); @@ -436,12 +440,13 @@ public final class BluetoothGattServer implements BluetoothProfile { *

            This is an asynchronous call. The callback is used to notify * success or failure if the function returns true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param callback GATT callback handler that will receive asynchronous callbacks. * @return true, the callback will be called to notify success or failure, false on immediate * error */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { return registerCallback(callback, false); } @@ -452,14 +457,15 @@ public final class BluetoothGattServer implements BluetoothProfile { *

            This is an asynchronous call. The callback is used to notify * success or failure if the function returns true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param callback GATT callback handler that will receive asynchronous callbacks. * @param eatt_support indicates if server can use eatt * @return true, the callback will be called to notify success or failure, false on immediate * error * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) /*package*/ boolean registerCallback(BluetoothGattServerCallback callback, boolean eatt_support) { if (DBG) Log.d(TAG, "registerCallback()"); @@ -504,6 +510,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Unregister the current application and callbacks. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void unregisterCallback() { if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); if (mService == null || mServerIf == 0) return; @@ -548,12 +555,13 @@ public final class BluetoothGattServer implements BluetoothProfile { * subsequent connections to known devices should be invoked with the * autoConnect parameter set to true. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param autoConnect Whether to directly connect to the remote device (false) or to * automatically connect as soon as the remote device becomes available (true). * @return true, if the connection attempt was initiated successfully */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device, boolean autoConnect) { if (DBG) { Log.d(TAG, @@ -576,10 +584,11 @@ public final class BluetoothGattServer implements BluetoothProfile { * Disconnects an established connection, or cancels a connection attempt * currently in progress. * - *

            Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Remote device */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void cancelConnection(BluetoothDevice device) { if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); if (mService == null || mServerIf == 0) return; @@ -609,6 +618,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or * {@link BluetoothDevice#PHY_OPTION_S8} */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { try { mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, @@ -624,6 +634,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * * @param device The remote device to send this response to */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(BluetoothDevice device) { try { mService.serverReadPhy(mServerIf, device.getAddress()); @@ -645,14 +656,15 @@ public final class BluetoothGattServer implements BluetoothProfile { *

          • {@link BluetoothGattServerCallback#onDescriptorWriteRequest} *
          * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device The remote device to send this response to * @param requestId The ID of the request that was received with the callback * @param status The status of the request to be sent to the remote devices * @param offset Value offset for partial read/write response * @param value The value of the attribute that was read/written (optional) */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) { if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); @@ -677,8 +689,6 @@ public final class BluetoothGattServer implements BluetoothProfile { * for every client that requests notifications/indications by writing * to the "Client Configuration" descriptor for the given characteristic. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device The remote device to receive the notification/indication * @param characteristic The local characteristic that has been updated * @param confirm true to request confirmation from the client (indication), false to send a @@ -686,6 +696,9 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return true, if the notification has been triggered successfully * @throws IllegalArgumentException */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); @@ -724,11 +737,12 @@ public final class BluetoothGattServer implements BluetoothProfile { * whether this service has been added successfully. Do not add another service * before this callback. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param service Service to be added to the list of services provided by this device. * @return true, if the request to add service has been initiated */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean addService(BluetoothGattService service) { if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); if (mService == null || mServerIf == 0) return false; @@ -748,11 +762,12 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Removes a service from the list of services to be provided. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param service Service to be removed. * @return true, if the service has been removed */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeService(BluetoothGattService service) { if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); if (mService == null || mServerIf == 0) return false; @@ -774,8 +789,10 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Remove all services from the list of provided services. - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void clearServices() { if (DBG) Log.d(TAG, "clearServices()"); if (mService == null || mServerIf == 0) return; @@ -794,10 +811,9 @@ public final class BluetoothGattServer implements BluetoothProfile { *

          An application must call {@link #addService} to add a serice to the * list of services offered by this device. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return List of services. Returns an empty list if no services have been added yet. */ + @RequiresLegacyBluetoothPermission public List getServices() { return mServices; } @@ -809,12 +825,11 @@ public final class BluetoothGattServer implements BluetoothProfile { *

          If multiple instances of the same service (as identified by UUID) * exist, the first instance of the service is returned. * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param uuid UUID of the requested service * @return BluetoothGattService if supported, or null if the requested service is not offered by * this device. */ + @RequiresLegacyBluetoothPermission public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { if (service.getUuid().equals(uuid)) { diff --git a/framework/java/android/bluetooth/BluetoothGattService.java b/framework/java/android/bluetooth/BluetoothGattService.java index 23dc7c83085..f64d09fc30d 100644 --- a/framework/java/android/bluetooth/BluetoothGattService.java +++ b/framework/java/android/bluetooth/BluetoothGattService.java @@ -15,6 +15,9 @@ */ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -98,7 +101,6 @@ public class BluetoothGattService implements Parcelable { /** * Create a new BluetoothGattService. - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param uuid The UUID for this service * @param serviceType The type of this service, @@ -225,11 +227,11 @@ public class BluetoothGattService implements Parcelable { /** * Add an included service to this service. - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param service The service to be added * @return true, if the included service was added to the service */ + @RequiresLegacyBluetoothPermission public boolean addService(BluetoothGattService service) { mIncludedServices.add(service); return true; @@ -237,11 +239,11 @@ public class BluetoothGattService implements Parcelable { /** * Add a characteristic to this service. - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param characteristic The characteristics to be added * @return true, if the characteristic was added to the service */ + @RequiresLegacyBluetoothPermission public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) { mCharacteristics.add(characteristic); characteristic.setService(this); diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 632572dea3c..84e8c5134e7 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -22,6 +22,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -70,10 +73,10 @@ public final class BluetoothHeadset implements BluetoothProfile { *

          {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; @@ -90,10 +93,10 @@ public final class BluetoothHeadset implements BluetoothProfile { *

        *

        {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, - * - *

        Requires {@link android.Manifest.permission#BLUETOOTH} permission - * to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; @@ -107,11 +110,11 @@ public final class BluetoothHeadset implements BluetoothProfile { * be null if no device is active.

      • *
      * - *

      Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = @@ -147,9 +150,10 @@ public final class BluetoothHeadset implements BluetoothProfile { *

    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
    • *
    • EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
    • *
    - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission - * to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; @@ -299,10 +303,12 @@ public final class BluetoothHeadset implements BluetoothProfile { * are given an assigned number. Below shows the assigned number of Indicator added so far * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; @@ -432,15 +438,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * the state. Users can get the connection state of the profile * from this intent. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -474,15 +482,14 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #STATE_DISCONNECTING} can be used to distinguish between the * two scenarios. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadset service = mService; @@ -502,6 +509,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHeadset service = mService; @@ -521,6 +529,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHeadset service = mService; @@ -540,6 +549,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); final IBluetoothHeadset service = mService; @@ -571,7 +581,12 @@ public final class BluetoothHeadset implements BluetoothProfile { */ @Deprecated @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); final IBluetoothHeadset service = mService; @@ -605,7 +620,11 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -638,7 +657,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); final IBluetoothHeadset service = mService; @@ -688,7 +709,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * @param device Bluetooth device * @return true if echo cancellation and/or noise reduction is supported, false otherwise */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) { if (DBG) log("isNoiseReductionSupported()"); final IBluetoothHeadset service = mService; @@ -709,7 +732,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * @param device Bluetooth device * @return true if voice recognition is supported, false otherwise */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) { if (DBG) log("isVoiceRecognitionSupported()"); final IBluetoothHeadset service = mService; @@ -738,13 +763,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} * in case of failure to establish the audio connection. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth headset * @return false if there is no headset connected, or the connected headset doesn't support * voice recognition, or voice recognition is already started, or audio channel is occupied, * or on error, true otherwise */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); final IBluetoothHeadset service = mService; @@ -767,12 +796,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * If this function returns true, this intent will be broadcasted with * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth headset * @return false if there is no headset connected, or voice recognition has not started, * or voice recognition has ended on this headset, or on error, true otherwise */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); final IBluetoothHeadset service = mService; @@ -790,11 +820,12 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Check if Bluetooth SCO audio is connected. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth headset * @return true if SCO is connected, false otherwise or on error */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); final IBluetoothHeadset service = mService; @@ -827,6 +858,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; @@ -853,6 +885,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAudioRouteAllowed(boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); final IBluetoothHeadset service = mService; @@ -874,6 +907,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getAudioRouteAllowed() { if (VDBG) log("getAudioRouteAllowed"); final IBluetoothHeadset service = mService; @@ -897,6 +931,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * False to use SCO audio in normal manner * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setForceScoAudio(boolean forced) { if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); final IBluetoothHeadset service = mService; @@ -915,12 +950,13 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Check if at least one headset's SCO audio is connected or connecting * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return true if at least one device's SCO audio is connected or connecting, false otherwise * or on error * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isAudioOn() { if (VDBG) log("isAudioOn()"); final IBluetoothHeadset service = mService; @@ -955,6 +991,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -982,6 +1019,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio() { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { @@ -1018,7 +1056,12 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); @@ -1048,7 +1091,12 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); @@ -1075,6 +1123,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; @@ -1095,6 +1147,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) public void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type) { final IBluetoothHeadset service = mService; @@ -1119,8 +1175,6 @@ public final class BluetoothHeadset implements BluetoothProfile { * *

    Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device Bluetooth headset. * @param command A vendor-specific command. * @param arg The argument that will be attached to the command. @@ -1128,6 +1182,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * vendor-specific unsolicited result code, or on error. {@code true} otherwise. * @throws IllegalArgumentException if {@code command} is {@code null}. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { if (DBG) { @@ -1164,15 +1221,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted * with the active device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device Remote Bluetooth Device, could be null if phone call audio should not be * streamed to a headset * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { @@ -1201,7 +1260,9 @@ public final class BluetoothHeadset implements BluetoothProfile { */ @UnsupportedAppUsage(trackingBug = 171933273) @Nullable - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getActiveDevice() { if (VDBG) { Log.d(TAG, "getActiveDevice"); @@ -1227,7 +1288,9 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if in-band ringing is enabled, false if in-band ringing is disabled * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isInbandRingingEnabled() { if (DBG) { log("isInbandRingingEnabled()"); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index e5b2a1e23cc..092130d0ce9 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -19,6 +19,8 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -447,6 +449,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -473,6 +476,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadsetClient service = @@ -495,6 +499,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return list of connected devices; empty list if nothing is connected. */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHeadsetClient service = @@ -519,6 +524,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * list if nothing matches the states */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHeadsetClient service = @@ -542,6 +548,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return the state of connection of the device */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); final IBluetoothHeadsetClient service = @@ -569,7 +576,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -587,7 +594,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -619,7 +626,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -636,7 +645,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient service = @@ -664,6 +675,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); final IBluetoothHeadsetClient service = @@ -688,6 +700,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { if (DBG) log("sendVendorSpecificCommand()"); @@ -715,6 +728,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); final IBluetoothHeadsetClient service = @@ -736,6 +750,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return list of calls; empty list if none call exists */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); final IBluetoothHeadsetClient service = @@ -757,6 +772,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return bundle of AG indicators; null if device is not in CONNECTED state */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); final IBluetoothHeadsetClient service = @@ -782,6 +798,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); final IBluetoothHeadsetClient service = @@ -804,6 +821,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); final IBluetoothHeadsetClient service = @@ -831,6 +849,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * supported.

    */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); final IBluetoothHeadsetClient service = @@ -862,6 +881,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not * supported.

    */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); final IBluetoothHeadsetClient service = @@ -891,6 +911,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not * supported.

    */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); final IBluetoothHeadsetClient service = @@ -919,6 +940,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); final IBluetoothHeadsetClient service = @@ -943,6 +965,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * successfully; {@link null} otherwise; upon completion HFP sends {@link * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise; */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); final IBluetoothHeadsetClient service = @@ -968,6 +991,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); final IBluetoothHeadsetClient service = @@ -1089,6 +1113,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio(BluetoothDevice device) { final IBluetoothHeadsetClient service = getService(); @@ -1114,6 +1139,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio(BluetoothDevice device) { final IBluetoothHeadsetClient service = getService(); @@ -1136,6 +1162,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return bundle of AG features; null if no service or AG not connected */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgFeatures(BluetoothDevice device) { final IBluetoothHeadsetClient service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothHealth.java b/framework/java/android/bluetooth/BluetoothHealth.java index 5fd60e00169..65f68a943e0 100644 --- a/framework/java/android/bluetooth/BluetoothHealth.java +++ b/framework/java/android/bluetooth/BluetoothHealth.java @@ -16,6 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -111,8 +115,6 @@ public final class BluetoothHealth implements BluetoothProfile { * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so * the callback is used to notify success or failure if the function returns true. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param name The friendly name associated with the application or configuration. * @param dataType The dataType of the Source role of Health Profile to which the sink wants to * connect to. @@ -126,6 +128,10 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean registerSinkAppConfiguration(String name, int dataType, BluetoothHealthCallback callback) { Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated"); @@ -136,8 +142,6 @@ public final class BluetoothHealth implements BluetoothProfile { * Unregister an application configuration that has been registered using * {@link #registerSinkAppConfiguration} * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param config The health app configuration * @return Success or failure. * @@ -147,6 +151,10 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated"); return false; @@ -157,8 +165,6 @@ public final class BluetoothHealth implements BluetoothProfile { * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device The remote Bluetooth device. * @param config The application configuration which has been registered using {@link * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } @@ -170,6 +176,10 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated"); @@ -181,8 +191,6 @@ public final class BluetoothHealth implements BluetoothProfile { * This is an asynchronous call. If this function returns true, the callback * associated with the application configuration will be called. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param device The remote Bluetooth device. * @param config The application configuration which has been registered using {@link * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } @@ -195,6 +203,10 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated"); @@ -205,8 +217,6 @@ public final class BluetoothHealth implements BluetoothProfile { * Get the file descriptor of the main channel associated with the remote device * and application configuration. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * *

    Its the responsibility of the caller to close the ParcelFileDescriptor * when done. * @@ -220,6 +230,10 @@ public final class BluetoothHealth implements BluetoothProfile { * {@link BluetoothDevice#createL2capChannel(int)} */ @Deprecated + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated"); @@ -229,8 +243,6 @@ public final class BluetoothHealth implements BluetoothProfile { /** * Get the current connection state of the profile. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * This is not specific to any application configuration but represents the connection * state of the local Bluetooth adapter with the remote device. This can be used * by applications like status bar which would just like to know the state of the @@ -241,6 +253,10 @@ public final class BluetoothHealth implements BluetoothProfile { * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ @Override + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public int getConnectionState(BluetoothDevice device) { Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated"); return STATE_DISCONNECTED; @@ -251,8 +267,6 @@ public final class BluetoothHealth implements BluetoothProfile { * *

    Return the set of devices which are in state {@link #STATE_CONNECTED} * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * This is not specific to any application configuration but represents the connection * state of the local Bluetooth adapter for this profile. This can be used * by applications like status bar which would just like to know the state of the @@ -261,6 +275,10 @@ public final class BluetoothHealth implements BluetoothProfile { * @return List of devices. The list will be empty on error. */ @Override + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public List getConnectedDevices() { Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated"); return new ArrayList<>(); @@ -273,8 +291,7 @@ public final class BluetoothHealth implements BluetoothProfile { *

    If none of the devices match any of the given states, * an empty list will be returned. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * This is not specific to any application configuration but represents the connection + *

    This is not specific to any application configuration but represents the connection * state of the local Bluetooth adapter for this profile. This can be used * by applications like status bar which would just like to know the state of the * local adapter. @@ -284,6 +301,10 @@ public final class BluetoothHealth implements BluetoothProfile { * @return List of devices. The list will be empty on error. */ @Override + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SuppressLint("AndroidFrameworkRequiresPermission") public List getDevicesMatchingConnectionStates(int[] states) { Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated"); return new ArrayList<>(); diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index ff78825e0f9..8ceeff53b13 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -22,6 +22,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -64,10 +67,10 @@ public final class BluetoothHearingAid implements BluetoothProfile { *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; @@ -81,11 +84,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * be null if no device is active. * * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ACTIVE_DEVICE_CHANGED = @@ -167,7 +170,10 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @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("connect(" + device + ")"); final IBluetoothHearingAid service = getService(); @@ -225,6 +231,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHearingAid service = getService(); @@ -244,6 +251,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); @@ -264,6 +272,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); @@ -295,14 +304,14 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted * with the active device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * * @param device the remote Bluetooth device. Could be null to clear * the active device and stop streaming audio to a Bluetooth device. * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); @@ -330,7 +339,9 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); final IBluetoothHearingAid service = getService(); @@ -357,7 +368,10 @@ public final class BluetoothHearingAid 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -376,7 +390,10 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -531,7 +548,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return SIDE_LEFT or SIDE_RIGHT * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission public int getDeviceSide(BluetoothDevice device) { if (VDBG) { log("getDeviceSide(" + device + ")"); @@ -557,7 +574,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return MODE_MONAURAL or MODE_BINAURAL * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission public int getDeviceMode(BluetoothDevice device) { if (VDBG) { log("getDeviceMode(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 2baa73822c9..c214d2b85ac 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -21,6 +21,8 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; @@ -56,9 +58,10 @@ public final class BluetoothHidDevice implements BluetoothProfile { *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link * #STATE_DISCONNECTING}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; @@ -436,6 +439,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { final IBluetoothHidDevice service = getService(); if (service != null) { @@ -453,6 +457,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { final IBluetoothHidDevice service = getService(); if (service != null) { @@ -470,6 +475,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { final IBluetoothHidDevice service = getService(); if (service != null) { @@ -508,6 +514,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * object is required. * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean registerApp( BluetoothHidDeviceAppSdpSettings sdp, BluetoothHidDeviceAppQosSettings inQos, @@ -553,6 +560,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean unregisterApp() { boolean result = false; @@ -578,6 +586,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; @@ -604,6 +613,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; @@ -628,6 +638,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; @@ -651,6 +662,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @return the current user name, or empty string if cannot get the name * {@hide} */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getUserAppName() { final IBluetoothHidDevice service = getService(); @@ -675,6 +687,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { boolean result = false; @@ -699,6 +712,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { boolean result = false; @@ -734,7 +748,10 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @hide */ @SystemApi - @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) { log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 9561d938384..70e3809cb59 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -21,6 +21,9 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; @@ -65,11 +68,11 @@ public final class BluetoothHidHost implements BluetoothProfile { *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ @SuppressLint("ActionValue") + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; @@ -328,7 +331,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost service = getService(); @@ -350,6 +353,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHidHost service = getService(); @@ -372,7 +376,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); if (device == null) { @@ -503,12 +507,13 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Initiate virtual unplug for a HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -529,12 +534,13 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Get_Protocol_Mode command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -553,12 +559,13 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Set_Protocol_Mode command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -577,8 +584,6 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Get_Report command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @param reportType Report type * @param reportId Report ID @@ -586,6 +591,9 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { if (VDBG) { @@ -608,14 +616,15 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Set_Report command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @param reportType Report type * @param report Report receiving buffer size * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); final IBluetoothHidHost service = getService(); @@ -634,13 +643,14 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Send_Data command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @param report Report to send * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); final IBluetoothHidHost service = getService(); @@ -659,12 +669,13 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Get_Idle_Time command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); final IBluetoothHidHost service = getService(); @@ -683,13 +694,14 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * Send Set_Idle_Time command to the connected HID input device. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. - * * @param device Remote Bluetooth Device * @param idleTime Idle time to be set on HID Device * @return false on immediate error, true otherwise * @hide */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); final IBluetoothHidHost service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 3f00fa6f418..4f095f6c700 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -23,6 +23,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -65,10 +68,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; @@ -82,11 +85,11 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * be null if no device is active. * * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; @@ -122,7 +125,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { /** * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void close() { mProfileConnector.disconnect(); } @@ -131,7 +133,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { return mProfileConnector.getService(); } - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); @@ -154,7 +155,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(@Nullable BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { @@ -193,7 +194,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(@Nullable BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { @@ -213,6 +214,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { @@ -232,6 +234,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * {@inheritDoc} */ @Override + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); @@ -252,7 +255,9 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * {@inheritDoc} */ @Override - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { @@ -289,7 +294,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -314,7 +319,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @hide */ @NonNull - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { @@ -337,7 +342,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return group id that this device currently belongs to * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission public int getGroupId(@NonNull BluetoothDevice device) { if (VDBG) log("getGroupId()"); try { @@ -365,7 +370,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -398,7 +406,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return connection policy of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); try { diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index d5c1c3e2d61..a1e1b630508 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -20,6 +20,8 @@ import android.Manifest; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; import android.content.pm.PackageManager; import android.os.IBinder; @@ -109,7 +111,9 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device, int profile) { if (DBG) Log.d(TAG, "getConnectionState()"); @@ -136,7 +140,9 @@ public final class BluetoothManager { * @param profile GATT or GATT_SERVER * @return List of devices. The list will be empty on error. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices(int profile) { if (DBG) Log.d(TAG, "getConnectedDevices"); if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { @@ -177,7 +183,9 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int profile, int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates"); @@ -210,6 +218,7 @@ public final class BluetoothManager { * @param callback GATT server callback handler that will receive asynchronous callbacks. * @return BluetoothGattServer instance */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) { @@ -229,6 +238,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, boolean eatt_support) { return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support)); @@ -249,6 +259,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport) { return (openGattServer(context, callback, transport, false)); @@ -270,6 +281,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport, boolean eatt_support) { if (context == null || callback == null) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 35549954007..3e7b75aa1f6 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -93,7 +93,6 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { mCloseGuard.open("close"); } - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); @@ -110,7 +109,6 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); @@ -128,6 +126,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getState() { if (VDBG) log("getState()"); final IBluetoothMap service = getService(); @@ -152,6 +151,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getClient() { if (VDBG) log("getClient()"); final IBluetoothMap service = getService(); @@ -175,6 +175,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); final IBluetoothMap service = getService(); @@ -211,6 +212,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap service = getService(); @@ -257,7 +259,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap service = getService(); @@ -280,6 +285,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); final IBluetoothMap service = getService(); @@ -302,6 +308,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); final IBluetoothMap service = getService(); @@ -328,7 +335,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -347,7 +357,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -378,7 +391,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -396,7 +412,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi - @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("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 0312a2190a4..db74a90f603 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(); diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ecd718cec32..b3924b1fa92 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -22,6 +22,8 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -74,10 +76,11 @@ public final class BluetoothPan implements BluetoothProfile { * *

    {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or * {@link #LOCAL_PANU_ROLE} - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ @SuppressLint("ActionValue") + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; @@ -102,9 +105,10 @@ public final class BluetoothPan implements BluetoothProfile { * *

    {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or * {@link #TETHERING_STATE_ON} - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED"; @@ -236,6 +240,10 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan service = getService(); @@ -274,6 +282,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothPan service = getService(); @@ -302,7 +311,10 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -330,7 +342,10 @@ public final class BluetoothPan implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothPan service = getService(); @@ -351,7 +366,12 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @Override - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothPan service = getService(); @@ -396,7 +416,11 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.TETHER_PRIVILEGED, + }) public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -417,7 +441,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 6e5c45f3d12..6c2e5bf2d39 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -22,6 +22,8 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -82,8 +84,6 @@ public class BluetoothPbap implements BluetoothProfile { * can be any of {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING}. - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. * * @hide */ @@ -142,6 +142,7 @@ public class BluetoothPbap implements BluetoothProfile { doBind(); } + @SuppressLint("AndroidFrameworkRequiresPermission") boolean doBind() { synchronized (mConnection) { try { @@ -216,6 +217,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { log("getConnectedDevices()"); final IBluetoothPbap service = mService; @@ -262,6 +264,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); final IBluetoothPbap service = mService; @@ -294,7 +297,10 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @SystemApi - @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("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -324,6 +330,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); final IBluetoothPbap service = mService; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index f356da18fc7..2c8fbc2509f 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -19,6 +19,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -160,6 +161,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return list of connected devices */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) { log("getConnectedDevices()"); @@ -185,6 +187,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return list of matching devices */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) { log("getDevicesMatchingStates()"); @@ -210,6 +213,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return device connection state */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) { log("getConnectionState(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 201d6c495d9..70053ee7149 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -14,12 +14,9 @@ * limitations under the License. */ - package android.bluetooth; -import android.Manifest; import android.annotation.IntDef; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -300,7 +297,6 @@ public interface BluetoothProfile { * * @return List of devices. The list will be empty on error. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) public List getConnectedDevices(); /** @@ -314,7 +310,6 @@ public interface BluetoothProfile { * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ - @RequiresPermission(Manifest.permission.BLUETOOTH) public List getDevicesMatchingConnectionStates(int[] states); /** @@ -324,7 +319,6 @@ public interface BluetoothProfile { * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ - @RequiresPermission(Manifest.permission.BLUETOOTH) @BtProfileState int getConnectionState(BluetoothDevice device); /** diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 863fd3698cb..12abcc4d11d 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -78,6 +80,7 @@ public abstract class BluetoothProfileConnector { mServiceName = serviceName; } + @SuppressLint("AndroidFrameworkRequiresPermission") private boolean doBind() { synchronized (mConnection) { if (mService == null) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 0d70dbdd842..c85494c01b2 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -18,6 +18,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -61,11 +64,11 @@ public final class BluetoothSap implements BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; @@ -140,6 +143,7 @@ public final class BluetoothSap implements BluetoothProfile { * connected to the Sap service. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getState() { if (VDBG) log("getState()"); final IBluetoothSap service = getService(); @@ -163,6 +167,7 @@ public final class BluetoothSap implements BluetoothProfile { * this proxy object is not connected to the Sap service. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getClient() { if (VDBG) log("getClient()"); final IBluetoothSap service = getService(); @@ -186,6 +191,7 @@ public final class BluetoothSap implements BluetoothProfile { * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); final IBluetoothSap service = getService(); @@ -221,6 +227,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap service = getService(); @@ -242,6 +249,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return list of connected devices * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothSap service = getService(); @@ -263,6 +271,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return list of matching devices * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); final IBluetoothSap service = getService(); @@ -284,6 +293,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return device connection state * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); final IBluetoothSap service = getService(); @@ -310,7 +320,10 @@ public final class BluetoothSap 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("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -328,7 +341,10 @@ public final class BluetoothSap 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(BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -359,7 +375,10 @@ public final class BluetoothSap 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("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -376,7 +395,10 @@ public final class BluetoothSap 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(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 5c1bcaf3131..50822354d69 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -62,9 +62,6 @@ import java.io.IOException; * safe. In particular, {@link #close} will always immediately abort ongoing * operations and close the server socket. * - *

    Note: - * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. - * *

    *

    Developer Guides

    *

    For more information about using Bluetooth, read the diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 65381dbb237..ef88147a40f 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.Build; @@ -70,9 +72,6 @@ import java.util.UUID; * safe. In particular, {@link #close} will always immediately abort ongoing * operations and close the socket. * - *

    Note: - * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. - * *

    *

    Developer Guides

    *

    For more information about using Bluetooth, read the @@ -199,6 +198,7 @@ public final class BluetoothSocket implements Closeable { * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges */ + @SuppressLint("AndroidFrameworkRequiresPermission") /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) throws IOException { @@ -386,6 +386,7 @@ public final class BluetoothSocket implements Closeable { * * @throws IOException on error, for example connection failure */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void connect() throws IOException { if (mDevice == null) throw new IOException("Connect is called on null device"); @@ -427,6 +428,7 @@ public final class BluetoothSocket implements Closeable { * Currently returns unix errno instead of throwing IOException, * so that BluetoothAdapter can check the error code for EADDRINUSE */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; @@ -682,6 +684,7 @@ public final class BluetoothSocket implements Closeable { * connection. This function is currently used for testing only. * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void requestMaximumTxDataLength() throws IOException { if (mDevice == null) { throw new IOException("requestMaximumTxDataLength is called on null device"); diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java new file mode 100644 index 00000000000..c508c2c9ca0 --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, + * this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE} + * permission which can be gained with + * {@link android.app.Activity#requestPermissions(String[], int)}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresBluetoothAdvertisePermission { +} diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java new file mode 100644 index 00000000000..e159eaafe2e --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, + * this requires the {@link Manifest.permission#BLUETOOTH_CONNECT} + * permission which can be gained with + * {@link android.app.Activity#requestPermissions(String[], int)}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresBluetoothConnectPermission { +} diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java new file mode 100644 index 00000000000..2bb32041394 --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc In addition, this requires either the + * {@link Manifest.permission#ACCESS_FINE_LOCATION} + * permission or a strong assertion that you will never derive the + * physical location of the device. You can make this assertion by + * declaring {@code usesPermissionFlags="neverForLocation"} on the + * relevant {@code } manifest tag, but it may + * restrict the types of Bluetooth devices you can interact with. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresBluetoothLocationPermission { +} diff --git a/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java new file mode 100644 index 00000000000..800ff39933f --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, + * this requires the {@link Manifest.permission#BLUETOOTH_SCAN} + * permission which can be gained with + * {@link android.app.Activity#requestPermissions(String[], int)}. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresBluetoothScanPermission { +} diff --git a/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java new file mode 100644 index 00000000000..9adf695cde0 --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this + * requires the {@link Manifest.permission#BLUETOOTH_ADMIN} + * permission which can be gained with a simple + * {@code } manifest tag. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresLegacyBluetoothAdminPermission { +} diff --git a/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java new file mode 100644 index 00000000000..79621c366f5 --- /dev/null +++ b/framework/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.Manifest; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this + * requires the {@link Manifest.permission#BLUETOOTH} permission + * which can be gained with a simple {@code } + * manifest tag. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, FIELD}) +public @interface RequiresLegacyBluetoothPermission { +} diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 1df35e1e382..54a18e6f1d6 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -16,9 +16,12 @@ package android.bluetooth.le; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.os.RemoteException; import android.util.Log; @@ -27,9 +30,6 @@ import android.util.Log; *

    * To get an instance of {@link AdvertisingSet}, call the * {@link BluetoothLeAdvertiser#startAdvertisingSet} method. - *

    - * Note: Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @see AdvertiseData */ @@ -58,8 +58,6 @@ public final class AdvertisingSet { /** * Enables Advertising. This method returns immediately, the operation status is * delivered through {@code callback.onAdvertisingEnabled()}. - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param enable whether the advertising should be enabled (true), or disabled (false) * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 @@ -68,6 +66,9 @@ public final class AdvertisingSet { * controller shall attempt to send prior to terminating the extended advertising, even if the * duration has not expired. Valid range is from 1 to 255. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void enableAdvertising(boolean enable, int duration, int maxExtendedAdvertisingEvents) { try { @@ -90,6 +91,9 @@ public final class AdvertisingSet { * three bytes will be added for flags. If the update takes place when the advertising set is * enabled, the data can be maximum 251 bytes long. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingData(AdvertiseData advertiseData) { try { mGatt.setAdvertisingData(mAdvertiserId, advertiseData); @@ -107,6 +111,9 @@ public final class AdvertisingSet { * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place * when the advertising set is enabled, the data can be maximum 251 bytes long. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setScanResponseData(AdvertiseData scanResponse) { try { mGatt.setScanResponseData(mAdvertiserId, scanResponse); @@ -122,6 +129,9 @@ public final class AdvertisingSet { * * @param parameters advertising set parameters. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { mGatt.setAdvertisingParameters(mAdvertiserId, parameters); @@ -135,6 +145,9 @@ public final class AdvertisingSet { * periodic advertising is not enabled. This method returns immediately, the operation * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { try { mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters); @@ -153,6 +166,9 @@ public final class AdvertisingSet { * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the * periodic advertising is enabled for this set, the data can be maximum 251 bytes long. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData); @@ -168,6 +184,9 @@ public final class AdvertisingSet { * @param enable whether the periodic advertising should be enabled (true), or disabled * (false). */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingEnabled(boolean enable) { try { mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable); @@ -181,10 +200,9 @@ public final class AdvertisingSet { * This method is exposed only for Bluetooth PTS tests, no app or system service * should ever use it. * - * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. - * * @hide */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void getOwnAddress() { try { mGatt.getOwnAddress(mAdvertiserId); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 5f166f4a41d..de11869e220 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -16,11 +16,15 @@ package android.bluetooth.le; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -38,9 +42,6 @@ import java.util.Map; *

    * To get an instance of {@link BluetoothLeAdvertiser}, call the * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. - *

    - * Note: Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. * * @see AdvertiseData */ @@ -81,13 +82,17 @@ public final class BluetoothLeAdvertiser { /** * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted. * Returns immediately, the operation status is delivered through {@code callback}. - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param settings Settings for Bluetooth LE advertising. * @param advertiseData Advertisement data to be broadcasted. * @param callback Callback for advertising status. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, final AdvertiseCallback callback) { startAdvertising(settings, advertiseData, null, callback); @@ -98,14 +103,18 @@ public final class BluetoothLeAdvertiser { * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an * active scan request. This method returns immediately, the operation status is delivered * through {@code callback}. - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param settings Settings for Bluetooth LE advertising. * @param advertiseData Advertisement data to be advertised in advertisement packet. * @param scanResponse Scan response associated with the advertisement data. * @param callback Callback for advertising status. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { @@ -160,9 +169,11 @@ public final class BluetoothLeAdvertiser { } } + @SuppressLint("AndroidFrameworkRequiresPermission") AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) { return new AdvertisingSetCallback() { @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { @@ -175,6 +186,7 @@ public final class BluetoothLeAdvertiser { /* Legacy advertiser is disabled on timeout */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, int status) { if (enabled) { @@ -192,11 +204,12 @@ public final class BluetoothLeAdvertiser { /** * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in * {@link BluetoothLeAdvertiser#startAdvertising}. - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void stopAdvertising(final AdvertiseCallback callback) { synchronized (mLegacyAdvertisers) { if (callback == null) { @@ -232,6 +245,12 @@ public final class BluetoothLeAdvertiser { * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising * feature is made when it's not supported by the controller. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -262,6 +281,12 @@ public final class BluetoothLeAdvertiser { * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising * feature is made when it's not supported by the controller. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -297,6 +322,12 @@ public final class BluetoothLeAdvertiser { * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising * feature is made when it's not supported by the controller. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -337,6 +368,12 @@ public final class BluetoothLeAdvertiser { * maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended * Advertising */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT, + }) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -445,6 +482,9 @@ public final class BluetoothLeAdvertiser { * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link * BluetoothLeAdvertiser#startAdvertisingSet}. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void stopAdvertisingSet(AdvertisingSetCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -476,6 +516,7 @@ public final class BluetoothLeAdvertiser { } // Compute the size of advertisement data or scan resp + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { if (data == null) return 0; // Flags field is omitted if the advertising is not connectable. diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2601cd4300e..4271a905c22 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -20,12 +20,16 @@ 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.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; @@ -45,9 +49,6 @@ import java.util.Map; *

    * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of * {@link BluetoothLeScanner}. - *

    - * Note: Most of the scan methods here require - * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @see ScanFilter */ @@ -117,7 +118,10 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code callback} is null. */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startScan(final ScanCallback callback) { startScan(null, new ScanSettings.Builder().build(), callback); } @@ -139,7 +143,10 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); @@ -168,7 +175,10 @@ public final class BluetoothLeScanner { * could not be sent. * @see #stopScan(PendingIntent) */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int startScan(@Nullable List filters, @Nullable ScanSettings settings, @NonNull PendingIntent callbackIntent) { return startScan(filters, @@ -186,8 +196,13 @@ public final class BluetoothLeScanner { * @hide */ @SystemApi + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission @RequiresPermission(allOf = { - Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) + android.Manifest.permission.BLUETOOTH_SCAN, + android.Manifest.permission.UPDATE_DEVICE_STATS + }) public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); } @@ -204,13 +219,20 @@ public final class BluetoothLeScanner { * @hide */ @SystemApi + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission @RequiresPermission(allOf = { - Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) + android.Manifest.permission.BLUETOOTH_SCAN, + android.Manifest.permission.UPDATE_DEVICE_STATS + }) + @SuppressLint("AndroidFrameworkRequiresPermission") public void startScanFromSource(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback) { startScan(filters, settings, workSource, callback, null, null); } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) private int startScan(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, final PendingIntent callbackIntent, @@ -268,7 +290,9 @@ public final class BluetoothLeScanner { * * @param callback */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopScan(ScanCallback callback) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); synchronized (mLeScanClients) { @@ -289,7 +313,9 @@ public final class BluetoothLeScanner { * @param callbackIntent The PendingIntent that was used to start the scan. * @see #startScan(List, ScanSettings, PendingIntent) */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopScan(PendingIntent callbackIntent) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); IBluetoothGatt gatt; @@ -308,6 +334,9 @@ public final class BluetoothLeScanner { * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one * used to start scan. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void flushPendingScanResults(ScanCallback callback) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null) { @@ -328,6 +357,7 @@ public final class BluetoothLeScanner { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(List truncatedFilters, ScanSettings settings, final ScanCallback callback) { int filterSize = truncatedFilters.size(); @@ -382,6 +412,8 @@ public final class BluetoothLeScanner { mResultStorages = resultStorages; } + @SuppressLint("AndroidFrameworkRequiresPermission") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startRegistration() { synchronized (this) { // Scan stopped. @@ -409,6 +441,8 @@ public final class BluetoothLeScanner { } } + @SuppressLint("AndroidFrameworkRequiresPermission") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan() { synchronized (this) { if (mScannerId <= 0) { @@ -425,6 +459,8 @@ public final class BluetoothLeScanner { } } + @SuppressLint("AndroidFrameworkRequiresPermission") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void flushPendingBatchResults() { synchronized (this) { if (mScannerId <= 0) { @@ -443,6 +479,7 @@ public final class BluetoothLeScanner { * Application interface registered - app is ready to go */ @Override + @SuppressLint("AndroidFrameworkRequiresPermission") public void onScannerRegistered(int status, int scannerId) { Log.d(TAG, "onScannerRegistered() - status=" + status + " scannerId=" + scannerId + " mScannerId=" + mScannerId); @@ -595,6 +632,7 @@ public final class BluetoothLeScanner { return true; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { final int callbackType = settings.getCallbackType(); if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 0f1a8e913ba..9ea6c4866f6 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -16,10 +16,14 @@ package android.bluetooth.le; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; +import android.bluetooth.annotations.RequiresBluetoothLocationPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -35,9 +39,6 @@ import java.util.Map; *

    * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an * instance of {@link PeriodicAdvertisingManager}. - *

    - * Note: Most of the methods here require - * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. * * @hide */ @@ -89,6 +90,10 @@ public final class PeriodicAdvertisingManager { * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or * {@code timeout} is invalid or {@code callback} is null. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback) { registerSync(scanResult, skip, timeout, callback, null); @@ -113,6 +118,10 @@ public final class PeriodicAdvertisingManager { * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or * {@code timeout} is invalid or {@code callback} is null. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresBluetoothLocationPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void registerSync(ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback, Handler handler) { if (callback == null) { @@ -170,6 +179,9 @@ public final class PeriodicAdvertisingManager { * @throws IllegalArgumentException if {@code callback} is null, or not a properly registered * callback. */ + @RequiresLegacyBluetoothAdminPermission + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void unregisterSync(PeriodicAdvertisingCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback can't be null"); diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index aa56da5773e..197321f1cb6 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -16,6 +16,7 @@ package com.android.server; +import android.annotation.RequiresPermission; import android.content.Context; import android.database.ContentObserver; import android.os.Handler; @@ -106,6 +107,7 @@ class BluetoothAirplaneModeListener { } @VisibleForTesting + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) void handleAirplaneModeChange() { if (shouldSkipAirplaneModeChange()) { Log.i(TAG, "Ignore airplane mode change"); diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 09cfac00567..feed2205dc4 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -23,6 +23,8 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -304,6 +306,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); + final long token = Binder.clearCallingIdentity(); + try { + return onFactoryResetInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + private boolean onFactoryResetInternal() { // Wait for stable state if bluetooth is temporary state. int state = getState(); if (state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -343,6 +358,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void onAirplaneModeChanged() { synchronized (this) { if (isBluetoothPersistedStateOn()) { @@ -707,9 +723,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { - if (!checkConnectPermissionForPreflight(mContext)) { - return; - } if (callback == null) { Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); return; @@ -720,9 +733,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { - if (!checkConnectPermissionForPreflight(mContext)) { - return; - } if (callback == null) { Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); return; @@ -935,6 +945,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return appCount; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) { if (isBluetoothDisallowed()) { if (DBG) { @@ -990,6 +1001,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disableBle(String packageName, IBinder token) throws RemoteException { if (!checkBluetoothPermissions(packageName, false)) { if (DBG) { @@ -1040,6 +1052,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on, * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) private void continueFromBleOnState() { if (DBG) { Slog.d(TAG, "continueFromBleOnState()"); @@ -1072,6 +1085,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Inform BluetoothAdapter instances that BREDR part is down * and turn off all service and stack if no LE app needs it */ + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) private void sendBrEdrDownCallback() { if (DBG) { Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks"); @@ -1265,12 +1282,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * * @hide */ + @SuppressLint("AndroidFrameworkRequiresPermission") private boolean checkBluetoothPermissionWhenWirelessConsentRequired() { int result = mContext.checkCallingPermission( android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED); return result == PackageManager.PERMISSION_GRANTED; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void unbindAndFinish() { if (DBG) { Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding @@ -2300,6 +2319,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED + }) private void restartForReason(int reason) { try { mBluetoothLock.readLock().lock(); @@ -2376,6 +2399,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void handleEnable(boolean quietMode) { mQuietEnable = quietMode; @@ -2418,6 +2442,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void handleDisable() { try { mBluetoothLock.readLock().lock(); @@ -2475,6 +2500,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); } + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) private void bluetoothStateChangeHandler(int prevState, int newState) { boolean isStandardBroadcast = true; if (prevState == newState) { // No change. Nothing to do. @@ -2615,6 +2644,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) private void recoverBluetoothServiceFromError(boolean clearBle) { Slog.e(TAG, "recoverBluetoothServiceFromError"); try { @@ -2848,6 +2881,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * *

    Should be used in situations where the app op should not be noted. */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private static boolean checkConnectPermissionForPreflight(Context context) { int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight( context, BLUETOOTH_CONNECT); diff --git a/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java index 242fa848c25..3642e4dccf3 100644 --- a/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java +++ b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java @@ -16,6 +16,7 @@ package com.android.server; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; @@ -101,6 +102,7 @@ public class BluetoothModeChangeHelper { } @VisibleForTesting + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void onAirplaneModeChanged(BluetoothManagerService managerService) { managerService.onAirplaneModeChanged(); } -- GitLab From 28253294ce8948cde84ce1e883b93ce1c75ac494 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Wed, 14 Apr 2021 11:45:35 -0700 Subject: [PATCH 1262/1408] OOB generateLocalOobData unhide @SystemApi callback methods Bug: 178007935 Tag: #feature Test: compiles Change-Id: I2d4167a6c92ee0cc24da12df206838161c8f3318 Merged-In: I2d4167a6c92ee0cc24da12df206838161c8f3318 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 7d62327738d..3802289dd6d 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3062,8 +3062,6 @@ public final class BluetoothAdapter { * * @param transport - whether the {@link OobData} is generated for LE or Classic. * @param oobData - data generated in the host stack(LE) or controller (Classic) - * - * @hide */ void onOobData(@Transport int transport, @Nullable OobData oobData); @@ -3071,8 +3069,6 @@ public final class BluetoothAdapter { * Provides feedback when things don't go as expected. * * @param errorCode - the code descibing the type of error that occurred. - * - * @hide */ void onError(@OobError int errorCode); } -- GitLab From 36582c19d128599272825eea29793dc475f6c9fd Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 15 Apr 2021 08:31:42 -0600 Subject: [PATCH 1263/1408] Refine BluetoothLeAdvertiser permissions. Technically these APIs required both ADVERTISE and CONNECT, since internally it would attempt getting the device name as part of calculating packet lengths. These methods shouldn't require the CONNECT permission, so we add a getNameLengthForAdvertise() method internally to remove this dependency. Bug: 183626724 Test: ./build/soong/soong_ui.bash --make-mode Bluetooth RUN_ERROR_PRONE=true Change-Id: I245417bfc26d6d3a4f8be14077c7f1d271b5959e --- .../android/bluetooth/BluetoothAdapter.java | 12 ++++++ .../bluetooth/le/BluetoothLeAdvertiser.java | 39 ++++++------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 972e9e6d73b..df2c512ebf6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1251,6 +1251,18 @@ public final class BluetoothAdapter { return null; } + /** {@hide} */ + @RequiresBluetoothAdvertisePermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) + public int getNameLengthForAdvertise() { + try { + return mService.getNameLengthForAdvertise(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return -1; + } + /** * Factory reset bluetooth settings. * diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index de11869e220..710fa6827b0 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -89,10 +89,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, final AdvertiseCallback callback) { startAdvertising(settings, advertiseData, null, callback); @@ -111,10 +108,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertising(AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseData scanResponse, final AdvertiseCallback callback) { @@ -247,10 +241,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -283,10 +274,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -324,10 +312,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -370,10 +355,7 @@ public final class BluetoothLeAdvertiser { */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_CONNECT, - }) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData, AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters, @@ -516,7 +498,7 @@ public final class BluetoothLeAdvertiser { } // Compute the size of advertisement data or scan resp - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { if (data == null) return 0; // Flags field is omitted if the advertising is not connectable. @@ -587,8 +569,11 @@ public final class BluetoothLeAdvertiser { if (data.getIncludeTxPowerLevel()) { size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. } - if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) { - size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length(); + if (data.getIncludeDeviceName()) { + final int length = mBluetoothAdapter.getNameLengthForAdvertise(); + if (length >= 0) { + size += OVERHEAD_BYTES_PER_FIELD + length; + } } return size; } -- GitLab From 5ba8bfca7e9adf5c6d8ee8180aebad6f04037d6c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 16 Apr 2021 09:53:23 -0600 Subject: [PATCH 1264/1408] 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/BluetoothA2dp.java | 6 ++ .../android/bluetooth/BluetoothA2dpSink.java | 2 + .../android/bluetooth/BluetoothAdapter.java | 56 ++++++++++++++++++- .../java/android/bluetooth/BluetoothGatt.java | 23 ++++++-- .../bluetooth/BluetoothGattServer.java | 12 ++++ .../android/bluetooth/BluetoothHeadset.java | 16 ++++++ .../bluetooth/BluetoothHeadsetClient.java | 24 +++++++- .../bluetooth/BluetoothHearingAid.java | 7 +++ .../android/bluetooth/BluetoothHidDevice.java | 13 +++++ .../android/bluetooth/BluetoothHidHost.java | 3 + .../bluetooth/BluetoothInputStream.java | 3 + .../android/bluetooth/BluetoothLeAudio.java | 7 +++ .../android/bluetooth/BluetoothManager.java | 7 +++ .../java/android/bluetooth/BluetoothMap.java | 14 +++++ .../android/bluetooth/BluetoothMapClient.java | 17 ++++++ .../bluetooth/BluetoothOutputStream.java | 3 + .../java/android/bluetooth/BluetoothPan.java | 6 ++ .../java/android/bluetooth/BluetoothPbap.java | 6 ++ .../bluetooth/BluetoothPbapClient.java | 4 ++ .../android/bluetooth/BluetoothProfile.java | 3 + .../bluetooth/BluetoothProfileConnector.java | 2 +- .../java/android/bluetooth/BluetoothSap.java | 13 +++++ .../bluetooth/BluetoothServerSocket.java | 2 + .../android/bluetooth/BluetoothSocket.java | 13 ++++- .../java/android/bluetooth/BluetoothUuid.java | 2 + .../android/bluetooth/le/AdvertisingSet.java | 3 + .../bluetooth/le/BluetoothLeAdvertiser.java | 14 ++++- .../bluetooth/le/BluetoothLeScanner.java | 12 ++-- .../le/PeriodicAdvertisingManager.java | 2 + .../java/android/bluetooth/le/ScanFilter.java | 1 - .../java/android/bluetooth/le/ScanRecord.java | 2 + .../android/bluetooth/le/TruncatedFilter.java | 2 + 32 files changed, 278 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index a268e168ae7..0d21e09293b 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; @@ -512,6 +514,7 @@ public final class BluetoothA2dp 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, @@ -534,6 +537,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -624,6 +628,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return true if device supports absolute volume * @hide */ + @RequiresNoPermission public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); try { @@ -690,6 +695,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean shouldSendVolumeKeys(BluetoothDevice device) { if (isEnabled() && isValidDevice(device)) { diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index d81316e357d..6aba483b151 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -282,6 +282,7 @@ public final class BluetoothA2dpSink 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, @@ -304,6 +305,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index df2c512ebf6..052a7733c22 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -716,6 +717,7 @@ public final class BluetoothAdapter { * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. */ + @SuppressLint("AndroidFrameworkBluetoothPermission") private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override @@ -747,6 +749,7 @@ public final class BluetoothAdapter { * @return the default local adapter, or null if Bluetooth is not supported on this hardware * platform */ + @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); @@ -792,6 +795,7 @@ public final class BluetoothAdapter { * @param address valid Bluetooth MAC address * @throws IllegalArgumentException if address is invalid */ + @RequiresNoPermission public BluetoothDevice getRemoteDevice(String address) { return new BluetoothDevice(address); } @@ -807,6 +811,7 @@ public final class BluetoothAdapter { * @param address Bluetooth MAC address (6 bytes) * @throws IllegalArgumentException if address is invalid */ + @RequiresNoPermission public BluetoothDevice getRemoteDevice(byte[] address) { if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); @@ -824,6 +829,7 @@ public final class BluetoothAdapter { * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported * on this device before calling this method. */ + @RequiresNoPermission public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { if (!getLeAccess()) { return null; @@ -846,6 +852,7 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresNoPermission public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { if (!getLeAccess()) { return null; @@ -866,6 +873,7 @@ public final class BluetoothAdapter { /** * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. */ + @RequiresNoPermission public BluetoothLeScanner getBluetoothLeScanner() { if (!getLeAccess()) { return null; @@ -887,6 +895,7 @@ public final class BluetoothAdapter { * @return true if the local adapter is turned on */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isEnabled() { return getState() == BluetoothAdapter.STATE_ON; } @@ -900,6 +909,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isLeEnabled() { final int state = getLeState(); if (DBG) { @@ -937,6 +947,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE() { if (!isBleScanAlwaysAvailable()) { @@ -983,6 +994,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE() { if (!isBleScanAlwaysAvailable()) { @@ -1015,6 +1027,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableBluetoothGetStateCache() { mBluetoothGetStateCache.disableLocal(); } @@ -1059,6 +1072,7 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission @AdapterState public int getState() { int state = getStateInternal(); @@ -1095,6 +1109,7 @@ public final class BluetoothAdapter { * @hide */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission @AdapterState @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + "whether you can use BLE & BT classic.") @@ -1649,6 +1664,7 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresBluetoothScanPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getDiscoverableTimeout() { if (getState() != STATE_ON) { @@ -1669,6 +1685,7 @@ public final class BluetoothAdapter { /** @hide */ @UnsupportedAppUsage + @RequiresBluetoothScanPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void setDiscoverableTimeout(int timeout) { if (getState() != STATE_ON) { @@ -1714,6 +1731,7 @@ public final class BluetoothAdapter { * Set the context for this BluetoothAdapter (only called from BluetoothManager) * @hide */ + @RequiresNoPermission public void setContext(Context context) { mContext = context; } @@ -1872,6 +1890,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -1916,6 +1935,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -1964,6 +1984,7 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -1996,6 +2017,7 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -2021,6 +2043,7 @@ public final class BluetoothAdapter { * @return true if Multiple Advertisement feature is supported */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isMultipleAdvertisementSupported() { if (getState() != STATE_ON) { return false; @@ -2049,6 +2072,7 @@ public final class BluetoothAdapter { * @hide */ @SystemApi + @RequiresNoPermission public boolean isBleScanAlwaysAvailable() { try { return mManagerService.isBleScanAlwaysAvailable(); @@ -2082,6 +2106,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableIsOffloadedFilteringSupportedCache() { mBluetoothFilteringCache.disableLocal(); } @@ -2097,6 +2122,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isOffloadedFilteringSupported() { if (!getLeAccess()) { return false; @@ -2110,6 +2136,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isOffloadedScanBatchingSupported() { if (!getLeAccess()) { return false; @@ -2133,6 +2160,7 @@ public final class BluetoothAdapter { * @return true if chipset supports LE 2M PHY feature */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLe2MPhySupported() { if (!getLeAccess()) { return false; @@ -2156,6 +2184,7 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Coded PHY feature */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLeCodedPhySupported() { if (!getLeAccess()) { return false; @@ -2179,6 +2208,7 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Extended Advertising feature */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLeExtendedAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2202,6 +2232,7 @@ public final class BluetoothAdapter { * @return true if chipset supports LE Periodic Advertising feature */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public boolean isLePeriodicAdvertisingSupported() { if (!getLeAccess()) { return false; @@ -2226,6 +2257,7 @@ public final class BluetoothAdapter { * @return the maximum LE advertising data length. */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getLeMaximumAdvertisingDataLength() { if (!getLeAccess()) { return 0; @@ -2248,6 +2280,7 @@ public final class BluetoothAdapter { * * @return true if phone supports Hearing Aid Profile */ + @RequiresNoPermission private boolean isHearingAidProfileSupported() { try { return mManagerService.isHearingAidProfileSupported(); @@ -2286,6 +2319,7 @@ public final class BluetoothAdapter { * @return true if there are hw entries available for matching beacons * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isHardwareTrackingFiltersAvailable() { if (!getLeAccess()) { @@ -2432,6 +2466,7 @@ public final class BluetoothAdapter { * BluetoothProfile}. * @hide */ + @RequiresNoPermission public @NonNull List getSupportedProfiles() { final ArrayList supportedProfiles = new ArrayList(); @@ -2479,6 +2514,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableGetAdapterConnectionStateCache() { mBluetoothGetAdapterConnectionStateCache.disableLocal(); } @@ -2503,6 +2539,7 @@ public final class BluetoothAdapter { */ @UnsupportedAppUsage @RequiresLegacyBluetoothPermission + @RequiresNoPermission public int getConnectionState() { if (getState() != STATE_ON) { return BluetoothAdapter.STATE_DISCONNECTED; @@ -2553,6 +2590,7 @@ public final class BluetoothAdapter { }; /** @hide */ + @RequiresNoPermission public void disableGetProfileConnectionStateCache() { mGetProfileConnectionStateCache.disableLocal(); } @@ -2753,6 +2791,7 @@ public final class BluetoothAdapter { return createNewRfcommSocketAndRecord(name, uuid, false, true); } + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, boolean auth, boolean encrypt) throws IOException { @@ -2779,6 +2818,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = @@ -2811,6 +2851,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) throws IOException { @@ -2844,6 +2885,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { return listenUsingL2capOn(port, false, false); @@ -2861,6 +2903,7 @@ public final class BluetoothAdapter { * permissions. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); @@ -2916,6 +2959,10 @@ public final class BluetoothAdapter { * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}. * @return true on success, false on error */ + @SuppressLint({ + "AndroidFrameworkRequiresPermission", + "AndroidFrameworkBluetoothPermission" + }) public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile) { if (context == null || listener == null) { @@ -2985,7 +3032,10 @@ public final class BluetoothAdapter { * @param profile * @param proxy Profile proxy object */ - @SuppressLint("AndroidFrameworkRequiresPermission") + @SuppressLint({ + "AndroidFrameworkRequiresPermission", + "AndroidFrameworkBluetoothPermission" + }) public void closeProfileProxy(int profile, BluetoothProfile proxy) { if (proxy == null) { return; @@ -3058,6 +3108,7 @@ public final class BluetoothAdapter { } } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { @SuppressLint("AndroidFrameworkRequiresPermission") @@ -3380,7 +3431,6 @@ public final class BluetoothAdapter { /** * @hide */ - @SuppressLint("AndroidFrameworkRequiresPermission") public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { private BluetoothStateChangeCallback mCallback; @@ -3631,6 +3681,7 @@ public final class BluetoothAdapter { return false; } + @SuppressLint("AndroidFrameworkBluetoothPermission") ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { @@ -3961,6 +4012,7 @@ public final class BluetoothAdapter { @Nullable byte[] value); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothConnectionCallback mConnectionCallback = new IBluetoothConnectionCallback.Stub() { @Override diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 942f8432639..9d3eed8f6fa 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,15 +16,12 @@ package android.bluetooth; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; -import android.annotation.RequiresPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; @@ -158,6 +155,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothGattCallback mBluetoothGattCallback = new IBluetoothGattCallback.Stub() { /** @@ -747,6 +745,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Application should call this method as early as possible after it is done with * this GATT client. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close() { if (DBG) Log.d(TAG, "close()"); @@ -881,6 +880,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Unregister the current application and callbacks. */ @UnsupportedAppUsage + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); @@ -973,6 +973,7 @@ public final class BluetoothGatt implements BluetoothProfile { * * @return true, if the connection attempt was initiated successfully */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect() { try { @@ -1003,6 +1004,7 @@ public final class BluetoothGatt implements BluetoothProfile { * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or * {@link BluetoothDevice#PHY_OPTION_S8} */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { try { @@ -1017,6 +1019,7 @@ public final class BluetoothGatt implements BluetoothProfile { * Read the current transmitter PHY and receiver PHY of the connection. The values are returned * in {@link BluetoothGattCallback#onPhyRead} */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy() { try { @@ -1031,6 +1034,7 @@ public final class BluetoothGatt implements BluetoothProfile { * * @return remote bluetooth device */ + @RequiresNoPermission public BluetoothDevice getDevice() { return mDevice; } @@ -1101,6 +1105,7 @@ public final class BluetoothGatt implements BluetoothProfile { * not yet been performed. */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public List getServices() { List result = new ArrayList(); @@ -1129,6 +1134,7 @@ public final class BluetoothGatt implements BluetoothProfile { * the remote device. */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { @@ -1444,6 +1450,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @deprecated Use {@link #abortReliableWrite()} */ @Deprecated + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite(BluetoothDevice mDevice) { abortReliableWrite(); @@ -1496,6 +1503,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean refresh() { if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); @@ -1579,6 +1587,7 @@ public final class BluetoothGatt implements BluetoothProfile { * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. * @throws IllegalArgumentException If the parameters are outside of their specified range. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestConnectionPriority(int connectionPriority) { if (connectionPriority < CONNECTION_PRIORITY_BALANCED @@ -1607,6 +1616,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @return true, if the request is send to the Bluetooth stack. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, @@ -1641,6 +1651,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public int getConnectionState(BluetoothDevice device) { throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } @@ -1652,6 +1663,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public List getConnectedDevices() { throw new UnsupportedOperationException( "Use BluetoothManager#getConnectedDevices instead."); @@ -1665,6 +1677,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public List getDevicesMatchingConnectionStates(int[] states) { throw new UnsupportedOperationException( "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index fdb801850e8..865f476e786 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -16,7 +16,9 @@ package android.bluetooth; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.os.ParcelUuid; @@ -58,6 +60,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Bluetooth GATT interface callbacks */ + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothGattServerCallback mBluetoothGattServerCallback = new IBluetoothGattServerCallback.Stub() { /** @@ -428,6 +431,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Application should call this method as early as possible after it is done with * this GATT server. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close() { if (DBG) Log.d(TAG, "close()"); @@ -510,6 +514,7 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Unregister the current application and callbacks. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void unregisterCallback() { if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); @@ -618,6 +623,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or * {@link BluetoothDevice#PHY_OPTION_S8} */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { try { @@ -634,6 +640,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * * @param device The remote device to send this response to */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(BluetoothDevice device) { try { @@ -814,6 +821,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @return List of services. Returns an empty list if no services have been added yet. */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public List getServices() { return mServices; } @@ -830,6 +838,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * this device. */ @RequiresLegacyBluetoothPermission + @RequiresNoPermission public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { if (service.getUuid().equals(uuid)) { @@ -848,6 +857,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public int getConnectionState(BluetoothDevice device) { throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } @@ -859,6 +869,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public List getConnectedDevices() { throw new UnsupportedOperationException( "Use BluetoothManager#getConnectedDevices instead."); @@ -872,6 +883,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * @throws UnsupportedOperationException */ @Override + @RequiresNoPermission public List getDevicesMatchingConnectionStates(int[] states) { throw new UnsupportedOperationException( "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 84e8c5134e7..a1ece7fc825 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; @@ -339,6 +340,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private volatile IBluetoothHeadset mService; private BluetoothAdapter mAdapter; + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -509,6 +511,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); @@ -529,6 +532,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); @@ -549,6 +553,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); @@ -620,6 +625,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -858,6 +864,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); @@ -885,6 +892,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAudioRouteAllowed(boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); @@ -907,6 +915,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getAudioRouteAllowed() { if (VDBG) log("getAudioRouteAllowed"); @@ -931,6 +940,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * False to use SCO audio in normal manner * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setForceScoAudio(boolean forced) { if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); @@ -991,6 +1001,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio() { final IBluetoothHeadset service = mService; @@ -1019,6 +1030,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio() { final IBluetoothHeadset service = mService; @@ -1123,6 +1135,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE, @@ -1147,6 +1160,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE, @@ -1320,6 +1334,7 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothProfileServiceConnection mConnection = new IBluetoothProfileServiceConnection.Stub() { @Override @@ -1356,6 +1371,7 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.d(TAG, msg); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 092130d0ce9..5816500d2c3 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; @@ -449,6 +448,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -476,6 +476,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -499,6 +500,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return list of connected devices; empty list if nothing is connected. */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); @@ -524,6 +526,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * list if nothing matches the states */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); @@ -548,6 +551,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return the state of connection of the device */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); @@ -576,6 +580,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); @@ -594,6 +599,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { @@ -675,6 +681,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); @@ -700,6 +707,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { @@ -728,6 +736,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); @@ -750,6 +759,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return list of calls; empty list if none call exists */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); @@ -772,6 +782,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return bundle of AG indicators; null if device is not in CONNECTED state */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); @@ -798,6 +809,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); @@ -821,6 +833,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); @@ -849,6 +862,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * supported.

    */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); @@ -881,6 +895,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not * supported.

    */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); @@ -911,6 +926,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not * supported.

    */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); @@ -940,6 +956,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature * is not supported.

    */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); @@ -965,6 +982,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * successfully; {@link null} otherwise; upon completion HFP sends {@link * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise; */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); @@ -991,6 +1009,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); @@ -1113,6 +1132,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio(BluetoothDevice device) { final IBluetoothHeadsetClient service = @@ -1139,6 +1159,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return true if command has been issued successfully; false * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio(BluetoothDevice device) { final IBluetoothHeadsetClient service = @@ -1162,6 +1183,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param device remote device * @return bundle of AG features; null if no service or AG not connected */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgFeatures(BluetoothDevice device) { final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 8ceeff53b13..1fd779a17db 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; @@ -170,6 +171,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -231,6 +233,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); @@ -251,6 +254,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { @@ -272,6 +276,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { @@ -368,6 +373,7 @@ public final class BluetoothHearingAid 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, @@ -390,6 +396,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index c214d2b85ac..6565ec0566b 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; @@ -439,6 +440,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { final IBluetoothHidDevice service = getService(); @@ -457,6 +459,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { final IBluetoothHidDevice service = getService(); @@ -475,6 +478,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { final IBluetoothHidDevice service = getService(); @@ -514,6 +518,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * object is required. * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean registerApp( BluetoothHidDeviceAppSdpSettings sdp, @@ -560,6 +565,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean unregisterApp() { boolean result = false; @@ -586,6 +592,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; @@ -613,6 +620,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param data Report data, not including Report Id. * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; @@ -638,6 +646,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @param error Error to be sent for SET_REPORT via HANDSHAKE. * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; @@ -662,6 +671,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @return the current user name, or empty string if cannot get the name * {@hide} */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getUserAppName() { final IBluetoothHidDevice service = getService(); @@ -687,6 +697,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { boolean result = false; @@ -712,6 +723,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * * @return true if the command is successfully sent; otherwise false. */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { boolean result = false; @@ -748,6 +760,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 70e3809cb59..bef4472b4a2 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -331,6 +331,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); @@ -353,6 +354,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); @@ -376,6 +378,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothInputStream.java b/framework/java/android/bluetooth/BluetoothInputStream.java index 8eb79b248d6..95f9229f044 100644 --- a/framework/java/android/bluetooth/BluetoothInputStream.java +++ b/framework/java/android/bluetooth/BluetoothInputStream.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SuppressLint; + import java.io.IOException; import java.io.InputStream; @@ -26,6 +28,7 @@ import java.io.InputStream; * * @hide */ +@SuppressLint("AndroidFrameworkBluetoothPermission") /*package*/ final class BluetoothInputStream extends InputStream { private BluetoothSocket mSocket; diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 4f095f6c700..c12b1f77fb9 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -155,6 +155,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(@Nullable BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); @@ -194,6 +195,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(@Nullable BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -214,6 +216,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); @@ -234,6 +237,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { @@ -294,6 +298,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return false on immediate error, true otherwise * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); @@ -370,6 +375,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return true if connectionPolicy is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -406,6 +412,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @return connection policy of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index a1e1b630508..2374f1cdc52 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -18,7 +18,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresFeature; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemService; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; @@ -93,6 +95,7 @@ public final class BluetoothManager { * * @return the BLUETOOTH Adapter */ + @RequiresNoPermission public BluetoothAdapter getAdapter() { return mAdapter; } @@ -218,6 +221,7 @@ public final class BluetoothManager { * @param callback GATT server callback handler that will receive asynchronous callbacks. * @return BluetoothGattServer instance */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) { @@ -238,6 +242,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, boolean eatt_support) { @@ -259,6 +264,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport) { @@ -281,6 +287,7 @@ public final class BluetoothManager { * @return BluetoothGattServer instance * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport, boolean eatt_support) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 3e7b75aa1f6..998fde02832 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -18,9 +18,11 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -126,6 +128,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getState() { if (VDBG) log("getState()"); @@ -151,6 +154,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getClient() { if (VDBG) log("getClient()"); @@ -175,6 +179,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); @@ -198,6 +203,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresNoPermission public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")" + "not supported for MAPS"); return false; @@ -212,6 +218,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -259,6 +266,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -285,6 +293,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); @@ -308,6 +317,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); @@ -335,6 +345,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @return true if priority is set, false on error * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -357,6 +368,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -391,6 +403,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @return priority of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -412,6 +425,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index db74a90f603..f20b533af04 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, diff --git a/framework/java/android/bluetooth/BluetoothOutputStream.java b/framework/java/android/bluetooth/BluetoothOutputStream.java index a0aa2dee9d3..ac2b3edb0eb 100644 --- a/framework/java/android/bluetooth/BluetoothOutputStream.java +++ b/framework/java/android/bluetooth/BluetoothOutputStream.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.SuppressLint; + import java.io.IOException; import java.io.OutputStream; @@ -26,6 +28,7 @@ import java.io.OutputStream; * * @hide */ +@SuppressLint("AndroidFrameworkBluetoothPermission") /*package*/ final class BluetoothOutputStream extends OutputStream { private BluetoothSocket mSocket; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index b3924b1fa92..c41c9dee254 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -240,6 +240,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -282,6 +283,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -311,6 +313,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -342,6 +345,7 @@ public final class BluetoothPan implements BluetoothProfile { */ @SystemApi @Override + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -416,6 +420,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -441,6 +446,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 6c2e5bf2d39..e41eb4f2581 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -110,6 +110,7 @@ public class BluetoothPbap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -217,6 +218,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { log("getConnectedDevices()"); @@ -264,6 +266,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); @@ -297,6 +300,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @SystemApi + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -330,6 +334,7 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); @@ -347,6 +352,7 @@ public class BluetoothPbap implements BluetoothProfile { return false; } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { log("Proxy object connected"); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 2c8fbc2509f..85b86507650 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -161,6 +162,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return list of connected devices */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) { @@ -187,6 +189,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return list of matching devices */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) { @@ -213,6 +216,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return device connection state */ @Override + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 70053ee7149..161c843f039 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.annotation.IntDef; +import android.annotation.RequiresNoPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; @@ -333,6 +334,7 @@ public interface BluetoothProfile { * @param profile - One of {@link #HEADSET} or {@link #A2DP} * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp} */ + @RequiresNoPermission public void onServiceConnected(int profile, BluetoothProfile proxy); /** @@ -341,6 +343,7 @@ public interface BluetoothProfile { * * @param profile - One of {@link #HEADSET} or {@link #A2DP} */ + @RequiresNoPermission public void onServiceDisconnected(int profile); } diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index 12abcc4d11d..b20ab754932 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; @@ -33,6 +32,7 @@ import android.util.Log; * @param The Bluetooth profile interface for this connection. * @hide */ +@SuppressLint("AndroidFrameworkBluetoothPermission") public abstract class BluetoothProfileConnector { private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index c85494c01b2..87da22cbb7f 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; @@ -143,6 +144,7 @@ public final class BluetoothSap implements BluetoothProfile { * connected to the Sap service. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getState() { if (VDBG) log("getState()"); @@ -167,6 +169,7 @@ public final class BluetoothSap implements BluetoothProfile { * this proxy object is not connected to the Sap service. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getClient() { if (VDBG) log("getClient()"); @@ -191,6 +194,7 @@ public final class BluetoothSap implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); @@ -214,6 +218,7 @@ public final class BluetoothSap implements BluetoothProfile { * * @hide */ + @RequiresNoPermission public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); return false; @@ -227,6 +232,7 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); @@ -249,6 +255,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return list of connected devices * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); @@ -271,6 +278,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return list of matching devices * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); @@ -293,6 +301,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return device connection state * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); @@ -320,6 +329,7 @@ public final class BluetoothSap 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, @@ -341,6 +351,7 @@ public final class BluetoothSap 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, @@ -375,6 +386,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return priority of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -395,6 +407,7 @@ public final class BluetoothSap implements BluetoothProfile { * @return connection policy of the device * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index 50822354d69..bb4e35483fe 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.ParcelUuid; @@ -70,6 +71,7 @@ import java.io.IOException; * * {@see BluetoothSocket} */ +@SuppressLint("AndroidFrameworkBluetoothPermission") public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index ef88147a40f..bb409d5360f 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; import android.os.Build; @@ -198,7 +200,6 @@ public final class BluetoothSocket implements Closeable { * @throws IOException On error, for example Bluetooth not available, or insufficient * privileges */ - @SuppressLint("AndroidFrameworkRequiresPermission") /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) throws IOException { @@ -326,6 +327,7 @@ public final class BluetoothSocket implements Closeable { * * @return remote device */ + @RequiresNoPermission public BluetoothDevice getRemoteDevice() { return mDevice; } @@ -338,6 +340,7 @@ public final class BluetoothSocket implements Closeable { * * @return InputStream */ + @RequiresNoPermission public InputStream getInputStream() throws IOException { return mInputStream; } @@ -350,6 +353,7 @@ public final class BluetoothSocket implements Closeable { * * @return OutputStream */ + @RequiresNoPermission public OutputStream getOutputStream() throws IOException { return mOutputStream; } @@ -360,6 +364,7 @@ public final class BluetoothSocket implements Closeable { * * @return true if connected false if not connected */ + @RequiresNoPermission public boolean isConnected() { return mSocketState == SocketState.CONNECTED; } @@ -386,6 +391,7 @@ public final class BluetoothSocket implements Closeable { * * @throws IOException on error, for example connection failure */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void connect() throws IOException { if (mDevice == null) throw new IOException("Connect is called on null device"); @@ -637,6 +643,7 @@ public final class BluetoothSocket implements Closeable { * * @return the maximum supported Transmit packet size for the underlying transport. */ + @RequiresNoPermission public int getMaxTransmitPacketSize() { return mMaxTxPacketSize; } @@ -649,6 +656,7 @@ public final class BluetoothSocket implements Closeable { * * @return the maximum supported Receive packet size for the underlying transport. */ + @RequiresNoPermission public int getMaxReceivePacketSize() { return mMaxRxPacketSize; } @@ -658,6 +666,7 @@ public final class BluetoothSocket implements Closeable { * * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} */ + @RequiresNoPermission public int getConnectionType() { if (mType == TYPE_L2CAP_LE) { // Treat the LE CoC to be the same type as L2CAP. @@ -674,6 +683,7 @@ public final class BluetoothSocket implements Closeable { * generate SPP SDP record. * @hide */ + @RequiresNoPermission public void setExcludeSdp(boolean excludeSdp) { mExcludeSdp = excludeSdp; } @@ -684,6 +694,7 @@ public final class BluetoothSocket implements Closeable { * connection. This function is currently used for testing only. * @hide */ + @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void requestMaximumTxDataLength() throws IOException { if (mDevice == null) { diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index d82cf19e882..bc3754a2fc5 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; @@ -34,6 +35,7 @@ import java.util.UUID; * @hide */ @SystemApi +@SuppressLint("AndroidFrameworkBluetoothPermission") public final class BluetoothUuid { /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 54a18e6f1d6..d7e48ca543c 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -16,7 +16,9 @@ package android.bluetooth.le; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; @@ -216,6 +218,7 @@ public final class AdvertisingSet { * * @hide */ + @RequiresNoPermission public int getAdvertiserId() { return mAdvertiserId; } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 710fa6827b0..ff279d85987 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; @@ -163,11 +164,13 @@ public final class BluetoothLeAdvertiser { } } - @SuppressLint("AndroidFrameworkRequiresPermission") + @SuppressLint({ + "AndroidFrameworkBluetoothPermission", + "AndroidFrameworkRequiresPermission", + }) AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) { return new AdvertisingSetCallback() { @Override - @SuppressLint("AndroidFrameworkRequiresPermission") public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) { if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { @@ -180,7 +183,6 @@ public final class BluetoothLeAdvertiser { /* Legacy advertiser is disabled on timeout */ @Override - @SuppressLint("AndroidFrameworkRequiresPermission") public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, int status) { if (enabled) { @@ -491,6 +493,7 @@ public final class BluetoothLeAdvertiser { * * @hide */ + @RequiresNoPermission public void cleanup() { mLegacyAdvertisers.clear(); mCallbackWrappers.clear(); @@ -498,6 +501,7 @@ public final class BluetoothLeAdvertiser { } // Compute the size of advertisement data or scan resp + @RequiresBluetoothAdvertisePermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { if (data == null) return 0; @@ -582,6 +586,7 @@ public final class BluetoothLeAdvertiser { return array == null ? 0 : array.length; } + @SuppressLint("AndroidFrameworkBluetoothPermission") IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { return new IAdvertisingSetCallback.Stub() { @Override @@ -706,6 +711,7 @@ public final class BluetoothLeAdvertiser { }; } + @SuppressLint("AndroidFrameworkBluetoothPermission") private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback, final int error) { handler.post(new Runnable() { @@ -716,6 +722,7 @@ public final class BluetoothLeAdvertiser { }); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private void postStartFailure(final AdvertiseCallback callback, final int error) { mHandler.post(new Runnable() { @Override @@ -725,6 +732,7 @@ public final class BluetoothLeAdvertiser { }); } + @SuppressLint("AndroidFrameworkBluetoothPermission") private void postStartSuccess(final AdvertiseCallback callback, final AdvertiseSettings settings) { mHandler.post(new Runnable() { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 4271a905c22..09cd11d0759 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,9 +16,9 @@ package android.bluetooth.le; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -357,6 +357,7 @@ public final class BluetoothLeScanner { * @hide */ @SystemApi + @RequiresBluetoothScanPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(List truncatedFilters, ScanSettings settings, final ScanCallback callback) { @@ -376,6 +377,7 @@ public final class BluetoothLeScanner { * * @hide */ + @RequiresNoPermission public void cleanup() { mLeScanClients.clear(); } @@ -383,6 +385,7 @@ public final class BluetoothLeScanner { /** * Bluetooth GATT interface callbacks */ + @SuppressLint("AndroidFrameworkRequiresPermission") private class BleScanCallbackWrapper extends IScannerCallback.Stub { private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; @@ -412,8 +415,6 @@ public final class BluetoothLeScanner { mResultStorages = resultStorages; } - @SuppressLint("AndroidFrameworkRequiresPermission") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startRegistration() { synchronized (this) { // Scan stopped. @@ -441,8 +442,6 @@ public final class BluetoothLeScanner { } } - @SuppressLint("AndroidFrameworkRequiresPermission") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan() { synchronized (this) { if (mScannerId <= 0) { @@ -459,8 +458,6 @@ public final class BluetoothLeScanner { } } - @SuppressLint("AndroidFrameworkRequiresPermission") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void flushPendingBatchResults() { synchronized (this) { if (mScannerId <= 0) { @@ -479,7 +476,6 @@ public final class BluetoothLeScanner { * Application interface registered - app is ready to go */ @Override - @SuppressLint("AndroidFrameworkRequiresPermission") public void onScannerRegistered(int status, int scannerId) { Log.d(TAG, "onScannerRegistered() - status=" + status + " scannerId=" + scannerId + " mScannerId=" + mScannerId); diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 9ea6c4866f6..26978e39807 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -17,6 +17,7 @@ package android.bluetooth.le; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; @@ -208,6 +209,7 @@ public final class PeriodicAdvertisingManager { } } + @SuppressLint("AndroidFrameworkBluetoothPermission") private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { return new IPeriodicAdvertisingCallback.Stub() { diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 27c579b3cdf..c5c4277d9eb 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,7 +16,6 @@ package android.bluetooth.le; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java index 794b512772f..9b8c2eaf4d1 100644 --- a/framework/java/android/bluetooth/le/ScanRecord.java +++ b/framework/java/android/bluetooth/le/ScanRecord.java @@ -18,6 +18,7 @@ package android.bluetooth.le; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothUuid; import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelUuid; @@ -34,6 +35,7 @@ import java.util.function.Predicate; /** * Represents a scan record from Bluetooth LE scan. */ +@SuppressLint("AndroidFrameworkBluetoothPermission") public final class ScanRecord { private static final String TAG = "ScanRecord"; diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java index a753aa6fef1..93f526bb9f0 100644 --- a/framework/java/android/bluetooth/le/TruncatedFilter.java +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -16,6 +16,7 @@ package android.bluetooth.le; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import java.util.List; @@ -26,6 +27,7 @@ import java.util.List; * @hide */ @SystemApi +@SuppressLint("AndroidFrameworkBluetoothPermission") public final class TruncatedFilter { private final ScanFilter mFilter; private final List mStorageDescriptors; -- GitLab From 3614e0a2a9aa949c68f3afb52fcee71a4fa39e48 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 16 Apr 2021 15:34:54 -0600 Subject: [PATCH 1265/1408] Add missing Bluetooth API permission enforcement. 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: I37a9e03ecdca6f7a6eb9d7f094e2f95a97036612 --- .../java/android/bluetooth/BluetoothA2dpSink.java | 8 ++++++++ .../android/bluetooth/BluetoothAvrcpController.java | 12 ++++++++++++ .../android/bluetooth/BluetoothHeadsetClient.java | 8 ++++++++ .../java/android/bluetooth/BluetoothHearingAid.java | 6 ++++++ .../java/android/bluetooth/BluetoothLeAudio.java | 4 ++++ .../android/bluetooth/le/BluetoothLeScanner.java | 3 +++ .../server/bluetooth/BluetoothManagerService.java | 3 --- 7 files changed, 41 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 6aba483b151..280e8bc8402 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -187,6 +187,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothA2dpSink service = getService(); @@ -208,6 +210,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothA2dpSink service = getService(); @@ -229,6 +233,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothA2dpSink service = getService(); @@ -256,6 +262,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); final IBluetoothA2dpSink service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 887cf3f08b9..5148d5b431d 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -117,6 +117,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothAvrcpController service = @@ -137,6 +139,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothAvrcpController service = @@ -157,6 +161,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothAvrcpController service = @@ -178,6 +184,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; @@ -198,6 +206,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * Sets the player app setting for current player. * returns true in case setting is supported by remote, false otherwise */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); final IBluetoothAvrcpController service = @@ -218,6 +228,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * Send Group Navigation Command to Remote. * possible keycode values: next_grp, previous_grp defined above */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 5816500d2c3..eef42d1b2f3 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -1038,6 +1038,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when * feature is not supported.

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); final IBluetoothHeadsetClient service = @@ -1059,6 +1061,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Note: This is an internal function and shouldn't be exposed */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadsetClient service = @@ -1083,6 +1087,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @param allowed if routing is allowed to the device Note: This is an internal function and * shouldn't be exposed */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); final IBluetoothHeadsetClient service = @@ -1106,6 +1112,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return whether the command succeeded Note: This is an internal function and shouldn't be * exposed */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); final IBluetoothHeadsetClient service = diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 1fd779a17db..fa52eda8ea6 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -497,6 +497,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @param volume Absolute volume to be set on remote * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); @@ -556,6 +558,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getDeviceSide(BluetoothDevice device) { if (VDBG) { log("getDeviceSide(" + device + ")"); @@ -582,6 +586,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getDeviceMode(BluetoothDevice device) { if (VDBG) { log("getDeviceMode(" + device + ")"); diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index c12b1f77fb9..462c7b7ede6 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -325,6 +325,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ @NonNull @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getActiveDevices() { if (VDBG) log("getActiveDevices()"); try { @@ -348,6 +350,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * @hide */ @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull BluetoothDevice device) { if (VDBG) log("getGroupId()"); try { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 09cd11d0759..f27f22b9af5 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -442,6 +442,7 @@ public final class BluetoothLeScanner { } } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan() { synchronized (this) { if (mScannerId <= 0) { @@ -458,6 +459,7 @@ public final class BluetoothLeScanner { } } + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) void flushPendingBatchResults() { synchronized (this) { if (mScannerId <= 0) { @@ -588,6 +590,7 @@ public final class BluetoothLeScanner { } } + @SuppressLint("AndroidFrameworkBluetoothPermission") private void postCallbackError(final ScanCallback callback, final int errorCode) { mHandler.post(new Runnable() { @Override diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index feed2205dc4..c3a5d1f871b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -714,9 +714,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Callback is null in unregisterAdapter"); return; } - if (!checkConnectPermissionForPreflight(mContext)) { - return; - } synchronized (mCallbacks) { mCallbacks.unregister(callback); } -- GitLab From ae8fd75393f903bfe10f478dbd0f6142e75831cb Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 15 Apr 2021 12:17:11 -0700 Subject: [PATCH 1266/1408] Set a floor value for BLE Batch Scan report delay of 5000ms Tag: #feature Bug: 167340030 Test: Manual Change-Id: I4ef99a9562f1447a2ccee42a344384a38a487c19 --- .../android/bluetooth/le/ScanSettings.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 368d1eecade..f3e971a0bb3 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -20,6 +20,7 @@ import android.annotation.SystemApi; import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; +import android.provider.DeviceConfig; /** * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the @@ -141,6 +142,12 @@ public final class ScanSettings implements Parcelable { */ public static final int PHY_LE_ALL_SUPPORTED = 255; + /** + * The default floor value for report delays greater than 0 in + * {@link Builder#setReportDelay(long)}. + */ + private static final long DEFAULT_REPORT_DELAY_FLOOR = 5000; + // Bluetooth LE scan mode. private int mScanMode; @@ -345,18 +352,28 @@ public final class ScanSettings implements Parcelable { } /** - * Set report delay timestamp for Bluetooth LE scan. + * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of + * scan results immediately. If > 0, scan results are queued up and delivered after the + * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be + * delivered sooner if the internal buffers fill up. * - * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of - * results immediately. Values > 0 causes the scan results to be queued up and delivered - * after the requested delay or when the internal buffers fill up. - * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. + * @param reportDelayMillis how frequently scan results should be delivered in + * milliseconds + * @throws IllegalArgumentException if {@code reportDelayMillis} < 0 */ public Builder setReportDelay(long reportDelayMillis) { if (reportDelayMillis < 0) { throw new IllegalArgumentException("reportDelay must be > 0"); } - mReportDelayMillis = reportDelayMillis; + + long floor = DeviceConfig.getLong(DeviceConfig.NAMESPACE_BLUETOOTH, "report_delay", + DEFAULT_REPORT_DELAY_FLOOR); + + if (reportDelayMillis > 0 && reportDelayMillis < floor) { + mReportDelayMillis = floor; + } else { + mReportDelayMillis = reportDelayMillis; + } return this; } -- GitLab From 8c8f7b80a1a01bdc7224ab44c514626f94b1c107 Mon Sep 17 00:00:00 2001 From: Nataniel Borges Date: Tue, 20 Apr 2021 15:50:03 +0000 Subject: [PATCH 1267/1408] Revert "Set a floor value for BLE Batch Scan report delay of 5000ms" This reverts commit ae8fd75393f903bfe10f478dbd0f6142e75831cb. Reason for revert: b/185890964 Change-Id: I270dc16de0e24728c694830fbe920786b6d90174 --- .../android/bluetooth/le/ScanSettings.java | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index f3e971a0bb3..368d1eecade 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -20,7 +20,6 @@ import android.annotation.SystemApi; import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; -import android.provider.DeviceConfig; /** * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the @@ -142,12 +141,6 @@ public final class ScanSettings implements Parcelable { */ public static final int PHY_LE_ALL_SUPPORTED = 255; - /** - * The default floor value for report delays greater than 0 in - * {@link Builder#setReportDelay(long)}. - */ - private static final long DEFAULT_REPORT_DELAY_FLOOR = 5000; - // Bluetooth LE scan mode. private int mScanMode; @@ -352,28 +345,18 @@ public final class ScanSettings implements Parcelable { } /** - * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of - * scan results immediately. If > 0, scan results are queued up and delivered after the - * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be - * delivered sooner if the internal buffers fill up. + * Set report delay timestamp for Bluetooth LE scan. * - * @param reportDelayMillis how frequently scan results should be delivered in - * milliseconds - * @throws IllegalArgumentException if {@code reportDelayMillis} < 0 + * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of + * results immediately. Values > 0 causes the scan results to be queued up and delivered + * after the requested delay or when the internal buffers fill up. + * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. */ public Builder setReportDelay(long reportDelayMillis) { if (reportDelayMillis < 0) { throw new IllegalArgumentException("reportDelay must be > 0"); } - - long floor = DeviceConfig.getLong(DeviceConfig.NAMESPACE_BLUETOOTH, "report_delay", - DEFAULT_REPORT_DELAY_FLOOR); - - if (reportDelayMillis > 0 && reportDelayMillis < floor) { - mReportDelayMillis = floor; - } else { - mReportDelayMillis = reportDelayMillis; - } + mReportDelayMillis = reportDelayMillis; return this; } -- GitLab From b20cfc47548bc6e17a5cf16f30d82c018c82e905 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 20 Apr 2021 14:22:16 -0700 Subject: [PATCH 1268/1408] Update docs to reflect LE batch scan report delay floor Tag: #feature Bug: 167340030 Test: Manual Change-Id: Ieeb0e6bccfc316fd4c8cdc40f6865b4185d6d9e8 --- .../java/android/bluetooth/le/ScanSettings.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 368d1eecade..1aa7cb5111c 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -345,12 +345,14 @@ public final class ScanSettings implements Parcelable { } /** - * Set report delay timestamp for Bluetooth LE scan. + * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of + * scan results immediately. If > 0, scan results are queued up and delivered after the + * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be + * delivered sooner if the internal buffers fill up. * - * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of - * results immediately. Values > 0 causes the scan results to be queued up and delivered - * after the requested delay or when the internal buffers fill up. - * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. + * @param reportDelayMillis how frequently scan results should be delivered in + * milliseconds + * @throws IllegalArgumentException if {@code reportDelayMillis} < 0 */ public Builder setReportDelay(long reportDelayMillis) { if (reportDelayMillis < 0) { -- GitLab From 486609871c5ac776d11ace1b73675e809417ddbb Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 20 Apr 2021 14:22:16 -0700 Subject: [PATCH 1269/1408] Update docs to reflect LE batch scan report delay floor Tag: #feature Bug: 167340030 Test: Manual Change-Id: Ieeb0e6bccfc316fd4c8cdc40f6865b4185d6d9e8 --- .../java/android/bluetooth/le/ScanSettings.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java index 368d1eecade..1aa7cb5111c 100644 --- a/framework/java/android/bluetooth/le/ScanSettings.java +++ b/framework/java/android/bluetooth/le/ScanSettings.java @@ -345,12 +345,14 @@ public final class ScanSettings implements Parcelable { } /** - * Set report delay timestamp for Bluetooth LE scan. + * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of + * scan results immediately. If > 0, scan results are queued up and delivered after the + * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be + * delivered sooner if the internal buffers fill up. * - * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of - * results immediately. Values > 0 causes the scan results to be queued up and delivered - * after the requested delay or when the internal buffers fill up. - * @throws IllegalArgumentException If {@code reportDelayMillis} < 0. + * @param reportDelayMillis how frequently scan results should be delivered in + * milliseconds + * @throws IllegalArgumentException if {@code reportDelayMillis} < 0 */ public Builder setReportDelay(long reportDelayMillis) { if (reportDelayMillis < 0) { -- GitLab From f0a1cae1d658c6915f9d663f1eca70f02c8c3581 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Tue, 20 Apr 2021 09:56:45 +0100 Subject: [PATCH 1270/1408] Pass AttributionSource to AdapterService methods. This adds attribution source to AdapterService bluetooth method calls. This is now required to allow the app ops for the new bluetooth permissions (BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE, and BLUETOOTH_SCAN) to be noted. Bug: 183626112 Test: atest AdapterServiceTest Test: atest CtsPermissionTestCases:android.permission.cts.NearbyDevicesPermissionTest Change-Id: I8d1fe41ca9945a3baab584f248a17b3a1eb255f7 --- .../android/bluetooth/BluetoothAdapter.java | 40 ++++++------ .../android/bluetooth/BluetoothDevice.java | 64 +++++++++++-------- .../bluetooth/BluetoothManagerService.java | 17 +++-- 3 files changed, 69 insertions(+), 52 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 052a7733c22..bf492efba65 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -31,12 +31,12 @@ import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.PeriodicAdvertisingManager; @@ -797,7 +797,7 @@ public final class BluetoothAdapter { */ @RequiresNoPermission public BluetoothDevice getRemoteDevice(String address) { - return new BluetoothDevice(address); + return new BluetoothDevice(address, getAttributionSource()); } /** @@ -818,7 +818,7 @@ public final class BluetoothAdapter { } return new BluetoothDevice( String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], - address[2], address[3], address[4], address[5])); + address[2], address[3], address[4], address[5]), getAttributionSource()); } /** @@ -1320,7 +1320,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getUuids(); + return mService.getUuids(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1354,7 +1354,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setName(name); + return mService.setName(name, getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1382,7 +1382,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getBluetoothClass(); + return mService.getBluetoothClass(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1438,7 +1438,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(); + if (mService != null) return mService.getIoCapability(getAttributionSource()); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1491,7 +1491,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(); + if (mService != null) return mService.getLeIoCapability(getAttributionSource()); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1553,7 +1553,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getScanMode(); + return mService.getScanMode(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1602,7 +1602,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds); + return mService.setScanMode(mode, durationSeconds, getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1652,7 +1652,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout()); + return mService.setScanMode(mode, getDiscoverableTimeout(), getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1673,7 +1673,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoverableTimeout(); + return mService.getDiscoverableTimeout(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1694,7 +1694,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.setDiscoverableTimeout(timeout); + mService.setDiscoverableTimeout(timeout, getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1745,7 +1745,7 @@ public final class BluetoothAdapter { return ActivityThread.currentOpPackageName(); } - private AttributionSource getAttributionSource() { + AttributionSource getAttributionSource() { if (mContext != null) { return mContext.getAttributionSource(); } @@ -1828,7 +1828,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.cancelDiscovery(); + return mService.cancelDiscovery(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1866,7 +1866,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isDiscovering(); + return mService.isDiscovering(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2303,7 +2303,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMaxConnectedAudioDevices(); + return mService.getMaxConnectedAudioDevices(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); @@ -2415,7 +2415,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(); + return mService.getMostRecentlyConnectedDevices(getAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2445,7 +2445,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices()); + return toDeviceSet(mService.getBondedDevices(getAttributionSource())); } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1201663d1d1..701a2cc33b7 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -32,6 +32,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.companion.AssociationRequest; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Build; import android.os.Handler; @@ -1087,6 +1088,8 @@ public final class BluetoothDevice implements Parcelable { */ private static volatile IBluetooth sService; + private final AttributionSource mAttributionSource; + private final String mAddress; @AddressType private final int mAddressType; @@ -1135,12 +1138,12 @@ public final class BluetoothDevice implements Parcelable { * and is validated in this constructor. * * @param address valid Bluetooth MAC address + * @param attributionSource attribution for permission-protected calls * @throws RuntimeException Bluetooth is not available on this platform * @throws IllegalArgumentException address is invalid * @hide */ - @UnsupportedAppUsage - /*package*/ BluetoothDevice(String address) { + public BluetoothDevice(String address, AttributionSource attributionSource) { getService(); // ensures sService is initialized if (!BluetoothAdapter.checkBluetoothAddress(address)) { throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); @@ -1148,6 +1151,12 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; + mAttributionSource = attributionSource; + } + + @UnsupportedAppUsage + /*package*/ BluetoothDevice(String address) { + this(address, BluetoothAdapter.getDefaultAdapter().getAttributionSource()); } @Override @@ -1185,7 +1194,8 @@ public final class BluetoothDevice implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothDevice createFromParcel(Parcel in) { - return new BluetoothDevice(in.readString()); + return new BluetoothDevice( + in.readString(), in.readParcelable(getClass().getClassLoader())); } public BluetoothDevice[] newArray(int size) { @@ -1196,6 +1206,7 @@ public final class BluetoothDevice implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); + out.writeParcelable(mAttributionSource, 0); } /** @@ -1240,7 +1251,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String name = service.getRemoteName(this); + String name = service.getRemoteName(this, mAttributionSource); if (name != null) { // remove whitespace characters from the name return name @@ -1271,7 +1282,7 @@ public final class BluetoothDevice implements Parcelable { return DEVICE_TYPE_UNKNOWN; } try { - return service.getRemoteType(this); + return service.getRemoteType(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1295,7 +1306,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String alias = service.getRemoteAlias(this); + String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource); if (alias == null) { return getName(); } @@ -1337,7 +1348,8 @@ public final class BluetoothDevice implements Parcelable { } try { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - return service.setRemoteAlias(this, alias, adapter.getOpPackageName()); + return service.setRemoteAlias( + this, alias, adapter.getOpPackageName(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1363,7 +1375,7 @@ public final class BluetoothDevice implements Parcelable { return BATTERY_LEVEL_BLUETOOTH_OFF; } try { - return service.getBatteryLevel(this); + return service.getBatteryLevel(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1453,8 +1465,8 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.createBond(this, transport, remoteP192Data, remoteP256Data, - BluetoothAdapter.getDefaultAdapter().getOpPackageName()); + return service.createBond( + this, transport, remoteP192Data, remoteP256Data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1479,7 +1491,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.isBondingInitiatedLocally(this); + return service.isBondingInitiatedLocally(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1504,7 +1516,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "cancelBondProcess() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.cancelBondProcess(this); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1532,7 +1544,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "removeBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.removeBond(this); + return service.removeBond(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1548,7 +1560,7 @@ public final class BluetoothDevice implements Parcelable { @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(BluetoothDevice query) { try { - return sService.getBondState(query); + return sService.getBondState(query, mAttributionSource); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1638,7 +1650,8 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED; + return service.getConnectionStateWithAttribution(this, mAttributionSource) + != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1663,7 +1676,8 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionState(this) > CONNECTION_STATE_CONNECTED; + return service.getConnectionStateWithAttribution(this, mAttributionSource) + > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1685,7 +1699,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - int classInt = service.getRemoteClass(this); + int classInt = service.getRemoteClass(this, mAttributionSource); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) { @@ -1714,7 +1728,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteUuids(this); + return service.getRemoteUuids(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1744,7 +1758,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.fetchRemoteUuids(this); + return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1781,7 +1795,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.sdpSearch(this, uuid); + return service.sdpSearch(this, uuid, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1803,7 +1817,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setPin(this, true, pin.length, pin); + return service.setPin(this, true, pin.length, pin, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1866,7 +1880,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.cancelBondProcess(this); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1899,7 +1913,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getPhonebookAccessPermission(this); + return service.getPhonebookAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2005,7 +2019,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getMessageAccessPermission(this); + return service.getMessageAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2056,7 +2070,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getSimAccessPermission(this); + return service.getSimAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c3a5d1f871b..6c1c9d3a57d 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -347,7 +347,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { addActiveLog( BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, mContext.getPackageName(), false); - mBluetooth.disable(); + mBluetooth.disable(mContext.getAttributionSource()); return true; } } catch (RemoteException e) { @@ -1714,7 +1714,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - return mBluetooth.getAddress(); + return mBluetooth.getAddressWithAttribution(mContext.getAttributionSource()); } } catch (RemoteException e) { Slog.e(TAG, @@ -1743,7 +1743,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - return mBluetooth.getName(); + return mBluetooth.getName(mContext.getAttributionSource()); } } catch (RemoteException e) { Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e); @@ -1830,7 +1830,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } else if (mBluetooth != null) { try { - storeNameAndAddress(mBluetooth.getName(), mBluetooth.getAddress()); + storeNameAndAddress( + mBluetooth.getName(mContext.getAttributionSource()), + mBluetooth.getAddressWithAttribution( + mContext.getAttributionSource())); } catch (RemoteException re) { Slog.e(TAG, "Unable to grab names", re); } @@ -2096,7 +2099,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Do enable request try { - if (!mBluetooth.enable(mQuietEnable)) { + if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) { Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { @@ -2417,7 +2420,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (mBluetooth != null) { //Enable bluetooth try { - if (!mBluetooth.enable(mQuietEnable)) { + if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) { Slog.e(TAG, "IBluetooth.enable() returned false"); } } catch (RemoteException e) { @@ -2447,7 +2450,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (DBG) { Slog.d(TAG, "Sending off request."); } - if (!mBluetooth.disable()) { + if (!mBluetooth.disable(mContext.getAttributionSource())) { Slog.e(TAG, "IBluetooth.disable() returned false"); } } -- GitLab From d7c55664839ca7e6237cd0ef0f36eb51a4ee7ae6 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 20 Apr 2021 12:30:37 -0600 Subject: [PATCH 1271/1408] 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/BluetoothA2dp.java | 2 ++ .../android/bluetooth/BluetoothA2dpSink.java | 6 ++++- .../android/bluetooth/BluetoothAdapter.java | 10 ++++++++ .../bluetooth/BluetoothAvrcpController.java | 6 +++++ .../android/bluetooth/BluetoothDevice.java | 22 ++++++++++++++---- .../bluetooth/BluetoothDevicePicker.java | 6 +++++ .../android/bluetooth/BluetoothHeadset.java | 1 + .../bluetooth/BluetoothHeadsetClient.java | 23 +++++++++++++++++++ .../android/bluetooth/BluetoothHidHost.java | 10 ++++++++ .../java/android/bluetooth/BluetoothMap.java | 5 ++++ .../android/bluetooth/BluetoothMapClient.java | 19 +++++++++++++++ .../java/android/bluetooth/BluetoothPbap.java | 3 ++- .../bluetooth/BluetoothPbapClient.java | 5 ++++ .../java/android/bluetooth/BluetoothSap.java | 3 +++ .../bluetooth/BluetoothManagerService.java | 4 ++-- 15 files changed, 117 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 0d21e09293b..c2718621a37 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -104,6 +104,8 @@ public final class BluetoothA2dp implements BluetoothProfile { "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 280e8bc8402..cbbf4c97969 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -19,8 +19,10 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; @@ -70,7 +72,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @SuppressLint("ActionValue") - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 052a7733c22..0442514d9c6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -604,6 +604,7 @@ public final class BluetoothAdapter { * * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; @@ -618,6 +619,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; @@ -642,6 +646,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_CONNECTED = "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; @@ -656,6 +663,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_DISCONNECTED = "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 5148d5b431d..cac676d7dc9 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -17,7 +17,9 @@ package android.bluetooth; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; @@ -62,6 +64,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; @@ -74,6 +77,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * most recent player setting. * */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 1201663d1d1..46ca5008233 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -389,6 +389,8 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = @@ -663,13 +665,15 @@ public final class BluetoothDevice implements Parcelable { *

    Always contains the extra field {@link #EXTRA_UUID} */ @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MAS_INSTANCE = "android.bluetooth.device.action.MAS_INSTANCE"; @@ -693,28 +697,36 @@ public final class BluetoothDevice implements Parcelable { * Broadcast Action: This intent is used to broadcast PAIRING REQUEST */ @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_REQUEST = "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_REPLY = "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_CANCEL = "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; @@ -725,6 +737,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_SILENCE_MODE_CHANGED = diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java index 09b0a80313f..26e46573dd9 100644 --- a/framework/java/android/bluetooth/BluetoothDevicePicker.java +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; /** * A helper to show a system "Device Picker" activity to the user. @@ -39,6 +41,8 @@ public interface BluetoothDevicePicker { * Selected {@link BluetoothDevice} is returned in extra data named * {@link BluetoothDevice#EXTRA_DEVICE}. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DEVICE_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; @@ -54,6 +58,8 @@ public interface BluetoothDevicePicker { * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent * come from */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH"; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index a1ece7fc825..51ef3c284f4 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -310,6 +310,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index eef42d1b2f3..840b4d3121b 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -72,6 +74,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * booleans with value true, * and not supported ones are not being sent at all.

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; @@ -90,6 +95,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * it also includes {@link #EXTRA_AUDIO_WBS} * indicating wide band speech support.

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; @@ -106,6 +114,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_VOICE_RECOGNITION}, * {@link #EXTRA_IN_BAND_RING}

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AG_EVENT = "android.bluetooth.headsetclient.profile.action.AG_EVENT"; @@ -117,6 +128,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * with value of {@link BluetoothHeadsetClientCall} instance, * representing actual call state.

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CALL_CHANGED = "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; @@ -127,6 +141,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * like ACTION_AG_EVENT with EXTRA_VOICE_RECOGNITION value * when for example user started voice recognition from HF unit. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_RESULT = "android.bluetooth.headsetclient.profile.action.RESULT"; @@ -138,6 +155,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Vendor event can be a response to an vendor specific command or unsolicited. * */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; @@ -149,6 +169,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_NUMBER}, * with a String value representing phone number.

    */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAST_VTAG = "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index bef4472b4a2..68a9d371d3f 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -80,6 +80,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PROTOCOL_MODE_CHANGED = "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; @@ -87,6 +89,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_HANDSHAKE = "android.bluetooth.input.profile.action.HANDSHAKE"; @@ -94,6 +98,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_REPORT = "android.bluetooth.input.profile.action.REPORT"; @@ -101,6 +107,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; @@ -108,6 +116,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_IDLE_TIME_CHANGED = "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 998fde02832..a025d9b4d0c 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -53,6 +55,9 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { /** @hide */ @SuppressLint("ActionValue") @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index f20b533af04..d72081c0bac 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"; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index e41eb4f2581..ef6fddf54d6 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -89,7 +89,8 @@ public class BluetoothPbap implements BluetoothProfile { */ @SuppressLint("ActionValue") @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 85b86507650..7f4863891ef 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -19,7 +19,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.content.Context; import android.os.Binder; @@ -41,6 +43,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { private static final boolean DBG = false; private static final boolean VDBG = false; + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 87da22cbb7f..b86857f42f4 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -19,7 +19,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -70,6 +72,7 @@ public final class BluetoothSap implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c3a5d1f871b..a68103f68d5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2494,7 +2494,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } @RequiresPermission(allOf = { @@ -2577,7 +2577,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } } -- GitLab From 93fcf85ab95cf2c8cfbde41f6745ba0d9d328058 Mon Sep 17 00:00:00 2001 From: Hui Yu Date: Tue, 13 Apr 2021 21:43:22 -0700 Subject: [PATCH 1272/1408] Temp allowlist bluetooth broadcast to start FGS for 10 seconds. Add an overloaded version of Context.sendBroadcastMultiplePermissions() that can specify BroadcastOptions, it is called by com.android.bluetooth package. Bug: 182816627 Test: atest AdapterServiceTest Test: atest AvrcpControllerStateMachineTest Test: atest BondStateMachineTest Test: atest MapClientStateMachineTest Test: atest RemoteDevicesTest Change-Id: I8bb2d2ed98ece70ebbe9d3a1b549b966d690de4f --- .../bluetooth/BluetoothManagerService.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 08c9c2a2c97..84e675e7f35 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -20,14 +20,17 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; @@ -62,6 +65,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerExemptionManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -2497,7 +2501,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions()); } @RequiresPermission(allOf = { @@ -2580,7 +2584,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, + getTempAllowlistBroadcastOptions()); } } @@ -2890,4 +2895,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } return permissionCheckResult == PERMISSION_GRANTED; } + + static @NonNull Bundle getTempAllowlistBroadcastOptions() { + final long duration = 10_000; + final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, ""); + return bOptions.toBundle(); + } } -- GitLab From 4e4c9c4796c2c16d32e87417e084a1f724e9f258 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 22 Apr 2021 12:21:46 -0600 Subject: [PATCH 1273/1408] Refinement of AttributionSource handling. Previous CLs had started passing AttributionSource values across Binder calls inside BluetoothDevice instances, but this can cause confuse the permission check logic in the future; we should instead always aim to use the AttributionSource closest to the app making the call, instead of parceling it. This change also improves logging to highlight when we're quietly treating a permission as denied, and when a UID is mismatched. Bug: 186106084 Test: atest BluetoothInstrumentationTests Change-Id: I5d3fdb3c573cb9e77474952d8680caa4c4c464eb --- .../android/bluetooth/BluetoothAdapter.java | 104 +++++++++--------- .../android/bluetooth/BluetoothDevice.java | 67 +++++------ .../android/bluetooth/BluetoothManager.java | 28 +---- 3 files changed, 87 insertions(+), 112 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5704c6513e9..2426ead8105 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -53,7 +53,6 @@ import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; -import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -75,6 +74,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; @@ -713,7 +713,6 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; @UnsupportedAppUsage private IBluetooth mService; - private Context mContext; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); private final Object mLock = new Object(); @@ -723,6 +722,8 @@ public final class BluetoothAdapter { private final Map mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); + private AttributionSource mAttributionSource; + /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. @@ -762,17 +763,22 @@ public final class BluetoothAdapter { @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b); - sAdapter = new BluetoothAdapter(managerService); - } else { - Log.e(TAG, "Bluetooth binder is null"); - } + sAdapter = createAdapter(); } return sAdapter; } + /** {@hide} */ + public static BluetoothAdapter createAdapter() { + IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); + if (binder != null) { + return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder)); + } else { + Log.e(TAG, "Bluetooth binder is null"); + return null; + } + } + /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ @@ -793,6 +799,15 @@ public final class BluetoothAdapter { mToken = new Binder(); } + void setAttributionSource(AttributionSource attributionSource) { + mAttributionSource = attributionSource; + } + + private AttributionSource resolveAttributionSource() { + return (mAttributionSource != null) ? mAttributionSource + : ActivityThread.currentAttributionSource(); + } + /** * Get a {@link BluetoothDevice} object for the given Bluetooth hardware * address. @@ -807,7 +822,9 @@ public final class BluetoothAdapter { */ @RequiresNoPermission public BluetoothDevice getRemoteDevice(String address) { - return new BluetoothDevice(address, getAttributionSource()); + final BluetoothDevice res = new BluetoothDevice(address); + res.setAttributionSource(mAttributionSource); + return res; } /** @@ -826,9 +843,11 @@ public final class BluetoothAdapter { if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } - return new BluetoothDevice( + final BluetoothDevice res = new BluetoothDevice( String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], - address[2], address[3], address[4], address[5]), getAttributionSource()); + address[2], address[3], address[4], address[5])); + res.setAttributionSource(mAttributionSource); + return res; } /** @@ -891,7 +910,7 @@ public final class BluetoothAdapter { synchronized (mLock) { if (sBluetoothLeScanner == null) { sBluetoothLeScanner = - new BluetoothLeScanner(mManagerService, getAttributionSource()); + new BluetoothLeScanner(mManagerService, resolveAttributionSource()); } } return sBluetoothLeScanner; @@ -1330,7 +1349,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getUuids(getAttributionSource()); + return mService.getUuids(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1364,7 +1383,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setName(name, getAttributionSource()); + return mService.setName(name, resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1392,7 +1411,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getBluetoothClass(getAttributionSource()); + return mService.getBluetoothClass(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1448,7 +1467,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(getAttributionSource()); + if (mService != null) return mService.getIoCapability(resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1501,7 +1520,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(getAttributionSource()); + if (mService != null) return mService.getLeIoCapability(resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1563,7 +1582,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getScanMode(getAttributionSource()); + return mService.getScanMode(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1612,7 +1631,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds, getAttributionSource()); + return mService.setScanMode(mode, durationSeconds, resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1662,7 +1681,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout(), getAttributionSource()); + return mService.setScanMode(mode, getDiscoverableTimeout(), resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1683,7 +1702,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoverableTimeout(getAttributionSource()); + return mService.getDiscoverableTimeout(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1704,7 +1723,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.setDiscoverableTimeout(timeout, getAttributionSource()); + mService.setDiscoverableTimeout(timeout, resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1737,31 +1756,6 @@ public final class BluetoothAdapter { return -1; } - /** - * Set the context for this BluetoothAdapter (only called from BluetoothManager) - * @hide - */ - @RequiresNoPermission - public void setContext(Context context) { - mContext = context; - } - - String getOpPackageName() { - // Workaround for legacy API for getting a BluetoothAdapter not - // passing a context - if (mContext != null) { - return mContext.getOpPackageName(); - } - return ActivityThread.currentOpPackageName(); - } - - AttributionSource getAttributionSource() { - if (mContext != null) { - return mContext.getAttributionSource(); - } - return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(), null); - } - /** * Start the remote device discovery process. *

    The discovery process usually involves an inquiry scan of about 12 @@ -1802,7 +1796,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(getAttributionSource()); + return mService.startDiscovery(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1838,7 +1832,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.cancelDiscovery(getAttributionSource()); + return mService.cancelDiscovery(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1876,7 +1870,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isDiscovering(getAttributionSource()); + return mService.isDiscovering(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2313,7 +2307,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMaxConnectedAudioDevices(getAttributionSource()); + return mService.getMaxConnectedAudioDevices(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); @@ -2425,7 +2419,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(getAttributionSource()); + return mService.getMostRecentlyConnectedDevices(resolveAttributionSource()); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2455,7 +2449,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices(getAttributionSource())); + return toDeviceSet(mService.getBondedDevices(resolveAttributionSource())); } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { @@ -3270,7 +3264,7 @@ public final class BluetoothAdapter { /** * Provides callback methods for receiving {@link OobData} from the host stack, as well as an - * error interface in order to allow the caller to determine next steps based on the {@link + * error interface in order to allow the caller to determine next steps based on the {@code * ErrorCode}. * * @hide diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index befb1ca4e58..cc2ba9b8abb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -24,6 +24,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; @@ -1102,11 +1103,11 @@ public final class BluetoothDevice implements Parcelable { */ private static volatile IBluetooth sService; - private final AttributionSource mAttributionSource; - private final String mAddress; @AddressType private final int mAddressType; + private AttributionSource mAttributionSource; + /*package*/ @UnsupportedAppUsage static IBluetooth getService() { @@ -1157,7 +1158,8 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException address is invalid * @hide */ - public BluetoothDevice(String address, AttributionSource attributionSource) { + @UnsupportedAppUsage + /*package*/ BluetoothDevice(String address) { getService(); // ensures sService is initialized if (!BluetoothAdapter.checkBluetoothAddress(address)) { throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); @@ -1165,12 +1167,15 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; + } + + void setAttributionSource(AttributionSource attributionSource) { mAttributionSource = attributionSource; } - @UnsupportedAppUsage - /*package*/ BluetoothDevice(String address) { - this(address, BluetoothAdapter.getDefaultAdapter().getAttributionSource()); + private AttributionSource resolveAttributionSource() { + return (mAttributionSource != null) ? mAttributionSource + : ActivityThread.currentAttributionSource(); } @Override @@ -1208,8 +1213,7 @@ public final class BluetoothDevice implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothDevice createFromParcel(Parcel in) { - return new BluetoothDevice( - in.readString(), in.readParcelable(getClass().getClassLoader())); + return new BluetoothDevice(in.readString()); } public BluetoothDevice[] newArray(int size) { @@ -1220,7 +1224,6 @@ public final class BluetoothDevice implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); - out.writeParcelable(mAttributionSource, 0); } /** @@ -1265,7 +1268,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String name = service.getRemoteName(this, mAttributionSource); + String name = service.getRemoteName(this, resolveAttributionSource()); if (name != null) { // remove whitespace characters from the name return name @@ -1296,7 +1299,7 @@ public final class BluetoothDevice implements Parcelable { return DEVICE_TYPE_UNKNOWN; } try { - return service.getRemoteType(this, mAttributionSource); + return service.getRemoteType(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1320,7 +1323,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource); + String alias = service.getRemoteAliasWithAttribution(this, resolveAttributionSource()); if (alias == null) { return getName(); } @@ -1361,9 +1364,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - return service.setRemoteAlias( - this, alias, adapter.getOpPackageName(), mAttributionSource); + return service.setRemoteAlias(this, alias, + resolveAttributionSource().getPackageName(), + resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1389,7 +1392,7 @@ public final class BluetoothDevice implements Parcelable { return BATTERY_LEVEL_BLUETOOTH_OFF; } try { - return service.getBatteryLevel(this, mAttributionSource); + return service.getBatteryLevel(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1480,7 +1483,7 @@ public final class BluetoothDevice implements Parcelable { } try { return service.createBond( - this, transport, remoteP192Data, remoteP256Data, mAttributionSource); + this, transport, remoteP192Data, remoteP256Data, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1505,7 +1508,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.isBondingInitiatedLocally(this, mAttributionSource); + return service.isBondingInitiatedLocally(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1530,7 +1533,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "cancelBondProcess() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.cancelBondProcess(this, mAttributionSource); + return service.cancelBondProcess(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1558,7 +1561,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "removeBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.removeBond(this, mAttributionSource); + return service.removeBond(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1574,7 +1577,7 @@ public final class BluetoothDevice implements Parcelable { @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(BluetoothDevice query) { try { - return sService.getBondState(query, mAttributionSource); + return sService.getBondState(query, resolveAttributionSource()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1664,7 +1667,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, mAttributionSource) + return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1690,7 +1693,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, mAttributionSource) + return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1713,7 +1716,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - int classInt = service.getRemoteClass(this, mAttributionSource); + int classInt = service.getRemoteClass(this, resolveAttributionSource()); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) { @@ -1742,7 +1745,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteUuids(this, mAttributionSource); + return service.getRemoteUuids(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1772,7 +1775,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); + return service.fetchRemoteUuidsWithAttribution(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1809,7 +1812,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.sdpSearch(this, uuid, mAttributionSource); + return service.sdpSearch(this, uuid, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1831,7 +1834,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setPin(this, true, pin.length, pin, mAttributionSource); + return service.setPin(this, true, pin.length, pin, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1894,7 +1897,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.cancelBondProcess(this, mAttributionSource); + return service.cancelBondProcess(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1927,7 +1930,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getPhonebookAccessPermission(this, mAttributionSource); + return service.getPhonebookAccessPermission(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2033,7 +2036,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getMessageAccessPermission(this, mAttributionSource); + return service.getMessageAccessPermission(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2084,7 +2087,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getSimAccessPermission(this, mAttributionSource); + return service.getSimAccessPermission(this, resolveAttributionSource()); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 2374f1cdc52..69f9a79c738 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -16,19 +16,15 @@ package android.bluetooth; -import android.Manifest; import android.annotation.RequiresFeature; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.annotation.SystemService; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; import android.content.pm.PackageManager; -import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.util.ArrayList; @@ -66,27 +62,9 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - if (context.getAttributionTag() == null) { - context = context.getApplicationContext(); - if (context == null) { - throw new IllegalArgumentException( - "context not associated with any application (using a mock context?)"); - } - - mAdapter = BluetoothAdapter.getDefaultAdapter(); - } else { - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - mAdapter = new BluetoothAdapter(IBluetoothManager.Stub.asInterface(b)); - } else { - Log.e(TAG, "Bluetooth binder is null"); - mAdapter = null; - } - } - - // Context is not initialized in constructor - if (mAdapter != null) { - mAdapter.setContext(context); + mAdapter = BluetoothAdapter.createAdapter(); + if (context != null) { + mAdapter.setAttributionSource(context.getAttributionSource()); } } -- GitLab From 4dabcb764f1948823dcd74eefb3440afcab07db2 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Thu, 22 Apr 2021 19:05:17 +0100 Subject: [PATCH 1274/1408] Pass attribution source to BT APIs. This adds attribution source to BT method calls. This is now required to allow the app ops for the new BT permissions (BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE, and BLUETOOTH_SCAN) to be noted. Bug: 183626112 Test: atest BluetoothInstrumentationTests Change-Id: I81598553b762e491d6364064a2e1ef41dec89bf9 --- .../android/bluetooth/BluetoothAdapter.java | 100 +++++++++--------- .../android/bluetooth/BluetoothDevice.java | 53 +++++----- .../java/android/bluetooth/BluetoothGatt.java | 73 +++++++------ .../bluetooth/BluetoothGattServer.java | 30 +++--- .../android/bluetooth/BluetoothManager.java | 19 ++-- .../android/bluetooth/le/AdvertisingSet.java | 23 ++-- .../bluetooth/le/BluetoothLeAdvertiser.java | 22 ++-- .../bluetooth/le/BluetoothLeScanner.java | 30 +++--- .../le/PeriodicAdvertisingManager.java | 17 +-- .../bluetooth/BluetoothManagerService.java | 2 +- 10 files changed, 200 insertions(+), 169 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2426ead8105..4ef37379a55 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -706,11 +706,13 @@ public final class BluetoothAdapter { */ private static BluetoothAdapter sAdapter; - private static BluetoothLeScanner sBluetoothLeScanner; - private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; - private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; + private BluetoothLeScanner mBluetoothLeScanner; + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + private PeriodicAdvertisingManager mPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; + private final AttributionSource mAttributionSource; + @UnsupportedAppUsage private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -722,8 +724,6 @@ public final class BluetoothAdapter { private final Map mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); - private AttributionSource mAttributionSource; - /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. @@ -763,16 +763,17 @@ public final class BluetoothAdapter { @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - sAdapter = createAdapter(); + sAdapter = createAdapter(ActivityThread.currentAttributionSource()); } return sAdapter; } /** {@hide} */ - public static BluetoothAdapter createAdapter() { + public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); if (binder != null) { - return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder)); + return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), + attributionSource); } else { Log.e(TAG, "Bluetooth binder is null"); return null; @@ -782,7 +783,7 @@ public final class BluetoothAdapter { /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ - BluetoothAdapter(IBluetoothManager managerService) { + BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { if (managerService == null) { throw new IllegalArgumentException("bluetooth manager service is null"); } @@ -794,20 +795,12 @@ public final class BluetoothAdapter { } finally { mServiceLock.writeLock().unlock(); } - mManagerService = managerService; + mManagerService = Objects.requireNonNull(managerService); + mAttributionSource = Objects.requireNonNull(attributionSource); mLeScanClients = new HashMap(); mToken = new Binder(); } - void setAttributionSource(AttributionSource attributionSource) { - mAttributionSource = attributionSource; - } - - private AttributionSource resolveAttributionSource() { - return (mAttributionSource != null) ? mAttributionSource - : ActivityThread.currentAttributionSource(); - } - /** * Get a {@link BluetoothDevice} object for the given Bluetooth hardware * address. @@ -864,11 +857,11 @@ public final class BluetoothAdapter { return null; } synchronized (mLock) { - if (sBluetoothLeAdvertiser == null) { - sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); + if (mBluetoothLeAdvertiser == null) { + mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this); } + return mBluetoothLeAdvertiser; } - return sBluetoothLeAdvertiser; } /** @@ -892,11 +885,11 @@ public final class BluetoothAdapter { } synchronized (mLock) { - if (sPeriodicAdvertisingManager == null) { - sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); + if (mPeriodicAdvertisingManager == null) { + mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this); } + return mPeriodicAdvertisingManager; } - return sPeriodicAdvertisingManager; } /** @@ -908,12 +901,11 @@ public final class BluetoothAdapter { return null; } synchronized (mLock) { - if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = - new BluetoothLeScanner(mManagerService, resolveAttributionSource()); + if (mBluetoothLeScanner == null) { + mBluetoothLeScanner = new BluetoothLeScanner(this); } + return mBluetoothLeScanner; } - return sBluetoothLeScanner; } /** @@ -1349,7 +1341,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getUuids(resolveAttributionSource()); + return mService.getUuids(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1383,7 +1375,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setName(name, resolveAttributionSource()); + return mService.setName(name, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1411,7 +1403,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getBluetoothClass(resolveAttributionSource()); + return mService.getBluetoothClass(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1467,7 +1459,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(resolveAttributionSource()); + if (mService != null) return mService.getIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1520,7 +1512,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(resolveAttributionSource()); + if (mService != null) return mService.getLeIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1582,7 +1574,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getScanMode(resolveAttributionSource()); + return mService.getScanMode(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1631,7 +1623,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds, resolveAttributionSource()); + return mService.setScanMode(mode, durationSeconds, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1681,7 +1673,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout(), resolveAttributionSource()); + return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1702,7 +1694,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoverableTimeout(resolveAttributionSource()); + return mService.getDiscoverableTimeout(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1723,7 +1715,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.setDiscoverableTimeout(timeout, resolveAttributionSource()); + mService.setDiscoverableTimeout(timeout, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1796,7 +1788,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(resolveAttributionSource()); + return mService.startDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1832,7 +1824,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.cancelDiscovery(resolveAttributionSource()); + return mService.cancelDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1870,7 +1862,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isDiscovering(resolveAttributionSource()); + return mService.isDiscovering(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2307,7 +2299,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMaxConnectedAudioDevices(resolveAttributionSource()); + return mService.getMaxConnectedAudioDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); @@ -2335,7 +2327,7 @@ public final class BluetoothAdapter { // BLE is not supported return false; } - return (iGatt.numHwTrackFiltersAvailable() != 0); + return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2419,7 +2411,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(resolveAttributionSource()); + return mService.getMostRecentlyConnectedDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2449,7 +2441,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices(resolveAttributionSource())); + return toDeviceSet(mService.getBondedDevices(mAttributionSource)); } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { @@ -3171,11 +3163,11 @@ public final class BluetoothAdapter { if (mLeScanClients != null) { mLeScanClients.clear(); } - if (sBluetoothLeAdvertiser != null) { - sBluetoothLeAdvertiser.cleanup(); + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.cleanup(); } - if (sBluetoothLeScanner != null) { - sBluetoothLeScanner.cleanup(); + if (mBluetoothLeScanner != null) { + mBluetoothLeScanner.cleanup(); } } finally { mServiceLock.writeLock().unlock(); @@ -3514,11 +3506,17 @@ public final class BluetoothAdapter { && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } + /** {@hide} */ @UnsupportedAppUsage - /*package*/ IBluetoothManager getBluetoothManager() { + public IBluetoothManager getBluetoothManager() { return mManagerService; } + /** {@hide} */ + public AttributionSource getAttributionSource() { + return mAttributionSource; + } + private final ArrayList mProxyServiceStateCallbacks = new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index cc2ba9b8abb..5eca947931d 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1167,17 +1167,13 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; + mAttributionSource = ActivityThread.currentAttributionSource(); } void setAttributionSource(AttributionSource attributionSource) { mAttributionSource = attributionSource; } - private AttributionSource resolveAttributionSource() { - return (mAttributionSource != null) ? mAttributionSource - : ActivityThread.currentAttributionSource(); - } - @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { @@ -1268,7 +1264,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String name = service.getRemoteName(this, resolveAttributionSource()); + String name = service.getRemoteName(this, mAttributionSource); if (name != null) { // remove whitespace characters from the name return name @@ -1299,7 +1295,7 @@ public final class BluetoothDevice implements Parcelable { return DEVICE_TYPE_UNKNOWN; } try { - return service.getRemoteType(this, resolveAttributionSource()); + return service.getRemoteType(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1323,7 +1319,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String alias = service.getRemoteAliasWithAttribution(this, resolveAttributionSource()); + String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource); if (alias == null) { return getName(); } @@ -1365,8 +1361,8 @@ public final class BluetoothDevice implements Parcelable { } try { return service.setRemoteAlias(this, alias, - resolveAttributionSource().getPackageName(), - resolveAttributionSource()); + mAttributionSource.getPackageName(), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1392,7 +1388,7 @@ public final class BluetoothDevice implements Parcelable { return BATTERY_LEVEL_BLUETOOTH_OFF; } try { - return service.getBatteryLevel(this, resolveAttributionSource()); + return service.getBatteryLevel(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1483,7 +1479,7 @@ public final class BluetoothDevice implements Parcelable { } try { return service.createBond( - this, transport, remoteP192Data, remoteP256Data, resolveAttributionSource()); + this, transport, remoteP192Data, remoteP256Data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1508,7 +1504,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.isBondingInitiatedLocally(this, resolveAttributionSource()); + return service.isBondingInitiatedLocally(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1533,7 +1529,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "cancelBondProcess() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.cancelBondProcess(this, resolveAttributionSource()); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1561,7 +1557,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "removeBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.removeBond(this, resolveAttributionSource()); + return service.removeBond(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1577,7 +1573,7 @@ public final class BluetoothDevice implements Parcelable { @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(BluetoothDevice query) { try { - return sService.getBondState(query, resolveAttributionSource()); + return sService.getBondState(query, mAttributionSource); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1667,7 +1663,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) + return service.getConnectionStateWithAttribution(this, mAttributionSource) != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1693,7 +1689,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) + return service.getConnectionStateWithAttribution(this, mAttributionSource) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1716,7 +1712,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - int classInt = service.getRemoteClass(this, resolveAttributionSource()); + int classInt = service.getRemoteClass(this, mAttributionSource); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) { @@ -1745,7 +1741,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteUuids(this, resolveAttributionSource()); + return service.getRemoteUuids(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1775,7 +1771,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.fetchRemoteUuidsWithAttribution(this, resolveAttributionSource()); + return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1812,7 +1808,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.sdpSearch(this, uuid, resolveAttributionSource()); + return service.sdpSearch(this, uuid, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1834,7 +1830,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setPin(this, true, pin.length, pin, resolveAttributionSource()); + return service.setPin(this, true, pin.length, pin, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1897,7 +1893,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.cancelBondProcess(this, resolveAttributionSource()); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1930,7 +1926,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getPhonebookAccessPermission(this, resolveAttributionSource()); + return service.getPhonebookAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2036,7 +2032,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getMessageAccessPermission(this, resolveAttributionSource()); + return service.getMessageAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2087,7 +2083,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getSimAccessPermission(this, resolveAttributionSource()); + return service.getSimAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2516,7 +2512,8 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy); + BluetoothGatt gatt = new BluetoothGatt( + iGatt, this, transport, opportunistic, phy, mAttributionSource); gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 9d3eed8f6fa..aea82102ca3 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; @@ -69,6 +70,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mTransport; private int mPhy; private boolean mOpportunistic; + private final AttributionSource mAttributionSource; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -198,7 +200,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.clientConnect(mClientIf, mDevice.getAddress(), !mAutoConnect, mTransport, mOpportunistic, - mPhy); // autoConnect is inverse of "isDirect" + mPhy, mAttributionSource); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -376,7 +378,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readCharacteristic(mClientIf, address, handle, authReq); + mService.readCharacteristic( + mClientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -439,7 +442,7 @@ public final class BluetoothGatt implements BluetoothProfile { ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), authReq, - characteristic.getValue()); + characteristic.getValue(), mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -521,7 +524,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readDescriptor(mClientIf, address, handle, authReq); + mService.readDescriptor( + mClientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -573,7 +577,7 @@ public final class BluetoothGatt implements BluetoothProfile { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, - authReq, descriptor.getValue()); + authReq, descriptor.getValue(), mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -726,13 +730,14 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport, boolean opportunistic, int phy) { + /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, + boolean opportunistic, int phy, AttributionSource attributionSource) { mService = iGatt; mDevice = device; mTransport = transport; mPhy = phy; mOpportunistic = opportunistic; + mAttributionSource = attributionSource; mServices = new ArrayList(); mConnState = CONN_STATE_IDLE; @@ -867,7 +872,8 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support); + mService.registerClient( + new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -888,7 +894,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback = null; - mService.unregisterClient(mClientIf); + mService.unregisterClient(mClientIf, mAttributionSource); mClientIf = 0; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -958,7 +964,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return; try { - mService.clientDisconnect(mClientIf, mDevice.getAddress()); + mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -977,8 +983,9 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect() { try { + // autoConnect is inverse of "isDirect" mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, - mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" + mOpportunistic, mPhy, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1009,7 +1016,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { try { mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1023,7 +1030,7 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy() { try { - mService.clientReadPhy(mClientIf, mDevice.getAddress()); + mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1060,7 +1067,7 @@ public final class BluetoothGatt implements BluetoothProfile { mServices.clear(); try { - mService.discoverServices(mClientIf, mDevice.getAddress()); + mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1087,7 +1094,8 @@ public final class BluetoothGatt implements BluetoothProfile { mServices.clear(); try { - mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid)); + mService.discoverServiceByUuid( + mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1179,7 +1187,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), AUTHENTICATION_NONE); + characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1214,7 +1222,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), - new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); + new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1262,7 +1271,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeCharacteristic(mClientIf, device.getAddress(), characteristic.getInstanceId(), characteristic.getWriteType(), - AUTHENTICATION_NONE, characteristic.getValue()); + AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1305,7 +1314,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readDescriptor(mClientIf, device.getAddress(), - descriptor.getInstanceId(), AUTHENTICATION_NONE); + descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1347,7 +1356,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - AUTHENTICATION_NONE, descriptor.getValue()); + AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1383,7 +1392,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.beginReliableWrite(mClientIf, mDevice.getAddress()); + mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1416,7 +1425,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1440,7 +1449,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return; try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1487,7 +1496,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification(mClientIf, device.getAddress(), - characteristic.getInstanceId(), enable); + characteristic.getInstanceId(), enable, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1510,7 +1519,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.refreshDevice(mClientIf, mDevice.getAddress()); + mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1535,7 +1544,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.readRemoteRssi(mClientIf, mDevice.getAddress()); + mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1567,7 +1576,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); + mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1599,7 +1608,8 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority); + mService.connectionParameterUpdate( + mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1633,9 +1643,10 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), - minConnectionInterval, maxConnectionInterval, - slaveLatency, supervisionTimeout, - minConnectionEventLen, maxConnectionEventLen); + minConnectionInterval, maxConnectionInterval, + slaveLatency, supervisionTimeout, + minConnectionEventLen, maxConnectionEventLen, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 865f476e786..e28006aac3b 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -48,6 +49,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; + private final AttributionSource mAttributionSource; private Object mServerIfLock = new Object(); private int mServerIf; @@ -382,13 +384,15 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) { + /*package*/ BluetoothGattServer( + IBluetoothGatt iGatt, int transport, AttributionSource attributionSource) { mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); mCallback = null; mServerIf = 0; mTransport = transport; mServices = new ArrayList(); + mAttributionSource = attributionSource; } /** @@ -488,7 +492,8 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback = callback; try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support); + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, + eatt_support, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mCallback = null; @@ -522,7 +527,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback = null; - mService.unregisterServer(mServerIf); + mService.unregisterServer(mServerIf, mAttributionSource); mServerIf = 0; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -576,7 +581,8 @@ public final class BluetoothGattServer implements BluetoothProfile { try { // autoConnect is inverse of "isDirect" - mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport); + mService.serverConnect( + mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -599,7 +605,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return; try { - mService.serverDisconnect(mServerIf, device.getAddress()); + mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -628,7 +634,7 @@ public final class BluetoothGattServer implements BluetoothProfile { public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { try { mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -644,7 +650,7 @@ public final class BluetoothGattServer implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(BluetoothDevice device) { try { - mService.serverReadPhy(mServerIf, device.getAddress()); + mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -679,7 +685,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.sendResponse(mServerIf, device.getAddress(), requestId, - status, offset, value); + status, offset, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -722,7 +728,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.sendNotification(mServerIf, device.getAddress(), characteristic.getInstanceId(), confirm, - characteristic.getValue()); + characteristic.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -757,7 +763,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mPendingService = service; try { - mService.addService(mServerIf, service); + mService.addService(mServerIf, service, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -784,7 +790,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (intService == null) return false; try { - mService.removeService(mServerIf, service.getInstanceId()); + mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource); mServices.remove(intService); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -805,7 +811,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return; try { - mService.clearServices(mServerIf); + mService.clearServices(mServerIf, mAttributionSource); mServices.clear(); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 69f9a79c738..07af8dbd234 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -20,8 +20,10 @@ import android.annotation.RequiresFeature; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.app.ActivityThread; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; @@ -29,6 +31,7 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; /** * High level manager used to obtain an instance of an {@link BluetoothAdapter} @@ -56,16 +59,16 @@ public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = false; + private final AttributionSource mAttributionSource; private final BluetoothAdapter mAdapter; /** * @hide */ public BluetoothManager(Context context) { - mAdapter = BluetoothAdapter.createAdapter(); - if (context != null) { - mAdapter.setAttributionSource(context.getAttributionSource()); - } + mAttributionSource = (context != null) ? context.getAttributionSource() + : ActivityThread.currentAttributionSource(); + mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); } /** @@ -138,7 +141,7 @@ public final class BluetoothManager { if (iGatt == null) return connectedDevices; connectedDevices = iGatt.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED}); + new int[]{BluetoothProfile.STATE_CONNECTED}, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -180,7 +183,8 @@ public final class BluetoothManager { IBluetoothManager managerService = mAdapter.getBluetoothManager(); IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; - devices = iGatt.getDevicesMatchingConnectionStates(states); + devices = iGatt.getDevicesMatchingConnectionStates( + states, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -283,7 +287,8 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); + BluetoothGattServer mGattServer = + new BluetoothGattServer(iGatt, transport, mAttributionSource); Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index d7e48ca543c..caa91fb2391 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -18,12 +18,12 @@ package android.bluetooth.le; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.RemoteException; import android.util.Log; @@ -40,11 +40,12 @@ public final class AdvertisingSet { private final IBluetoothGatt mGatt; private int mAdvertiserId; + private AttributionSource mAttributionSource; - /* package */ AdvertisingSet(int advertiserId, - IBluetoothManager bluetoothManager) { + /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager, + AttributionSource attributionSource) { mAdvertiserId = advertiserId; - + mAttributionSource = attributionSource; try { mGatt = bluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { @@ -75,7 +76,7 @@ public final class AdvertisingSet { int maxExtendedAdvertisingEvents) { try { mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration, - maxExtendedAdvertisingEvents); + maxExtendedAdvertisingEvents, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -98,7 +99,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingData(AdvertiseData advertiseData) { try { - mGatt.setAdvertisingData(mAdvertiserId, advertiseData); + mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -118,7 +119,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setScanResponseData(AdvertiseData scanResponse) { try { - mGatt.setScanResponseData(mAdvertiserId, scanResponse); + mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -136,7 +137,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { - mGatt.setAdvertisingParameters(mAdvertiserId, parameters); + mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -152,7 +153,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { try { - mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters); + mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -173,7 +174,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { - mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData); + mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -191,7 +192,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingEnabled(boolean enable) { try { - mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable); + mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ff279d85987..58029745ab9 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -26,6 +26,7 @@ import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -35,6 +36,7 @@ import android.util.Log; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * This class provides a way to perform Bluetooth LE advertise operations, such as starting and @@ -58,9 +60,11 @@ public final class BluetoothLeAdvertiser { private static final int FLAGS_FIELD_BYTES = 3; private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; + private final AttributionSource mAttributionSource; + private final Handler mHandler; - private BluetoothAdapter mBluetoothAdapter; private final Map mLegacyAdvertisers = new HashMap<>(); private final Map @@ -74,9 +78,10 @@ public final class BluetoothLeAdvertiser { * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management * @hide */ - public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) { + mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); + mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); + mAttributionSource = mBluetoothAdapter.getAttributionSource(); mHandler = new Handler(Looper.getMainLooper()); } @@ -453,7 +458,8 @@ public final class BluetoothLeAdvertiser { try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, wrapped); + periodicData, duration, maxExtendedAdvertisingEvents, wrapped, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); postStartSetFailure(handler, callback, @@ -482,7 +488,7 @@ public final class BluetoothLeAdvertiser { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopAdvertisingSet(wrapped); + gatt.stopAdvertisingSet(wrapped, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising - ", e); } @@ -600,8 +606,8 @@ public final class BluetoothLeAdvertiser { return; } - AdvertisingSet advertisingSet = - new AdvertisingSet(advertiserId, mBluetoothManager); + AdvertisingSet advertisingSet = new AdvertisingSet( + advertiserId, mBluetoothManager, mAttributionSource); mAdvertisingSets.put(advertiserId, advertisingSet); callback.onAdvertisingSetStarted(advertisingSet, txPower, status); } diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index f27f22b9af5..60d4e2d6d2d 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An @@ -80,11 +81,12 @@ public final class BluetoothLeScanner { */ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; + private final AttributionSource mAttributionSource; + private final Handler mHandler; - private BluetoothAdapter mBluetoothAdapter; private final Map mLeScanClients; - private final AttributionSource mAttributionSource; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. @@ -94,13 +96,12 @@ public final class BluetoothLeScanner { * @param featureId The featureId of the context this object was created from * @hide */ - public BluetoothLeScanner(IBluetoothManager bluetoothManager, - @NonNull AttributionSource attributionSource) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) { + mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); + mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); + mAttributionSource = mBluetoothAdapter.getAttributionSource(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap(); - mAttributionSource = attributionSource; } /** @@ -276,7 +277,8 @@ public final class BluetoothLeScanner { wrapper.startRegistration(); } else { try { - gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource); + gatt.startScanForIntent(callbackIntent, settings, filters, + mAttributionSource); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } @@ -321,7 +323,7 @@ public final class BluetoothLeScanner { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent); + gatt.stopScanForIntent(callbackIntent, mAttributionSource); } catch (RemoteException e) { } } @@ -420,7 +422,7 @@ public final class BluetoothLeScanner { // Scan stopped. if (mScannerId == -1 || mScannerId == -2) return; try { - mBluetoothGatt.registerScanner(this, mWorkSource); + mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "application registeration exception", e); @@ -450,8 +452,8 @@ public final class BluetoothLeScanner { return; } try { - mBluetoothGatt.stopScan(mScannerId); - mBluetoothGatt.unregisterScanner(mScannerId); + mBluetoothGatt.stopScan(mScannerId, mAttributionSource); + mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to stop scan and unregister", e); } @@ -467,7 +469,7 @@ public final class BluetoothLeScanner { return; } try { - mBluetoothGatt.flushPendingBatchResults(mScannerId); + mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to get pending scan results", e); } @@ -486,7 +488,7 @@ public final class BluetoothLeScanner { try { if (mScannerId == -1) { // Registration succeeds after timeout, unregister scanner. - mBluetoothGatt.unregisterScanner(scannerId); + mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource); } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 26978e39807..47f47bb8e36 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -25,6 +25,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -32,6 +33,7 @@ import android.util.Log; import java.util.IdentityHashMap; import java.util.Map; +import java.util.Objects; /** * This class provides methods to perform periodic advertising related @@ -54,8 +56,9 @@ public final class PeriodicAdvertisingManager { private static final int SYNC_STARTING = -1; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; - private BluetoothAdapter mBluetoothAdapter; + private final AttributionSource mAttributionSource; /* maps callback, to callback wrapper and sync handle */ Map(); } @@ -166,7 +170,8 @@ public final class PeriodicAdvertisingManager { mCallbackWrappers.put(callback, wrapped); try { - gatt.registerSync(scanResult, skip, timeout, wrapped); + gatt.registerSync( + scanResult, skip, timeout, wrapped, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to register sync - ", e); return; @@ -202,7 +207,7 @@ public final class PeriodicAdvertisingManager { } try { - gatt.unregisterSync(wrapper); + gatt.unregisterSync(wrapper, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel sync creation - ", e); return; diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 08c9c2a2c97..583e54b5781 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1099,7 +1099,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBleAppPresent()) { // Need to stay at BLE ON. Disconnect all Gatt connections try { - mBluetoothGatt.unregAll(); + mBluetoothGatt.unregAll(mContext.getAttributionSource()); } catch (RemoteException e) { Slog.e(TAG, "Unable to disconnect all apps.", e); } -- GitLab From f9e176c3dcafb82f251a123751578539e3484deb Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 22 Apr 2021 16:01:29 -0600 Subject: [PATCH 1275/1408] 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/BluetoothA2dp.java | 19 +++--- .../android/bluetooth/BluetoothA2dpSink.java | 16 +++-- .../android/bluetooth/BluetoothAdapter.java | 46 ++++++++------- .../bluetooth/BluetoothAvrcpController.java | 16 +++-- .../android/bluetooth/BluetoothDevice.java | 20 ++++++- .../bluetooth/BluetoothGattServer.java | 15 ++--- .../android/bluetooth/BluetoothHeadset.java | 15 +++-- .../bluetooth/BluetoothHeadsetClient.java | 16 +++-- .../bluetooth/BluetoothHearingAid.java | 16 +++-- .../android/bluetooth/BluetoothHidDevice.java | 15 +++-- .../android/bluetooth/BluetoothHidHost.java | 16 +++-- .../android/bluetooth/BluetoothLeAudio.java | 16 +++-- .../android/bluetooth/BluetoothManager.java | 59 +++++++++++-------- .../java/android/bluetooth/BluetoothMap.java | 23 ++++---- .../android/bluetooth/BluetoothMapClient.java | 22 +++---- .../java/android/bluetooth/BluetoothPan.java | 16 +++-- .../java/android/bluetooth/BluetoothPbap.java | 15 +++-- .../bluetooth/BluetoothPbapClient.java | 22 +++---- .../java/android/bluetooth/BluetoothSap.java | 26 ++++---- 19 files changed, 254 insertions(+), 155 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index c2718621a37..aa0bc3e1e35 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -23,13 +23,13 @@ import android.annotation.Nullable; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -265,7 +265,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", IBluetoothA2dp.class.getName()) { @@ -279,8 +280,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothA2dp(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -386,7 +389,8 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -407,7 +411,8 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index cbbf4c97969..0158e3c0d9e 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -27,6 +27,7 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -78,7 +79,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { @@ -92,8 +94,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothA2dpSink(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -198,7 +202,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -221,7 +226,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4ef37379a55..18e6356d825 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -752,18 +752,23 @@ public final class BluetoothAdapter { /** * Get a handle to the default local Bluetooth adapter. - *

    Currently Android only supports one Bluetooth adapter, but the API - * could be extended to support more. This will always return the default - * adapter. + *

    + * Currently Android only supports one Bluetooth adapter, but the API could + * be extended to support more. This will always return the default adapter. *

    * - * @return the default local adapter, or null if Bluetooth is not supported on this hardware - * platform + * @return the default local adapter, or null if Bluetooth is not supported + * on this hardware platform + * @deprecated this method will continue to work, but developers are + * strongly encouraged to migrate to using + * {@link BluetoothManager#getAdapter()}, since that approach + * enables support for {@link Context#createAttributionContext}. */ + @Deprecated @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - sAdapter = createAdapter(ActivityThread.currentAttributionSource()); + sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null)); } return sAdapter; } @@ -2966,50 +2971,51 @@ public final class BluetoothAdapter { } if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = new BluetoothHeadset(context, listener); + BluetoothHeadset headset = new BluetoothHeadset(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP) { - BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); + BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP_SINK) { - BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); + BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this); return true; } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { - BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); + BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_HOST) { - BluetoothHidHost iDev = new BluetoothHidHost(context, listener); + BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this); return true; } else if (profile == BluetoothProfile.PAN) { - BluetoothPan pan = new BluetoothPan(context, listener); + BluetoothPan pan = new BluetoothPan(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP) { - BluetoothPbap pbap = new BluetoothPbap(context, listener); + BluetoothPbap pbap = new BluetoothPbap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEALTH) { Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); return false; } else if (profile == BluetoothProfile.MAP) { - BluetoothMap map = new BluetoothMap(context, listener); + BluetoothMap map = new BluetoothMap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEADSET_CLIENT) { - BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); + BluetoothHeadsetClient headsetClient = + new BluetoothHeadsetClient(context, listener, this); return true; } else if (profile == BluetoothProfile.SAP) { - BluetoothSap sap = new BluetoothSap(context, listener); + BluetoothSap sap = new BluetoothSap(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP_CLIENT) { - BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); + BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.MAP_CLIENT) { - BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); + BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_DEVICE) { - BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); return true; } else if (profile == BluetoothProfile.HEARING_AID) { if (isHearingAidProfileSupported()) { - BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); return true; } return false; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index cac676d7dc9..be3d5ee9ef1 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -86,7 +87,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { @@ -101,8 +103,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothAvrcpController(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -131,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -153,7 +158,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 5eca947931d..c700a10f3a0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -24,7 +24,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; @@ -48,6 +47,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.UUID; /** @@ -1167,13 +1167,29 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; - mAttributionSource = ActivityThread.currentAttributionSource(); + mAttributionSource = BluetoothManager.resolveAttributionSource(null); } void setAttributionSource(AttributionSource attributionSource) { mAttributionSource = attributionSource; } + static BluetoothDevice setAttributionSource(BluetoothDevice device, + AttributionSource attributionSource) { + device.setAttributionSource(attributionSource); + return device; + } + + static List setAttributionSource(List devices, + AttributionSource attributionSource) { + if (devices != null) { + for (BluetoothDevice device : devices) { + device.setAttributionSource(attributionSource); + } + } + return devices; + } + @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index e28006aac3b..3e799defa5e 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -46,11 +46,12 @@ public final class BluetoothGattServer implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; - private BluetoothAdapter mAdapter; - private IBluetoothGatt mService; - private BluetoothGattServerCallback mCallback; + private final IBluetoothGatt mService; + private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; + private BluetoothGattServerCallback mCallback; + private Object mServerIfLock = new Object(); private int mServerIf; private int mTransport; @@ -384,15 +385,15 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer( - IBluetoothGatt iGatt, int transport, AttributionSource attributionSource) { + /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport, + BluetoothAdapter adapter) { mService = iGatt; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mCallback = null; mServerIf = 0; mTransport = transport; mServices = new ArrayList(); - mAttributionSource = attributionSource; } /** diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 51ef3c284f4..92f3f5427b0 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -339,7 +340,8 @@ public final class BluetoothHeadset implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private volatile IBluetoothHeadset mService; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -357,10 +359,11 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Create a BluetoothHeadset proxy object. */ - /*package*/ BluetoothHeadset(Context context, ServiceListener l) { + /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) { mContext = context; mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -519,7 +522,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; 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(); @@ -540,7 +544,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; 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(); diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 840b4d3121b..de0043f2bf7 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -424,7 +425,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @@ -437,8 +439,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -531,7 +535,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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(); @@ -557,7 +562,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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(); diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index fa52eda8ea6..491937d0472 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -130,7 +131,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { @@ -144,8 +146,10 @@ public final class BluetoothHearingAid implements BluetoothProfile { * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHearingAid(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -240,7 +244,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -262,7 +267,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 6565ec0566b..6abaa22ceac 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -415,7 +416,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { @@ -425,8 +427,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { } }; - BluetoothHidDevice(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -446,7 +449,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -465,7 +469,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 68a9d371d3f..c655422dbb1 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -26,6 +26,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -236,7 +237,8 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, "BluetoothHidHost", IBluetoothHidHost.class.getName()) { @@ -250,8 +252,10 @@ public final class BluetoothHidHost implements BluetoothProfile { * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHidHost(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -348,7 +352,8 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -371,7 +376,8 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 462c7b7ede6..a8ca9a8adab 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -101,7 +102,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", IBluetoothLeAudio.class.getName()) { @@ -115,8 +117,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * Create a BluetoothLeAudio proxy object for interacting with the local * Bluetooth LeAudio service. */ - /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothLeAudio(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); @@ -223,7 +227,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -245,7 +250,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 07af8dbd234..b13ccaf7e57 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -21,6 +21,7 @@ import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.app.ActivityThread; +import android.app.AppGlobals; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; @@ -31,7 +32,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; -import java.util.function.Supplier; /** * High level manager used to obtain an instance of an {@link BluetoothAdapter} @@ -66,11 +66,36 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - mAttributionSource = (context != null) ? context.getAttributionSource() - : ActivityThread.currentAttributionSource(); + mAttributionSource = resolveAttributionSource(context); mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); } + /** {@hide} */ + public static AttributionSource resolveAttributionSource(Context context) { + AttributionSource res = null; + if (context != null) { + res = context.getAttributionSource(); + } + if (res == null) { + res = ActivityThread.currentAttributionSource(); + } + if (res == null) { + int uid = android.os.Process.myUid(); + if (uid == android.os.Process.ROOT_UID) { + uid = android.os.Process.SYSTEM_UID; + } + try { + res = new AttributionSource(uid, + AppGlobals.getPackageManager().getPackagesForUid(uid)[0], null); + } catch (RemoteException ignored) { + } + } + if (res == null) { + throw new IllegalStateException("Failed to resolve AttributionSource"); + } + return res; + } + /** * Get the BLUETOOTH Adapter for this device. * @@ -129,24 +154,9 @@ public final class BluetoothManager { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices(int profile) { if (DBG) Log.d(TAG, "getConnectedDevices"); - if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { - throw new IllegalArgumentException("Profile not supported: " + profile); - } - - List connectedDevices = new ArrayList(); - - try { - IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = managerService.getBluetoothGatt(); - if (iGatt == null) return connectedDevices; - - connectedDevices = iGatt.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED}, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - - return connectedDevices; + return getDevicesMatchingConnectionStates(profile, new int[] { + BluetoothProfile.STATE_CONNECTED + }); } /** @@ -183,8 +193,9 @@ public final class BluetoothManager { IBluetoothManager managerService = mAdapter.getBluetoothManager(); IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; - devices = iGatt.getDevicesMatchingConnectionStates( - states, mAttributionSource); + devices = BluetoothDevice.setAttributionSource( + iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -288,7 +299,7 @@ public final class BluetoothManager { return null; } BluetoothGattServer mGattServer = - new BluetoothGattServer(iGatt, transport, mAttributionSource); + new BluetoothGattServer(iGatt, transport, mAdapter); Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index a025d9b4d0c..68bb60a8542 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -79,7 +80,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP, "BluetoothMap", IBluetoothMap.class.getName()) { @@ -92,9 +94,11 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener listener) { + /* package */ BluetoothMap(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); @@ -281,7 +285,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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(); @@ -305,7 +310,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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(); @@ -455,13 +461,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } + private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index d72081c0bac..823967de6d9 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()); } - } diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index c41c9dee254..c3865386013 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -183,7 +184,8 @@ public final class BluetoothPan implements BluetoothProfile { private final Context mContext; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PAN, "BluetoothPan", IBluetoothPan.class.getName()) { @@ -201,8 +203,10 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothPan(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mContext = context; mProfileConnector.connect(context, listener); } @@ -355,7 +359,8 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -381,7 +386,8 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index ef6fddf54d6..1a179c722e1 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -25,6 +25,7 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -98,7 +99,8 @@ public class BluetoothPbap implements BluetoothProfile { private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; /** @hide */ public static final int RESULT_FAILURE = 0; @@ -129,10 +131,11 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - public BluetoothPbap(Context context, ServiceListener l) { + public BluetoothPbap(Context context, ServiceListener l, BluetoothAdapter adapter) { mContext = context; mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -229,7 +232,8 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -277,7 +281,8 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 7f4863891ef..d9c69f0fcce 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -57,7 +58,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { @@ -70,11 +72,12 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener listener) { + BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient proxy object"); } - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -176,7 +179,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient 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(); @@ -203,7 +207,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient 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(); @@ -247,12 +252,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index b86857f42f4..832538e8b8d 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -20,11 +20,11 @@ import android.Manifest; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -97,7 +97,8 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.SAP, "BluetoothSap", IBluetoothSap.class.getName()) { @@ -110,9 +111,11 @@ public final class BluetoothSap implements BluetoothProfile { /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener listener) { + /* package */ BluetoothSap(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -265,7 +268,8 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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(); @@ -288,7 +292,8 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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(); @@ -435,17 +440,10 @@ public final class BluetoothSap implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } -- GitLab From 43ee69eed974cd7ebc4784416a6e1e251464cc36 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 23 Apr 2021 14:13:57 -0600 Subject: [PATCH 1276/1408] 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/BluetoothA2dp.java | 59 ++++++++----- .../android/bluetooth/BluetoothA2dpSink.java | 43 +++++++--- .../android/bluetooth/BluetoothAdapter.java | 29 +++---- .../bluetooth/BluetoothAvrcpController.java | 13 +-- .../android/bluetooth/BluetoothDevice.java | 4 +- .../android/bluetooth/BluetoothHeadset.java | 62 ++++++++------ .../bluetooth/BluetoothHeadsetClient.java | 55 ++++++------ .../bluetooth/BluetoothHearingAid.java | 51 ++++++++---- .../android/bluetooth/BluetoothHidDevice.java | 25 +++--- .../android/bluetooth/BluetoothHidHost.java | 68 ++++++++++----- .../android/bluetooth/BluetoothLeAudio.java | 21 ++--- .../java/android/bluetooth/BluetoothMap.java | 19 +++-- .../android/bluetooth/BluetoothMapClient.java | 32 +++---- .../java/android/bluetooth/BluetoothPan.java | 27 +++--- .../java/android/bluetooth/BluetoothPbap.java | 17 ++-- .../bluetooth/BluetoothPbapClient.java | 51 +++++++++--- .../java/android/bluetooth/BluetoothSap.java | 19 +++-- .../bluetooth/BluetoothManagerService.java | 83 ++++++++++++------- 18 files changed, 417 insertions(+), 261 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index aa0bc3e1e35..1fb7638a6f0 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -473,7 +473,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return service.setActiveDevice(device); + return service.setActiveDevice(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -500,7 +500,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getActiveDevice(); + return service.getActiveDevice(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; @@ -560,7 +560,7 @@ public final class BluetoothA2dp implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -590,7 +590,8 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + return BluetoothAdapter.connectionPolicyToPriority( + service.getPriority(device, mAttributionSource)); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; @@ -612,14 +613,18 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -664,7 +669,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - service.setAvrcpAbsoluteVolume(volume); + service.setAvrcpAbsoluteVolume(volume, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { @@ -685,7 +690,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.isA2dpPlaying(device); + return service.isA2dpPlaying(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -737,7 +742,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getCodecStatus(device); + return service.getCodecStatus(device, mAttributionSource); } if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -772,7 +777,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - service.setCodecConfigPreference(device, codecConfig); + service.setCodecConfigPreference(device, codecConfig, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -829,9 +834,9 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { if (enable) { - service.enableOptionalCodecs(device); + service.enableOptionalCodecs(device, mAttributionSource); } else { - service.disableOptionalCodecs(device); + service.disableOptionalCodecs(device, mAttributionSource); } } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -860,7 +865,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.supportsOptionalCodecs(device); + return service.supportsOptionalCodecs(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; @@ -888,7 +893,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.getOptionalCodecsEnabled(device); + return service.getOptionalCodecsEnabled(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; @@ -924,7 +929,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - service.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -946,13 +951,17 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @Type int getDynamicBufferSupport() { if (VDBG) log("getDynamicBufferSupport()"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getDynamicBufferSupport(); + return service.getDynamicBufferSupport(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return DYNAMIC_BUFFER_SUPPORT_NONE; @@ -973,13 +982,17 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @Nullable BufferConstraints getBufferConstraints() { if (VDBG) log("getBufferConstraints()"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getBufferConstraints(); + return service.getBufferConstraints(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; @@ -999,14 +1012,18 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.setBufferLengthMillis(codec, value); + return service.setBufferLengthMillis(codec, value, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 0158e3c0d9e..c0a2aa3db42 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -132,13 +132,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -179,7 +183,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -203,7 +207,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -227,7 +231,8 @@ public final class BluetoothA2dpSink 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, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -250,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -279,7 +284,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getAudioConfig(device); + return service.getAudioConfig(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; @@ -338,7 +343,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -358,7 +363,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -376,13 +385,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -401,12 +414,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean isAudioPlaying(@NonNull BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isA2dpPlaying(device); + return service.isA2dpPlaying(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 18e6356d825..8afc557ef85 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -981,7 +981,7 @@ public final class BluetoothAdapter { } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.disableBle(packageName, mToken); + return mManagerService.disableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1028,7 +1028,7 @@ public final class BluetoothAdapter { } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.enableBle(packageName, mToken); + return mManagerService.enableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1193,7 +1193,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(ActivityThread.currentPackageName()); + return mManagerService.enable(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1226,7 +1226,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable() { try { - return mManagerService.disable(ActivityThread.currentPackageName(), true); + return mManagerService.disable(mAttributionSource, true); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1246,7 +1246,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(ActivityThread.currentPackageName(), persist); + return mManagerService.disable(mAttributionSource, persist); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1267,7 +1267,7 @@ public final class BluetoothAdapter { }) public String getAddress() { try { - return mManagerService.getAddress(); + return mManagerService.getAddress(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1285,7 +1285,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName() { try { - return mManagerService.getName(); + return mManagerService.getName(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1297,7 +1297,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public int getNameLengthForAdvertise() { try { - return mService.getNameLengthForAdvertise(); + return mService.getNameLengthForAdvertise(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1316,7 +1316,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null && mService.factoryReset() - && mManagerService != null && mManagerService.onFactoryReset()) { + && mManagerService != null + && mManagerService.onFactoryReset(mAttributionSource)) { return true; } Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); @@ -1910,7 +1911,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); - return mService.removeActiveDevice(profiles); + return mService.removeActiveDevice(profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1962,7 +1963,7 @@ public final class BluetoothAdapter { if (DBG) { Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); } - return mService.setActiveDevice(device, profiles); + return mService.setActiveDevice(device, profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1995,7 +1996,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.connectAllEnabledProfiles(device); + return mService.connectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2027,7 +2028,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.disconnectAllEnabledProfiles(device); + return mService.disconnectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -3219,7 +3220,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); + return mManagerService.enableNoAutoConnect(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index be3d5ee9ef1..0b43e71aba2 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -136,7 +136,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -159,7 +159,8 @@ public final class BluetoothAvrcpController 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, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -181,7 +182,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -205,7 +206,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - settings = service.getPlayerSettings(device); + settings = service.getPlayerSettings(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getMetadata() " + e); return null; @@ -226,7 +227,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.setPlayerApplicationSetting(plAppSetting); + return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); return false; @@ -249,7 +250,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - service.sendGroupNavigationCmd(device, keyCode, keyState); + service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c700a10f3a0..98823b096a5 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1376,9 +1376,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setRemoteAlias(this, alias, - mAttributionSource.getPackageName(), - mAttributionSource); + return service.setRemoteAlias(this, alias, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 92f3f5427b0..9dc2d8e9539 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -545,7 +545,8 @@ public final class BluetoothHeadset 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(); @@ -608,7 +609,8 @@ public final class BluetoothHeadset implements BluetoothProfile { } try { return service.setPriority( - device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + device, BluetoothAdapter.priorityToConnectionPolicy(priority), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -647,7 +649,7 @@ public final class BluetoothHeadset 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; @@ -677,7 +679,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + return BluetoothAdapter.connectionPolicyToPriority( + service.getPriority(device, mAttributionSource)); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; @@ -699,13 +702,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; 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; @@ -729,7 +736,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isNoiseReductionSupported(device); + return service.isNoiseReductionSupported(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -752,7 +759,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isVoiceRecognitionSupported(device); + return service.isVoiceRecognitionSupported(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -791,7 +798,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device); + return service.startVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -820,7 +827,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device); + return service.stopVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -843,7 +850,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isAudioConnected(device); + return service.isAudioConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -877,7 +884,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && !isDisabled()) { try { - return service.getAudioState(device); + return service.getAudioState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -905,7 +912,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.setAudioRouteAllowed(allowed); + service.setAudioRouteAllowed(allowed, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -928,7 +935,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getAudioRouteAllowed(); + return service.getAudioRouteAllowed(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -953,7 +960,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.setForceScoAudio(forced); + service.setForceScoAudio(forced, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -978,7 +985,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.isAudioOn(); + return service.isAudioOn(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1013,7 +1020,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.connectAudio(); + return service.connectAudio(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1042,7 +1049,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.disconnectAudio(); + return service.disconnectAudio(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1086,7 +1093,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.startScoUsingVirtualVoiceCall(); + return service.startScoUsingVirtualVoiceCall(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1121,7 +1128,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.stopScoUsingVirtualVoiceCall(); + return service.stopScoUsingVirtualVoiceCall(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1151,7 +1158,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.phoneStateChanged(numActive, numHeld, callState, number, type, name); + service.phoneStateChanged(numActive, numHeld, callState, number, type, name, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1176,7 +1184,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.clccResponse(index, direction, status, mode, mpty, number, type); + service.clccResponse(index, direction, status, mode, mpty, number, type, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1216,7 +1225,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendVendorSpecificResultCode(device, command, arg); + return service.sendVendorSpecificResultCode(device, command, arg, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1260,7 +1270,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && (device == null || isValidDevice(device))) { try { - return service.setActiveDevice(device); + return service.setActiveDevice(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1290,7 +1300,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getActiveDevice(); + return service.getActiveDevice(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1318,7 +1328,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.isInbandRingingEnabled(); + return service.isInbandRingingEnabled(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index de0043f2bf7..0059cdb9fbb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -483,7 +483,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -511,7 +511,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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())); return false; @@ -536,7 +536,7 @@ public final class BluetoothHeadsetClient 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(); @@ -563,7 +563,8 @@ public final class BluetoothHeadsetClient 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(); @@ -588,7 +589,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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; @@ -641,7 +642,7 @@ public final class BluetoothHeadsetClient 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; @@ -689,7 +690,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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; @@ -718,7 +719,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device); + return service.startVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -745,7 +746,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendVendorAtCommand(device, vendorId, atCommand); + return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -773,7 +774,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device); + return service.stopVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -796,7 +797,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getCurrentCalls(device); + return service.getCurrentCalls(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -819,7 +820,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getCurrentAgEvents(device); + return service.getCurrentAgEvents(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -846,7 +847,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.acceptCall(device, flag); + return service.acceptCall(device, flag, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -870,7 +871,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.holdCall(device); + return service.holdCall(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -899,7 +900,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.rejectCall(device); + return service.rejectCall(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -932,7 +933,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.terminateCall(device, call); + return service.terminateCall(device, call, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -963,7 +964,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.enterPrivateMode(device, index); + return service.enterPrivateMode(device, index, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -993,7 +994,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.explicitCallTransfer(device); + return service.explicitCallTransfer(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1019,7 +1020,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.dial(device, number); + return service.dial(device, number, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1046,7 +1047,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendDTMF(device, code); + return service.sendDTMF(device, code, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1075,7 +1076,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getLastVoiceTagNumber(device); + return service.getLastVoiceTagNumber(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1098,7 +1099,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getAudioState(device); + return service.getAudioState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1124,7 +1125,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - service.setAudioRouteAllowed(device, allowed); + service.setAudioRouteAllowed(device, allowed, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1149,7 +1150,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getAudioRouteAllowed(device); + return service.getAudioRouteAllowed(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1176,7 +1177,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.connectAudio(device); + return service.connectAudio(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1203,7 +1204,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.disconnectAudio(device); + return service.disconnectAudio(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1227,7 +1228,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getCurrentAgFeatures(device); + return service.getCurrentAgFeatures(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 491937d0472..3ff2ebd86b6 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -185,7 +185,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.connect(device); + return service.connect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -217,13 +217,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -245,7 +249,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled()) { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -268,7 +272,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled()) { return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -291,7 +296,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -330,7 +335,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device); + service.setActiveDevice(device, mAttributionSource); return true; } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -358,7 +363,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getActiveDevices(); + return service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -419,7 +424,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -439,7 +444,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -457,7 +466,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); verifyDeviceNotNull(device, "getConnectionPolicy"); @@ -465,7 +478,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -517,7 +530,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (!isEnabled()) return; - service.setVolume(volume); + service.setVolume(volume, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -534,7 +547,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public long getHiSyncId(@NonNull BluetoothDevice device) { if (VDBG) { log("getHiSyncId(" + device + ")"); @@ -549,7 +566,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return service.getHiSyncId(device); + return service.getHiSyncId(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; @@ -574,7 +591,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getDeviceSide(device); + return service.getDeviceSide(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; @@ -602,7 +619,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getDeviceMode(device); + return service.getDeviceMode(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 6abaa22ceac..11e5711eff1 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -450,7 +450,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -470,7 +470,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -489,7 +490,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -549,7 +550,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); - result = service.registerApp(sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -578,7 +579,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.unregisterApp(); + result = service.unregisterApp(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -605,7 +606,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.sendReport(device, id, data); + result = service.sendReport(device, id, data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -633,7 +634,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.replyReport(device, type, id, data); + result = service.replyReport(device, type, id, data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -659,7 +660,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.reportError(device, error); + result = service.reportError(device, error, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -683,7 +684,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { - return service.getUserAppName(); + return service.getUserAppName(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -710,7 +711,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.connect(device); + result = service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -736,7 +737,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.disconnect(device); + result = service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -781,7 +782,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index c655422dbb1..0abe18c4d22 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -284,13 +284,17 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -322,13 +326,17 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -353,7 +361,7 @@ public final class BluetoothHidHost implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -377,7 +385,8 @@ public final class BluetoothHidHost 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, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -404,7 +413,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -425,7 +434,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -444,7 +457,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -458,7 +475,7 @@ public final class BluetoothHidHost implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -478,7 +495,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -496,7 +517,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); if (device == null) { @@ -505,7 +530,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -538,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.virtualUnplug(device); + return service.virtualUnplug(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -565,7 +590,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getProtocolMode(device); + return service.getProtocolMode(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -590,7 +615,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setProtocolMode(device, protocolMode); + return service.setProtocolMode(device, protocolMode, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -622,7 +647,8 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getReport(device, reportType, reportId, bufferSize); + return service.getReport(device, reportType, reportId, bufferSize, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -649,7 +675,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setReport(device, reportType, report); + return service.setReport(device, reportType, report, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -675,7 +701,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendData(device, report); + return service.sendData(device, report, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -700,7 +726,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getIdleTime(device); + return service.getIdleTime(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -726,7 +752,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setIdleTime(device, idleTime); + return service.setIdleTime(device, idleTime, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index a8ca9a8adab..51bfd048fdc 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -166,7 +166,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.connect(device); + return service.connect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -206,7 +206,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -228,7 +228,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -251,7 +251,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -274,7 +275,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -312,7 +313,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device); + service.setActiveDevice(device, mAttributionSource); return true; } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -338,7 +339,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getActiveDevices(); + return service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -363,7 +364,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getGroupId(device); + return service.getGroupId(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return GROUP_ID_INVALID; @@ -401,7 +402,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -430,7 +431,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 68bb60a8542..88505b51f83 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -144,7 +144,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.getState(); + return service.getState(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -170,7 +170,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.getClient(); + return service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -195,7 +195,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -234,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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())); return false; @@ -286,7 +286,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { 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(); @@ -311,7 +311,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { 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(); @@ -335,7 +336,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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; @@ -394,7 +395,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { 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; @@ -446,7 +447,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 823967de6d9..14804dbefeb 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index c3865386013..90c94de2f4e 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -22,10 +22,10 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; @@ -254,7 +254,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -294,7 +294,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -333,7 +333,7 @@ public final class BluetoothPan implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -360,7 +360,7 @@ public final class BluetoothPan implements BluetoothProfile { if (service != null && isEnabled()) { try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -387,7 +387,8 @@ public final class BluetoothPan 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, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -403,13 +404,17 @@ public final class BluetoothPan implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -438,7 +443,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value, pkgName, mContext.getAttributionTag()); + service.setBluetoothTethering(value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -459,7 +464,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.isTetheringOn(); + return service.isTetheringOn(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 1a179c722e1..2600029362a 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -233,7 +233,7 @@ public class BluetoothPbap implements BluetoothProfile { } try { return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -247,13 +247,17 @@ public class BluetoothPbap implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { log("getConnectionState: device=" + device); try { final IBluetoothPbap service = mService; if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -282,7 +286,8 @@ public class BluetoothPbap implements BluetoothProfile { } try { return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -322,7 +327,7 @@ public class BluetoothPbap implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -350,7 +355,7 @@ public class BluetoothPbap implements BluetoothProfile { return false; } try { - service.disconnect(device); + service.disconnect(device, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, e.toString()); diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index d9c69f0fcce..3ebd8fe1920 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -114,7 +114,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) { log("connect(" + device + ") for PBAP Client."); @@ -122,7 +126,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -142,7 +146,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) { log("disconnect(" + device + ")" + new Exception()); @@ -150,7 +158,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - service.disconnect(device); + service.disconnect(device, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); @@ -180,7 +188,7 @@ public final class BluetoothPbapClient 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(); @@ -208,7 +216,8 @@ public final class BluetoothPbapClient 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(); @@ -235,7 +244,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient 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; @@ -270,7 +279,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -288,7 +301,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) { @@ -301,7 +318,7 @@ public final class BluetoothPbapClient 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; @@ -323,7 +340,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -340,7 +361,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { log("getConnectionPolicy(" + device + ")"); @@ -348,7 +373,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient 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; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 832538e8b8d..0631abdadac 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -157,7 +157,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.getState(); + return service.getState(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -182,7 +182,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.getClient(); + return service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -207,7 +207,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -245,7 +245,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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())); return false; @@ -269,7 +269,7 @@ public final class BluetoothSap 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(); @@ -293,7 +293,8 @@ public final class BluetoothSap 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(); @@ -316,7 +317,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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; @@ -374,7 +375,7 @@ public final class BluetoothSap 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; @@ -425,7 +426,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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; diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 583e54b5781..a24d813d591 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; +import static android.content.PermissionChecker.PID_UNKNOWN; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.UserHandle.USER_SYSTEM; @@ -42,6 +43,7 @@ import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; import android.content.ActivityNotFoundException; +import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -302,13 +304,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); } - public boolean onFactoryReset() { + public boolean onFactoryReset(AttributionSource attributionSource) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); final long token = Binder.clearCallingIdentity(); try { - return onFactoryResetInternal(); + return onFactoryResetInternal(attributionSource); } finally { Binder.restoreCallingIdentity(token); } @@ -318,7 +320,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) - private boolean onFactoryResetInternal() { + private boolean onFactoryResetInternal(AttributionSource attributionSource) { // Wait for stable state if bluetooth is temporary state. int state = getState(); if (state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -347,7 +349,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { addActiveLog( BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, mContext.getPackageName(), false); - mBluetooth.disable(mContext.getAttributionSource()); + mBluetooth.disable(attributionSource); return true; } } catch (RemoteException e) { @@ -943,7 +945,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) { + private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message, + boolean requireForeground) { if (isBluetoothDisallowed()) { if (DBG) { Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed"); @@ -954,22 +957,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; if (!isCallerSystem) { - checkPackage(callingUid, packageName); + checkPackage(callingUid, attributionSource.getPackageName()); if (requireForeground && !checkIfCallerIsForegroundUser()) { Slog.w(TAG, "Not allowed for non-active and non system user"); return false; } - if (!checkConnectPermissionForPreflight(mContext)) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) { return false; } } return true; } - public boolean enableBle(String packageName, IBinder token) throws RemoteException { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean enableBle(AttributionSource attributionSource, IBinder token) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) { if (DBG) { Slog.d(TAG, "enableBle(): bluetooth disallowed"); } @@ -999,8 +1004,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean disableBle(String packageName, IBinder token) throws RemoteException { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean disableBle(AttributionSource attributionSource, IBinder token) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) { if (DBG) { Slog.d(TAG, "disableBLE(): bluetooth disallowed"); } @@ -1118,8 +1125,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } - public boolean enableNoAutoConnect(String packageName) { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean enableNoAutoConnect(AttributionSource attributionSource) { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) { if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); } @@ -1145,8 +1153,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable(String packageName) throws RemoteException { - if (!checkBluetoothPermissions(packageName, true)) { + public boolean enable(AttributionSource attributionSource) throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enable", true)) { if (DBG) { Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); } @@ -1179,8 +1188,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(String packageName, boolean persist) throws RemoteException { - if (!checkBluetoothPermissions(packageName, true)) { + public boolean disable(AttributionSource attributionSource, boolean persist) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "disable", true)) { if (DBG) { Slog.d(TAG, "disable(): not disabling - bluetooth disallowed"); } @@ -1696,8 +1707,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public String getAddress() { - if (!checkConnectPermissionForPreflight(mContext)) { + public String getAddress(AttributionSource attributionSource) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) { return null; } @@ -1730,8 +1741,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mAddress; } - public String getName() { - if (!checkConnectPermissionForPreflight(mContext)) { + public String getName(AttributionSource attributionSource) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) { return null; } @@ -2874,6 +2885,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private static boolean checkPermissionForDataDelivery(Context context, String permission, + AttributionSource attributionSource, String message) { + final int result = PermissionChecker.checkPermissionForDataDeliveryFromDataSource( + context, permission, PID_UNKNOWN, + new AttributionSource(context.getAttributionSource(), attributionSource), message); + if (result == PERMISSION_GRANTED) { + return true; + } + + final String msg = "Need " + permission + " permission for " + attributionSource + ": " + + message; + if (result == PERMISSION_HARD_DENIED) { + throw new SecurityException(msg); + } else { + Log.w(TAG, msg); + return false; + } + } + /** * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns * false if the result is a soft denial. Throws SecurityException if the result is a hard @@ -2882,12 +2912,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { *

    Should be used in situations where the app op should not be noted. */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private static boolean checkConnectPermissionForPreflight(Context context) { - int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight( - context, BLUETOOTH_CONNECT); - if (permissionCheckResult == PERMISSION_HARD_DENIED) { - throw new SecurityException("Need BLUETOOTH_CONNECT permission"); - } - return permissionCheckResult == PERMISSION_GRANTED; + public static boolean checkConnectPermissionForDataDelivery( + Context context, AttributionSource attributionSource, String message) { + return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT, + attributionSource, message); } } -- GitLab From 82f1ab97e734508f258cda31cc2a7ec54fd74c04 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Thu, 22 Apr 2021 11:39:21 -0700 Subject: [PATCH 1277/1408] OOB: Remove static creator methods in favor of public constructors Bug: 184370881 Tag: #feature Test: Manual compile Change-Id: I502cdf5adde324b7a41cfb6974f0b43b0064a911 --- framework/java/android/bluetooth/OobData.java | 112 +++++------------- 1 file changed, 27 insertions(+), 85 deletions(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index d6868e0ffd5..2dfa91dcba3 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -25,7 +25,6 @@ import android.os.Parcelable; import com.android.internal.util.Preconditions; -import java.lang.IllegalArgumentException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -164,68 +163,6 @@ public final class OobData implements Parcelable { @SystemApi public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04; - /** - * Main creation method for creating a Classic version of {@link OobData}. - * - *

    This object will allow the caller to call {@link ClassicBuilder#build()} - * to build the data object or add any option information to the builder. - * - * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} octets - * of data. Data is derived from controller/host stack and is required for pairing OOB. - * @param classicLength byte array representing the length of data from 8-65535 across 2 - * octets (0xXXXX). - * @param deviceAddressWithType byte array representing the Bluetooth Address of the device - * that owns the OOB data. (i.e. the originator) [6 octets] - * - * @return a Classic Builder instance with all the given data set or null. - * - * @throws IllegalArgumentException if any of the values fail to be set. - * @throws NullPointerException if any argument is null. - * - * @hide - */ - @NonNull - @SystemApi - public static ClassicBuilder createClassicBuilder(@NonNull byte[] confirmationHash, - @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { - return new ClassicBuilder(confirmationHash, classicLength, deviceAddressWithType); - } - - /** - * Main creation method for creating a LE version of {@link OobData}. - * - *

    This object will allow the caller to call {@link LeBuilder#build()} - * to build the data object or add any option information to the builder. - * - * @param deviceAddressWithType the LE device address plus the address type (7 octets); - * not null. - * @param leDeviceRole whether the device supports Peripheral, Central, - * Both including preference; not null. (1 octet) - * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets - * of data. Data is derived from controller/host stack and is - * required for pairing OOB. - * - *

    Possible LE Device Role Values: - * 0x00 Only Peripheral supported - * 0x01 Only Central supported - * 0x02 Central & Peripheral supported; Peripheral Preferred - * 0x03 Only peripheral supported; Central Preferred - * 0x04 - 0xFF Reserved - * - * @return a LeBuilder instance with all the given data set or null. - * - * @throws IllegalArgumentException if any of the values fail to be set. - * @throws NullPointerException if any argument is null. - * - * @hide - */ - @NonNull - @SystemApi - public static LeBuilder createLeBuilder(@NonNull byte[] confirmationHash, - @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { - return new LeBuilder(confirmationHash, deviceAddressWithType, leDeviceRole); - } - /** * Builds an {@link OobData} object and validates that the required combination * of values are present to create the LE specific OobData type. @@ -342,16 +279,18 @@ public final class OobData implements Parcelable { private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default /** - * Constructing an OobData object for use with LE requires - * a LE Device Address and LE Device Role as well as the Confirmation - * and optionally, the Randomizer, however it is recommended to use. + * Main creation method for creating a LE version of {@link OobData}. * - * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} - * octets of data. Data is derived from controller/host stack and is required for - * pairing OOB. - * @param deviceAddressWithType 7 bytes containing the 6 byte address with the 1 byte - * address type. - * @param leDeviceRole indicating device's role and preferences (Central or Peripheral) + *

    This object will allow the caller to call {@link LeBuilder#build()} + * to build the data object or add any option information to the builder. + * + * @param deviceAddressWithType the LE device address plus the address type (7 octets); + * not null. + * @param leDeviceRole whether the device supports Peripheral, Central, + * Both including preference; not null. (1 octet) + * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets + * of data. Data is derived from controller/host stack and is + * required for pairing OOB. * *

    Possible Values: * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported @@ -361,11 +300,13 @@ public final class OobData implements Parcelable { * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred * 0x04 - 0xFF Reserved * - * @throws IllegalArgumentException if deviceAddressWithType is not - * {@link LE_DEVICE_ADDRESS_OCTETS} octets + * @throws IllegalArgumentException if any of the values fail to be set. * @throws NullPointerException if any argument is null. + * + * @hide */ - private LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, + @SystemApi + public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { Preconditions.checkNotNull(confirmationHash); Preconditions.checkNotNull(deviceAddressWithType); @@ -572,25 +513,26 @@ public final class OobData implements Parcelable { private byte[] mClassOfDevice = null; /** + * Main creation method for creating a Classic version of {@link OobData}. + * + *

    This object will allow the caller to call {@link ClassicBuilder#build()} + * to build the data object or add any option information to the builder. + * * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} * octets of data. Data is derived from controller/host stack and is required for pairing * OOB. - * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets - * of data. Data is derived from controller/host stack and is required - * for pairing OOB. Also, randomizerHash may be all 0s or null in which case - * it becomes all 0s. * @param classicLength byte array representing the length of data from 8-65535 across 2 - * octets (0xXXXX). Inclusive of this value in the length. + * octets (0xXXXX). * @param deviceAddressWithType byte array representing the Bluetooth Address of the device - * that owns the OOB data. (i.e. the originator) [7 octets] this includes the Address Type - * as the last octet. + * that owns the OOB data. (i.e. the originator) [6 octets] * - * @throws IllegalArgumentException if any value is not the correct length - * @throws NullPointerException if anything passed is null + * @throws IllegalArgumentException if any of the values fail to be set. + * @throws NullPointerException if any argument is null. * * @hide */ - private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, + @SystemApi + public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { Preconditions.checkNotNull(confirmationHash); Preconditions.checkNotNull(classicLength); -- GitLab From 08fa792031a529c1558250845e3d811ebcc0f6d0 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Mon, 26 Apr 2021 15:23:45 -0700 Subject: [PATCH 1278/1408] Convert onOobData parameter for OobData from @Nullable to @NonNull Bug: 185603183 Test: Compiles, test app works Tag: #feature Change-Id: I52636769f50f50b5ad2d135f54472bdeb1c25ee5 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3802289dd6d..63221c5d894 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3063,7 +3063,7 @@ public final class BluetoothAdapter { * @param transport - whether the {@link OobData} is generated for LE or Classic. * @param oobData - data generated in the host stack(LE) or controller (Classic) */ - void onOobData(@Transport int transport, @Nullable OobData oobData); + void onOobData(@Transport int transport, @NonNull OobData oobData); /** * Provides feedback when things don't go as expected. @@ -3104,7 +3104,7 @@ public final class BluetoothAdapter { * * @hide */ - public void onOobData(@Transport int transport, OobData oobData) { + public void onOobData(@Transport int transport, @NonNull OobData oobData) { mExecutor.execute(new Runnable() { public void run() { mCallback.onOobData(transport, oobData); -- GitLab From 19a8d096ae4e76490be422c7033f69896199421c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 28 Apr 2021 09:25:36 -0600 Subject: [PATCH 1279/1408] Preserve legacy permission check behavior. As part of the new "Nearby devices" permission work, the registerStateChangeCallback() API has been relaxed to no longer require permissions. However, we've discovered that some apps were depending on that SecurityException being thrown, so this change restores throwing behavior for those legacy apps. Bug: 186176507 Test: atest BluetoothInstrumentationTests Change-Id: Ife536dee246b300ffb3dd78aef0b059a230f3835 --- .../java/android/bluetooth/BluetoothHeadset.java | 10 ++++++++++ framework/java/android/bluetooth/BluetoothPbap.java | 11 +++++++++++ .../android/bluetooth/BluetoothProfileConnector.java | 12 ++++++++++++ 3 files changed, 33 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 9dc2d8e9539..3bf517c046f 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -31,6 +31,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -365,6 +366,15 @@ public final class BluetoothHeadset implements BluetoothProfile { mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); + // Preserve legacy compatibility where apps were depending on + // registerStateChangeCallback() performing a permissions check which + // has been relaxed in modern platform versions + if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R + && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Need BLUETOOTH permission"); + } + IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 2600029362a..8ce01a37cdd 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -30,6 +30,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; @@ -136,6 +137,16 @@ public class BluetoothPbap implements BluetoothProfile { mServiceListener = l; mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); + + // Preserve legacy compatibility where apps were depending on + // registerStateChangeCallback() performing a permissions check which + // has been relaxed in modern platform versions + if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R + && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Need BLUETOOTH permission"); + } + IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index b20ab754932..beff841f2fc 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -21,6 +21,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -123,6 +125,16 @@ public abstract class BluetoothProfileConnector { mContext = context; mServiceListener = listener; IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + + // Preserve legacy compatibility where apps were depending on + // registerStateChangeCallback() performing a permissions check which + // has been relaxed in modern platform versions + if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R + && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Need BLUETOOTH permission"); + } + if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); -- GitLab From a2d365484c43a19ab6f67152651d61f3b0667b47 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 29 Apr 2021 07:12:20 -0600 Subject: [PATCH 1280/1408] Ensure privileged APIs require runtime permission. When users revoke a runtime permission, they expect all interactions to be blocked, including those protected by the BLUETOOTH_PRIVILEGED permission. This change finishes applying that policy to any remaining Bluetooth APIs which didn't already implement it. To keep the implementation straightforward, this change does "data delivery" checks when registering for callbacks; the ideal behavior would be to wait until data is actually delivered through the callbacks, but RemoteCallbackList doesn't have support for AttributionSource yet. Bug: 186405452 Test: atest BluetoothInstrumentationTests Change-Id: Idd7be143eb8baff020a0718065293baae708041b --- .../android/bluetooth/BluetoothAdapter.java | 104 +++++++++++++----- .../android/bluetooth/BluetoothDevice.java | 63 ++++++++--- .../android/bluetooth/le/AdvertisingSet.java | 8 +- .../bluetooth/BluetoothManagerService.java | 52 ++++----- 4 files changed, 152 insertions(+), 75 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8afc557ef85..67179c7d3e4 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1311,11 +1311,15 @@ public final class BluetoothAdapter { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean factoryReset() { try { mServiceLock.readLock().lock(); - if (mService != null && mService.factoryReset() + if (mService != null && mService.factoryReset(mAttributionSource) && mManagerService != null && mManagerService.onFactoryReset(mAttributionSource)) { return true; @@ -1430,7 +1434,11 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setBluetoothClass(BluetoothClass bluetoothClass) { if (getState() != STATE_ON) { return false; @@ -1438,7 +1446,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setBluetoothClass(bluetoothClass); + return mService.setBluetoothClass(bluetoothClass, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1487,12 +1495,16 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setIoCapability(capability); + if (mService != null) return mService.setIoCapability(capability, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1540,12 +1552,16 @@ public final class BluetoothAdapter { * * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setLeIoCapability(@IoCapability int capability) { if (getState() != STATE_ON) return false; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.setLeIoCapability(capability); + if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1739,12 +1755,16 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public long getDiscoveryEndMillis() { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoveryEndMillis(); + return mService.getDiscoveryEndMillis(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2353,7 +2373,11 @@ public final class BluetoothAdapter { * instead. */ @Deprecated - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { SynchronousResultReceiver receiver = new SynchronousResultReceiver(); requestControllerActivityEnergyInfo(receiver); @@ -2379,12 +2403,16 @@ public final class BluetoothAdapter { * @param result The callback to which to send the activity info. * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public void requestControllerActivityEnergyInfo(ResultReceiver result) { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.requestActivityInfo(result); + mService.requestActivityInfo(result, mAttributionSource); result = null; } } catch (RemoteException e) { @@ -3141,7 +3169,7 @@ public final class BluetoothAdapter { sMetadataListeners.forEach((device, pair) -> { try { mService.registerMetadataListener(sBluetoothMetadataListener, - device); + device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to register metadata listener", e); } @@ -3150,7 +3178,8 @@ public final class BluetoothAdapter { synchronized (mBluetoothConnectionCallbackExecutorMap) { if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { try { - mService.registerBluetoothConnectionCallback(mConnectionCallback); + mService.registerBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" + "connection callback", e); @@ -3364,7 +3393,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public void generateLocalOobData(@Transport int transport, @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { if (transport != BluetoothDevice.TRANSPORT_BREDR && transport @@ -3378,7 +3411,7 @@ public final class BluetoothAdapter { } else { try { mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, - executor)); + executor), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -3515,11 +3548,13 @@ public final class BluetoothAdapter { /** {@hide} */ @UnsupportedAppUsage + @RequiresNoPermission public IBluetoothManager getBluetoothManager() { return mManagerService; } /** {@hide} */ + @RequiresNoPermission public AttributionSource getAttributionSource() { return mAttributionSource; } @@ -3892,7 +3927,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); @@ -3932,7 +3971,8 @@ public final class BluetoothAdapter { boolean ret = false; try { - ret = service.registerMetadataListener(sBluetoothMetadataListener, device); + ret = service.registerMetadataListener(sBluetoothMetadataListener, device, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "registerMetadataListener fail", e); } finally { @@ -3965,7 +4005,11 @@ public final class BluetoothAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, @NonNull OnMetadataChangedListener listener) { if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); @@ -3993,7 +4037,7 @@ public final class BluetoothAdapter { return true; } try { - return service.unregisterMetadataListener(device); + return service.unregisterMetadataListener(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "unregisterMetadataListener fail", e); return false; @@ -4055,7 +4099,11 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if the callback is already registered * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); @@ -4069,7 +4117,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource)) { return false; } } @@ -4098,7 +4147,11 @@ public final class BluetoothAdapter { * @return true if the callback was unregistered successfully, false otherwise * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean unregisterBluetoothConnectionCallback( @NonNull BluetoothConnectionCallback callback) { if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); @@ -4120,7 +4173,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.unregisterBluetoothConnectionCallback(mConnectionCallback); + return mService.unregisterBluetoothConnectionCallback(mConnectionCallback, + mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 98823b096a5..0ca6d74c675 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1644,7 +1644,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean canBondWithoutDialog() { final IBluetooth service = sService; if (service == null) { @@ -1653,7 +1656,7 @@ public final class BluetoothDevice implements Parcelable { } try { if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this); - return service.canBondWithoutDialog(this); + return service.canBondWithoutDialog(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1874,7 +1877,10 @@ public final class BluetoothDevice implements Parcelable { * * @return true confirmation has been sent out false for error */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPairingConfirmation(boolean confirm) { final IBluetooth service = sService; if (service == null) { @@ -1882,7 +1888,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setPairingConfirmation(this, confirm); + return service.setPairingConfirmation(this, confirm, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1971,14 +1977,17 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setSilenceMode(boolean silence) { final IBluetooth service = sService; if (service == null) { throw new IllegalStateException("Bluetooth is not turned ON"); } try { - return service.setSilenceMode(this, silence); + return service.setSilenceMode(this, silence, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "setSilenceMode fail", e); return false; @@ -1993,14 +2002,17 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean isInSilenceMode() { final IBluetooth service = sService; if (service == null) { throw new IllegalStateException("Bluetooth is not turned ON"); } try { - return service.getSilenceMode(this); + return service.getSilenceMode(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "isInSilenceMode fail", e); return false; @@ -2016,14 +2028,17 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPhonebookAccessPermission(@AccessPermission int value) { final IBluetooth service = sService; if (service == null) { return false; } try { - return service.setPhonebookAccessPermission(this, value); + return service.setPhonebookAccessPermission(this, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2063,7 +2078,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setMessageAccessPermission(@AccessPermission int value) { // Validates param value is one of the accepted constants if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) { @@ -2074,7 +2092,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setMessageAccessPermission(this, value); + return service.setMessageAccessPermission(this, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2114,14 +2132,17 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setSimAccessPermission(int value) { final IBluetooth service = sService; if (service == null) { return false; } try { - return service.setSimAccessPermission(this, value); + return service.setSimAccessPermission(this, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2616,7 +2637,10 @@ public final class BluetoothDevice implements Parcelable { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { @@ -2628,7 +2652,7 @@ public final class BluetoothDevice implements Parcelable { + ", should not over " + METADATA_MAX_LENGTH); } try { - return service.setMetadata(this, key, value); + return service.setMetadata(this, key, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "setMetadata fail", e); return false; @@ -2644,7 +2668,10 @@ public final class BluetoothDevice implements Parcelable { */ @SystemApi @Nullable - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public byte[] getMetadata(@MetadataKey int key) { final IBluetooth service = sService; if (service == null) { @@ -2652,7 +2679,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getMetadata(this, key); + return service.getMetadata(this, key, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "getMetadata fail", e); return null; diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index caa91fb2391..bbdb6953afd 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -205,10 +205,14 @@ public final class AdvertisingSet { * * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothAdvertisePermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public void getOwnAddress() { try { - mGatt.getOwnAddress(mAdvertiserId); + mGatt.getOwnAddress(mAdvertiserId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index c5246c7073e..85ff2be43be 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -312,19 +312,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); - final long token = Binder.clearCallingIdentity(); - try { - return onFactoryResetInternal(attributionSource); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - private boolean onFactoryResetInternal(AttributionSource attributionSource) { // Wait for stable state if bluetooth is temporary state. int state = getState(); if (state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -347,7 +334,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { addActiveLog( BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(); + mBluetooth.onBrEdrDown(attributionSource); return true; } else if (state == BluetoothAdapter.STATE_ON) { addActiveLog( @@ -404,7 +391,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { addActiveLog( BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(); + mBluetooth.onBrEdrDown(mContext.getAttributionSource()); mEnable = false; mEnableExternal = false; } @@ -888,7 +875,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth != null) { addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(); + mBluetooth.onBrEdrDown(mContext.getAttributionSource()); } } catch (RemoteException e) { Slog.e(TAG, "error when disabling bluetooth", e); @@ -1037,7 +1024,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (!mEnableExternal) { addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName, false); - sendBrEdrDownCallback(); + sendBrEdrDownCallback(attributionSource); } } return true; @@ -1074,12 +1061,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (!mEnableExternal && !isBleAppPresent()) { Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now"); mEnable = false; - mBluetooth.onBrEdrDown(); + mBluetooth.onBrEdrDown(mContext.getAttributionSource()); return; } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { // This triggers transition to STATE_ON - mBluetooth.onLeServiceUp(); + mBluetooth.onLeServiceUp(mContext.getAttributionSource()); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } } catch (RemoteException e) { @@ -1097,7 +1084,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) - private void sendBrEdrDownCallback() { + private void sendBrEdrDownCallback(AttributionSource attributionSource) { if (DBG) { Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks"); } @@ -1110,7 +1097,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBleAppPresent()) { // Need to stay at BLE ON. Disconnect all Gatt connections try { - mBluetoothGatt.unregAll(mContext.getAttributionSource()); + mBluetoothGatt.unregAll(attributionSource); } catch (RemoteException e) { Slog.e(TAG, "Unable to disconnect all apps.", e); } @@ -1118,7 +1105,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - mBluetooth.onBrEdrDown(); + mBluetooth.onBrEdrDown(attributionSource); } } catch (RemoteException e) { Slog.e(TAG, "Call to onBrEdrDown() failed.", e); @@ -1319,7 +1306,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth != null) { //Unregister callback object try { - mBluetooth.unregisterCallback(mBluetoothCallback); + mBluetooth.unregisterCallback(mBluetoothCallback, + mContext.getAttributionSource()); } catch (RemoteException re) { Slog.e(TAG, "Unable to unregister BluetoothCallback", re); } @@ -1729,7 +1717,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - return mBluetooth.getAddressWithAttribution(mContext.getAttributionSource()); + return mBluetooth.getAddressWithAttribution(attributionSource); } } catch (RemoteException e) { Slog.e(TAG, @@ -1758,7 +1746,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - return mBluetooth.getName(mContext.getAttributionSource()); + return mBluetooth.getName(attributionSource); } } catch (RemoteException e) { Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e); @@ -1886,7 +1874,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { int state = mBluetooth.getState(); if (state == BluetoothAdapter.STATE_BLE_ON) { Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(); + mBluetooth.onLeServiceUp(mContext.getAttributionSource()); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); break; } @@ -2105,7 +2093,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Register callback object try { - mBluetooth.registerCallback(mBluetoothCallback); + mBluetooth.registerCallback(mBluetoothCallback, + mContext.getAttributionSource()); } catch (RemoteException re) { Slog.e(TAG, "Unable to register BluetoothCallback", re); } @@ -2342,7 +2331,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { - mBluetooth.unregisterCallback(mBluetoothCallback); + mBluetooth.unregisterCallback(mBluetoothCallback, + mContext.getAttributionSource()); } } catch (RemoteException re) { Slog.e(TAG, "Unable to unregister", re); @@ -2569,7 +2559,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothStateCallback(false); // BT is OFF for general users // Broadcast as STATE_OFF newState = BluetoothAdapter.STATE_OFF; - sendBrEdrDownCallback(); + sendBrEdrDownCallback(mContext.getAttributionSource()); } } else if (newState == BluetoothAdapter.STATE_ON) { boolean isUp = (newState == BluetoothAdapter.STATE_ON); @@ -2670,7 +2660,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { //Unregister callback object - mBluetooth.unregisterCallback(mBluetoothCallback); + mBluetooth.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource()); } } catch (RemoteException re) { Slog.e(TAG, "Unable to unregister", re); @@ -2890,6 +2880,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + @SuppressLint("AndroidFrameworkRequiresPermission") private static boolean checkPermissionForDataDelivery(Context context, String permission, AttributionSource attributionSource, String message) { final int result = PermissionChecker.checkPermissionForDataDeliveryFromDataSource( @@ -2916,6 +2907,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * *

    Should be used in situations where the app op should not be noted. */ + @SuppressLint("AndroidFrameworkRequiresPermission") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static boolean checkConnectPermissionForDataDelivery( Context context, AttributionSource attributionSource, String message) { -- GitLab From a7d0db7b74a3930b87c2f6f865688edff0cf6b73 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 7 May 2021 19:13:25 -0700 Subject: [PATCH 1281/1408] Update docs for BluetoothDevice#fetchUuidsWithSdp to reflect that if the device is bonding, we either broadcast cached UUIDs or wait for SDP to be performed after the device is bonded. Tag: #feature Bug: 187157597 Test: Manual Change-Id: I1bd694195c4e974b7cd72f81848a6343b45c98fd --- framework/java/android/bluetooth/BluetoothDevice.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index a40bf343239..11b45e32c42 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1605,13 +1605,13 @@ public final class BluetoothDevice implements Parcelable { * *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently - * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * in getting the SDP records or if the process takes a long time, or the device is bonding and + * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is + * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs * if service discovery is not to be performed. * * @return False if the check fails, True if the process of initiating an ACL connection - * to the remote device was started. + * to the remote device was started or cached UUIDs will be broadcast. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { -- GitLab From f5cb1b0e7225e6d7e4d54d733a38f8a6197574cd Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Mon, 10 May 2021 13:51:17 -0700 Subject: [PATCH 1282/1408] Bluetooth ScanFilter: Allow null in setDeviceAddress Bug: 187076761 Test: manual scan filter test Change-Id: I6456be4baab4b2a2b6d4607619ff92370b8b457d --- framework/java/android/bluetooth/le/ScanFilter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 3c20dcac8ca..a74c663a9ce 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -587,6 +587,10 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { + if (deviceAddress == null) { + mDeviceAddress = deviceAddress; + return this; + } return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); } -- GitLab From 739f619eb30c7e37b0a7449c14220e2ea4f5eb86 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 7 May 2021 10:28:59 -0600 Subject: [PATCH 1283/1408] Apply AttributionSource during Intent delivery. There are some Parcelables which offer to perform Binder calls, and when these are delivered via Intent extras they fallback to ActivityThread.currentAttributionSource(), instead of being tagged based on the relevant app component. This change begins using Intent.prepareToEnterProcess() as a hook to fix-up AttributionSource when those extras finally land in the destination process. It uses the relevant AttributionSource based on the Activity or Service the Intent is delivered to, which developers have control over via AppComponentFactory. In the case of manifest elements, this change applies the first android:attributionTags value to the Context used for that BroadcastReceiver. Bug: 187097694 Test: atest AttributionTest Change-Id: I8f5197db7e8d7277d34f0ef2bb90bfdf1871186a --- framework/java/android/bluetooth/BluetoothDevice.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 40326631402..52d4c7116a6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1190,6 +1190,11 @@ public final class BluetoothDevice implements Parcelable { return devices; } + /** {@hide} */ + public void prepareToEnterProcess(AttributionSource attributionSource) { + setAttributionSource(attributionSource); + } + @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { -- GitLab From 4c2ce6e1885c73085bd2eece5cdc53e141348258 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 5 May 2021 14:04:04 -0700 Subject: [PATCH 1284/1408] Update BluetoothDevice#setAlias based on API council feedback: now accepts null input and returns an int (with error codes). Update CompanionDeviceManager#canPairWithoutPrompt to take a UserHandle instead of an int. Adds BluetoothStatusCodes class for all new Bluetooth error / success codes. Moved OOB and hci disconnect constants to the new BluetoothStatusCodes class. Tag: #feature Bug: 184714087 Test: atest BluetoothDeviceTest#test_setAlias_getAlias Change-Id: Ife03506f2cf68800f5824cb5fa94fec8aa34a39c --- .../android/bluetooth/BluetoothAdapter.java | 177 +++------------ .../android/bluetooth/BluetoothDevice.java | 38 ++-- .../bluetooth/BluetoothStatusCodes.java | 205 ++++++++++++++++++ 3 files changed, 258 insertions(+), 162 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothStatusCodes.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9fc1f88c01c..54941724b2f 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3258,38 +3258,13 @@ public final class BluetoothAdapter { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "OOB_ERROR_" }, value = { - OOB_ERROR_UNKNOWN, - OOB_ERROR_ANOTHER_ACTIVE_REQUEST, - OOB_ERROR_ADAPTER_DISABLED + @IntDef(value = { + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, + BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST, }) public @interface OobError {} - /** - * An unknown error has occurred in the controller, stack, or callback pipeline. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_UNKNOWN = 0; - - /** - * If another application has already requested {@link OobData} then another fetch will be - * disallowed until the callback is removed. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; - - /** - * The adapter is currently disabled, please enable it. - * - * @hide - */ - @SystemApi - public static final int OOB_ERROR_ADAPTER_DISABLED = 2; - /** * Provides callback methods for receiving {@link OobData} from the host stack, as well as an * error interface in order to allow the caller to determine next steps based on the {@code @@ -3310,7 +3285,7 @@ public final class BluetoothAdapter { /** * Provides feedback when things don't go as expected. * - * @param errorCode - the code descibing the type of error that occurred. + * @param errorCode - the code describing the type of error that occurred. */ void onError(@OobError int errorCode); } @@ -3407,7 +3382,7 @@ public final class BluetoothAdapter { Preconditions.checkNotNull(callback); if (!isEnabled()) { Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); - callback.onError(OOB_ERROR_ADAPTER_DISABLED); + callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); } else { try { mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, @@ -4210,141 +4185,45 @@ public final class BluetoothAdapter { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "REASON_" }, value = { - REASON_UNKNOWN, - REASON_LOCAL_REQUEST, - REASON_REMOTE_REQUEST, - REASON_LOCAL_ERROR, - REASON_REMOTE_ERROR, - REASON_TIMEOUT, - REASON_SECURITY, - REASON_SYSTEM_POLICY, - REASON_RESOURCE_LIMIT_REACHED, - REASON_CONNECTION_EXISTS, - REASON_BAD_PARAMETERS}) + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS, + BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS}) public @interface DisconnectReason {} - /** - * Indicates that the ACL disconnected due to an unknown reason. - */ - public static final int REASON_UNKNOWN = 0; - - /** - * Indicates that the ACL disconnected due to an explicit request from the local device. - *

    - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - */ - public static final int REASON_LOCAL_REQUEST = 1; - - /** - * Indicates that the ACL disconnected due to an explicit request from the remote device. - *

    - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - *

    - * Example solution: The app can also prompt the user to check their remote device. - */ - public static final int REASON_REMOTE_REQUEST = 2; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the local - * device. - *

    - * Example solution: Prompt the user to check their local device (e.g., phone, car - * headunit). - */ - public static final int REASON_LOCAL_ERROR = 3; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the remote - * device. - *

    - * Example solution: Prompt the user to check their remote device (e.g., headset, car - * headunit, watch). - */ - public static final int REASON_REMOTE_ERROR = 4; - - /** - * Indicates that the ACL disconnected due to a timeout. - *

    - * Example cause: remote device might be out of range. - *

    - * Example solution: Prompt user to verify their remote device is on or in - * connection/pairing mode. - */ - public static final int REASON_TIMEOUT = 5; - - /** - * Indicates that the ACL disconnected due to link key issues. - *

    - * Example cause: Devices are either unpaired or remote device is refusing our pairing - * request. - *

    - * Example solution: Prompt user to unpair and pair again. - */ - public static final int REASON_SECURITY = 6; - - /** - * Indicates that the ACL disconnected due to the local device's system policy. - *

    - * Example cause: privacy policy, power management policy, permissions, etc. - *

    - * Example solution: Prompt the user to check settings, or check with their system - * administrator (e.g. some corp-managed devices do not allow OPP connection). - */ - public static final int REASON_SYSTEM_POLICY = 7; - - /** - * Indicates that the ACL disconnected due to resource constraints, either on the local - * device or the remote device. - *

    - * Example cause: controller is busy, memory limit reached, maximum number of connections - * reached. - *

    - * Example solution: The app should wait and try again. If still failing, prompt the user - * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. - */ - public static final int REASON_RESOURCE_LIMIT_REACHED = 8; - - /** - * Indicates that the ACL disconnected because another ACL connection already exists. - */ - public static final int REASON_CONNECTION_EXISTS = 9; - - /** - * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. - *

    - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - */ - public static final int REASON_BAD_PARAMETERS = 10; - /** * Returns human-readable strings corresponding to {@link DisconnectReason}. */ public static String disconnectReasonText(@DisconnectReason int reason) { switch (reason) { - case REASON_UNKNOWN: + case BluetoothStatusCodes.ERROR_UNKNOWN: return "Reason unknown"; - case REASON_LOCAL_REQUEST: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST: return "Local request"; - case REASON_REMOTE_REQUEST: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST: return "Remote request"; - case REASON_LOCAL_ERROR: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL: return "Local error"; - case REASON_REMOTE_ERROR: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE: return "Remote error"; - case REASON_TIMEOUT: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT: return "Timeout"; - case REASON_SECURITY: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY: return "Security"; - case REASON_SYSTEM_POLICY: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY: return "System policy"; - case REASON_RESOURCE_LIMIT_REACHED: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED: return "Resource constrained"; - case REASON_CONNECTION_EXISTS: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS: return "Connection already exists"; - case REASON_BAD_PARAMETERS: + case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS: return "Bad parameters"; default: return "Unrecognized disconnect reason: " + reason; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0ca6d74c675..15b058a28a9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1346,6 +1346,17 @@ public final class BluetoothDevice implements Parcelable { return null; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED + }) + public @interface SetAliasReturnValues{} + /** * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method * overwrites the previously stored alias. The new alias is saved in local @@ -1353,34 +1364,35 @@ public final class BluetoothDevice implements Parcelable { * *

    This method requires the calling app to be associated with Companion Device Manager (see * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest, - * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the {@link - * android.Manifest.permission#BLUETOOTH} permission. Alternatively, if the caller has the - * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can bypass the - * Companion Device Manager association requirement. + * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the + * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the + * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can + * bypass the Companion Device Manager association requirement as well as other permission + * requirements. * - * @param alias is the new locally modifiable name for the remote Bluetooth device which must be - * non-null and not the empty string. - * @return {@code true} if the alias is successfully set, {@code false} on error - * @throws IllegalArgumentException if the alias is {@code null} or the empty string + * @param alias is the new locally modifiable name for the remote Bluetooth device which must + * be the empty string. If null, we clear the alias. + * @return whether the alias was successfully changed + * @throws IllegalArgumentException if the alias is the empty string */ @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setAlias(@NonNull String alias) { - if (alias == null || alias.isEmpty()) { - throw new IllegalArgumentException("Cannot set the alias to null or the empty string"); + public @SetAliasReturnValues int setAlias(@Nullable String alias) { + if (alias != null && alias.isEmpty()) { + throw new IllegalArgumentException("alias cannot be the empty string"); } final IBluetooth service = sService; if (service == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); - return false; + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } try { return service.setRemoteAlias(this, alias, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); + throw e.rethrowFromSystemServer(); } - return false; } /** diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java new file mode 100644 index 00000000000..31bb0f68c6f --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2021 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.annotation.SystemApi; + +/** + * A class with constants representing possible return values for Bluetooth APIs. General return + * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999. + * API-specific return values start at 1000. The exception to this is the "other" error code which + * occupies the max integer value. + */ +public final class BluetoothStatusCodes { + + private BluetoothStatusCodes() {} + + /** + * Indicates that the API call was successful + */ + public static final int SUCCESS = 0; + + /** + * Error code indicating that Bluetooth is not enabled + */ + public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; + + /** + * Error code indicating that the API call was initiated by neither the system nor the active + * Zuser + */ + public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; + + /** + * Error code indicating that the Bluetooth Device specified is not bonded + */ + public static final int ERROR_DEVICE_NOT_BONDED = 3; + + /** + * Error code indicating that the Bluetooth Device specified is not connected, but is bonded + * + * @hide + */ + public static final int ERROR_DEVICE_NOT_CONNECTED = 4; + + /** + * Error code indicating that the caller does not have the + * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission + * + * @hide + */ + public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5; + + /** + * Error code indicating that the caller does not have the + * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission + */ + public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; + + /** + * Error code indicating that the caller does not have the + * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission + * + * @hide + */ + public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7; + + /** + * If another application has already requested {@link OobData} then another fetch will be + * disallowed until the callback is removed. + * + * @hide + */ + @SystemApi + public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; + + /** + * Indicates that the ACL disconnected due to an explicit request from the local device. + *

    + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100; + + /** + * Indicates that the ACL disconnected due to an explicit request from the remote device. + *

    + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + *

    + * Example solution: The app can also prompt the user to check their remote device. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the local + * device. + *

    + * Example solution: Prompt the user to check their local device (e.g., phone, car + * headunit). + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the remote + * device. + *

    + * Example solution: Prompt the user to check their remote device (e.g., headset, car + * headunit, watch). + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103; + + /** + * Indicates that the ACL disconnected due to a timeout. + *

    + * Example cause: remote device might be out of range. + *

    + * Example solution: Prompt user to verify their remote device is on or in + * connection/pairing mode. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104; + + /** + * Indicates that the ACL disconnected due to link key issues. + *

    + * Example cause: Devices are either unpaired or remote device is refusing our pairing + * request. + *

    + * Example solution: Prompt user to unpair and pair again. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105; + + /** + * Indicates that the ACL disconnected due to the local device's system policy. + *

    + * Example cause: privacy policy, power management policy, permissions, etc. + *

    + * Example solution: Prompt the user to check settings, or check with their system + * administrator (e.g. some corp-managed devices do not allow OPP connection). + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106; + + /** + * Indicates that the ACL disconnected due to resource constraints, either on the local + * device or the remote device. + *

    + * Example cause: controller is busy, memory limit reached, maximum number of connections + * reached. + *

    + * Example solution: The app should wait and try again. If still failing, prompt the user + * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107; + + /** + * Indicates that the ACL disconnected because another ACL connection already exists. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108; + + /** + * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109; + + /** + * Indicates that an unknown error has occurred has occurred. + */ + public static final int ERROR_UNKNOWN = Integer.MAX_VALUE; +} -- GitLab From 3112dc754eca9033336283d886c44429ef519d61 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 20 May 2021 07:54:55 -0600 Subject: [PATCH 1285/1408] Relax ACTION_TETHERING_STATE_CHANGED permissions. This broadcast doesn't contain any sensitive BluetoothDevice extras; it only contains a single boolean indicating tethering state, so relax it to no longer require the BLUETOOTH_CONNECT permission. Bug: 188706031 Test: atest BluetoothInstrumentationTests Change-Id: I2ff5c501844186a5813b2347246bbc83c190222d --- framework/java/android/bluetooth/BluetoothPan.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 90c94de2f4e..d1f34cbe037 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -108,8 +108,6 @@ public final class BluetoothPan implements BluetoothProfile { * {@link #TETHERING_STATE_ON} */ @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED"; -- GitLab From 4eac11658fb7da2562325d7a8e2c6bd88e52bc05 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Wed, 2 Jun 2021 10:34:56 +0800 Subject: [PATCH 1286/1408] Use finally block for Binder.restoreCallingIdentity 1. Use final for Binder.clearCallingIdentity() to protect not being overwritten. 2. Protect it in the finally block of the try statement to avoid missing restore the CallingIdentity as exception. Bug: 189819183 Test: Manual test Ignore-AOSP-First: prevent merge conflict Change-Id: I2dd4cfc1aacecb0b77fa4c82b48b24b284271779 --- .../android/server/bluetooth/BluetoothManagerService.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 85ff2be43be..b4912bbc898 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -614,8 +614,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } // waive WRITE_SECURE_SETTINGS permission check final long callingIdentity = Binder.clearCallingIdentity(); - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); - Binder.restoreCallingIdentity(callingIdentity); + try { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_ON, value); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } } /** -- GitLab From 0a67fabedbf8f985ba2c7b85591213a3f053590a Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Wed, 2 Jun 2021 10:34:56 +0800 Subject: [PATCH 1287/1408] Use finally block for Binder.restoreCallingIdentity 1. Use final for Binder.clearCallingIdentity() to protect not being overwritten. 2. Protect it in the finally block of the try statement to avoid missing restore the CallingIdentity as exception. Bug: 189819183 Test: Manual test Change-Id: I2dd4cfc1aacecb0b77fa4c82b48b24b284271779 Merged-In: I2dd4cfc1aacecb0b77fa4c82b48b24b284271779 --- .../android/server/bluetooth/BluetoothManagerService.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 992ef2657a6..12a94e19962 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -601,8 +601,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } // waive WRITE_SECURE_SETTINGS permission check long callingIdentity = Binder.clearCallingIdentity(); - Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value); - Binder.restoreCallingIdentity(callingIdentity); + try { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.BLUETOOTH_ON, value); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } } /** -- GitLab From 98f3044ce87c7ab9d2a0efbfb8ef6a16872262df Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 3 Jun 2021 09:26:53 -0600 Subject: [PATCH 1288/1408] 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 --- .../java/android/bluetooth/BluetoothA2dp.java | 15 +++-- .../android/bluetooth/BluetoothA2dpSink.java | 5 +- .../android/bluetooth/BluetoothAdapter.java | 58 +++++++++++-------- .../bluetooth/BluetoothAvrcpController.java | 5 +- .../android/bluetooth/BluetoothDevice.java | 25 ++------ .../android/bluetooth/BluetoothHeadset.java | 11 ++-- .../bluetooth/BluetoothHeadsetClient.java | 11 ++-- .../bluetooth/BluetoothHeadsetClientCall.java | 10 +++- .../bluetooth/BluetoothHearingAid.java | 8 ++- .../android/bluetooth/BluetoothHidDevice.java | 21 +++++-- .../android/bluetooth/BluetoothHidHost.java | 5 +- .../android/bluetooth/BluetoothLeAudio.java | 8 ++- .../android/bluetooth/BluetoothManager.java | 7 ++- .../java/android/bluetooth/BluetoothMap.java | 8 ++- .../android/bluetooth/BluetoothMapClient.java | 5 +- .../java/android/bluetooth/BluetoothPan.java | 5 +- .../java/android/bluetooth/BluetoothPbap.java | 5 +- .../bluetooth/BluetoothPbapClient.java | 5 +- .../java/android/bluetooth/BluetoothSap.java | 8 ++- .../bluetooth/le/BluetoothLeScanner.java | 4 ++ .../le/PeriodicAdvertisingManager.java | 3 +- .../java/android/bluetooth/le/ScanResult.java | 10 +++- 22 files changed, 147 insertions(+), 95 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1fb7638a6f0..65cdca9eb7d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -29,6 +29,7 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -389,8 +390,9 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + return Attributable.setAttributionSource( + service.getConnectedDevicesWithAttribution(mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -411,8 +413,10 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return BluetoothDevice.setAttributionSource( - service.getDevicesMatchingConnectionStates(states), mAttributionSource); + return Attributable.setAttributionSource( + service.getDevicesMatchingConnectionStatesWithAttribution(states, + mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -500,7 +504,8 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getActiveDevice(mAttributionSource); + return Attributable.setAttributionSource( + service.getActiveDevice(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index c0a2aa3db42..2dd63a0263f 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -27,6 +27,7 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -206,7 +207,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -230,7 +231,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 54941724b2f..054b63fbad5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -46,6 +46,7 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.BatteryStats; @@ -719,8 +720,8 @@ public final class BluetoothAdapter { private final Object mLock = new Object(); private final Map mLeScanClients; - private static final Map>> - sMetadataListeners = new HashMap<>(); + private final Map>> + mMetadataListeners = new HashMap<>(); private final Map mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); @@ -729,14 +730,15 @@ public final class BluetoothAdapter { * implementation. */ @SuppressLint("AndroidFrameworkBluetoothPermission") - private static final IBluetoothMetadataListener sBluetoothMetadataListener = + private final IBluetoothMetadataListener mBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { - synchronized (sMetadataListeners) { - if (sMetadataListeners.containsKey(device)) { + Attributable.setAttributionSource(device, mAttributionSource); + synchronized (mMetadataListeners) { + if (mMetadataListeners.containsKey(device)) { List> list = - sMetadataListeners.get(device); + mMetadataListeners.get(device); for (Pair pair : list) { OnMetadataChangedListener listener = pair.first; Executor executor = pair.second; @@ -2445,7 +2447,9 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(mAttributionSource); + return Attributable.setAttributionSource( + mService.getMostRecentlyConnectedDevices(mAttributionSource), + mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2470,14 +2474,16 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Set getBondedDevices() { if (getState() != STATE_ON) { - return toDeviceSet(new BluetoothDevice[0]); + return toDeviceSet(Arrays.asList()); } try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices(mAttributionSource)); + return toDeviceSet(Attributable.setAttributionSource( + Arrays.asList(mService.getBondedDevices(mAttributionSource)), + mAttributionSource)); } - return toDeviceSet(new BluetoothDevice[0]); + return toDeviceSet(Arrays.asList()); } catch (RemoteException e) { Log.e(TAG, "", e); } finally { @@ -3165,10 +3171,10 @@ public final class BluetoothAdapter { } } } - synchronized (sMetadataListeners) { - sMetadataListeners.forEach((device, pair) -> { + synchronized (mMetadataListeners) { + mMetadataListeners.forEach((device, pair) -> { try { - mService.registerMetadataListener(sBluetoothMetadataListener, + mService.registerMetadataListener(mBluetoothMetadataListener, device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to register metadata listener", e); @@ -3455,8 +3461,8 @@ public final class BluetoothAdapter { } } - private Set toDeviceSet(BluetoothDevice[] devices) { - Set deviceSet = new HashSet(Arrays.asList(devices)); + private Set toDeviceSet(List devices) { + Set deviceSet = new HashSet(devices); return Collections.unmodifiableSet(deviceSet); } @@ -3926,13 +3932,13 @@ public final class BluetoothAdapter { throw new NullPointerException("executor is null"); } - synchronized (sMetadataListeners) { + synchronized (mMetadataListeners) { List> listenerList = - sMetadataListeners.get(device); + mMetadataListeners.get(device); if (listenerList == null) { // Create new listener/executor list for registeration listenerList = new ArrayList<>(); - sMetadataListeners.put(device, listenerList); + mMetadataListeners.put(device, listenerList); } else { // Check whether this device was already registed by the lisenter if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { @@ -3946,7 +3952,7 @@ public final class BluetoothAdapter { boolean ret = false; try { - ret = service.registerMetadataListener(sBluetoothMetadataListener, device, + ret = service.registerMetadataListener(mBluetoothMetadataListener, device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "registerMetadataListener fail", e); @@ -3956,7 +3962,7 @@ public final class BluetoothAdapter { listenerList.remove(listenerPair); if (listenerList.isEmpty()) { // Remove the device if its listener list is empty - sMetadataListeners.remove(device); + mMetadataListeners.remove(device); } } } @@ -3995,17 +4001,17 @@ public final class BluetoothAdapter { throw new NullPointerException("listener is null"); } - synchronized (sMetadataListeners) { - if (!sMetadataListeners.containsKey(device)) { + synchronized (mMetadataListeners) { + if (!mMetadataListeners.containsKey(device)) { throw new IllegalArgumentException("device was not registered"); } // Remove issued listener from the registered device - sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); + mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - if (sMetadataListeners.get(device).isEmpty()) { + if (mMetadataListeners.get(device).isEmpty()) { // Unregister to Bluetooth service if all listeners are removed from // the registered device - sMetadataListeners.remove(device); + mMetadataListeners.remove(device); final IBluetooth service = mService; if (service == null) { // Bluetooth is OFF, do nothing to Bluetooth service. @@ -4045,6 +4051,7 @@ public final class BluetoothAdapter { new IBluetoothConnectionCallback.Stub() { @Override public void onDeviceConnected(BluetoothDevice device) { + Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); @@ -4055,6 +4062,7 @@ public final class BluetoothAdapter { @Override public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { + Attributable.setAttributionSource(device, mAttributionSource); for (Map.Entry callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 0b43e71aba2..d27c2764800 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -135,7 +136,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -158,7 +159,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f2496fe0cac..21ec91865d0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -32,6 +32,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.companion.AssociationRequest; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Build; @@ -47,7 +48,6 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.List; import java.util.UUID; /** @@ -84,7 +84,7 @@ import java.util.UUID; * {@see BluetoothAdapter} * {@see BluetoothSocket} */ -public final class BluetoothDevice implements Parcelable { +public final class BluetoothDevice implements Parcelable, Attributable { private static final String TAG = "BluetoothDevice"; private static final boolean DBG = false; @@ -1170,28 +1170,13 @@ public final class BluetoothDevice implements Parcelable { mAttributionSource = BluetoothManager.resolveAttributionSource(null); } - void setAttributionSource(AttributionSource attributionSource) { + /** {@hide} */ + public void setAttributionSource(@NonNull AttributionSource attributionSource) { mAttributionSource = attributionSource; } - static BluetoothDevice setAttributionSource(BluetoothDevice device, - AttributionSource attributionSource) { - device.setAttributionSource(attributionSource); - return device; - } - - static List setAttributionSource(List devices, - AttributionSource attributionSource) { - if (devices != null) { - for (BluetoothDevice device : devices) { - device.setAttributionSource(attributionSource); - } - } - return devices; - } - /** {@hide} */ - public void prepareToEnterProcess(AttributionSource attributionSource) { + public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) { setAttributionSource(attributionSource); } diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 3bf517c046f..42b9a8d2e35 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; @@ -532,8 +533,9 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( - service.getConnectedDevices(), mAttributionSource); + return Attributable.setAttributionSource( + service.getConnectedDevicesWithAttribution(mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList(); @@ -554,7 +556,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { @@ -1310,7 +1312,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getActiveDevice(mAttributionSource); + return Attributable.setAttributionSource( + service.getActiveDevice(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 0059cdb9fbb..83108d22e0c 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -535,7 +536,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { 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())); @@ -562,7 +563,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { @@ -797,7 +798,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getCurrentCalls(device, mAttributionSource); + return Attributable.setAttributionSource( + service.getCurrentCalls(device, mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1020,7 +1022,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.dial(device, number, mAttributionSource); + return Attributable.setAttributionSource( + service.dial(device, number, mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 219d1596fbf..3f1ef846125 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -16,7 +16,10 @@ package android.bluetooth; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; +import android.content.AttributionSource; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -30,7 +33,7 @@ import java.util.UUID; * * @hide */ -public final class BluetoothHeadsetClientCall implements Parcelable { +public final class BluetoothHeadsetClientCall implements Parcelable, Attributable { /* Call state */ /** @@ -98,6 +101,11 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mCreationElapsedMilli = SystemClock.elapsedRealtime(); } + /** {@hide} */ + public void setAttributionSource(@NonNull AttributionSource attributionSource) { + Attributable.setAttributionSource(mDevice, attributionSource); + } + /** * Sets call's state. * diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 3ff2ebd86b6..183f4d55bde 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -248,7 +249,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -271,7 +272,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } @@ -363,7 +364,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getActiveDevices(mAttributionSource); + return Attributable.setAttributionSource( + service.getActiveDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index 11e5711eff1..c2744b89aad 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -20,11 +20,11 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SystemApi; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -339,14 +339,17 @@ public final class BluetoothHidDevice implements BluetoothProfile { private final Executor mExecutor; private final Callback mCallback; + private final AttributionSource mAttributionSource; - CallbackWrapper(Executor executor, Callback callback) { + CallbackWrapper(Executor executor, Callback callback, AttributionSource attributionSource) { mExecutor = executor; mCallback = callback; + mAttributionSource = attributionSource; } @Override public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { + Attributable.setAttributionSource(pluggedDevice, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); @@ -357,6 +360,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onConnectionStateChanged(BluetoothDevice device, int state) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); @@ -367,6 +371,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); @@ -377,6 +382,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); @@ -387,6 +393,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onSetProtocol(BluetoothDevice device, byte protocol) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); @@ -397,6 +404,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); @@ -407,6 +415,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { @Override public void onVirtualCableUnplug(BluetoothDevice device) { + Attributable.setAttributionSource(device, mAttributionSource); final long token = clearCallingIdentity(); try { mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); @@ -449,7 +458,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); @@ -469,7 +478,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { @@ -549,7 +558,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - CallbackWrapper cbw = new CallbackWrapper(executor, callback); + CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource); result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 0abe18c4d22..fb4cbb2eb13 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -26,6 +26,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -360,7 +361,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -384,7 +385,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 51bfd048fdc..9de27ff97fe 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -227,7 +228,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -250,7 +251,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } @@ -339,7 +340,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getActiveDevices(mAttributionSource); + return Attributable.setAttributionSource( + service.getActiveDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index b13ccaf7e57..c21362cd89f 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; @@ -24,6 +26,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManager; @@ -71,7 +74,7 @@ public final class BluetoothManager { } /** {@hide} */ - public static AttributionSource resolveAttributionSource(Context context) { + public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) { AttributionSource res = null; if (context != null) { res = context.getAttributionSource(); @@ -193,7 +196,7 @@ public final class BluetoothManager { IBluetoothManager managerService = mAdapter.getBluetoothManager(); IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; - devices = BluetoothDevice.setAttributionSource( + devices = Attributable.setAttributionSource( iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 88505b51f83..86796519df4 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -170,7 +171,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.getClient(mAttributionSource); + return Attributable.setAttributionSource( + service.getClient(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -285,7 +287,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap 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())); @@ -310,7 +312,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 14804dbefeb..042b58669a0 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) { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index d1f34cbe037..577be3d2aea 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -357,7 +358,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -384,7 +385,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 8ce01a37cdd..c000e56e7b0 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -25,6 +25,7 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; @@ -243,7 +244,7 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); @@ -296,7 +297,7 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList(); } try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index 3ebd8fe1920..c7dd6bd9af1 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -187,7 +188,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient 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())); @@ -215,7 +216,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index 0631abdadac..fda19ed6d0c 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -24,6 +24,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; @@ -182,7 +183,8 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.getClient(mAttributionSource); + return Attributable.setAttributionSource( + service.getClient(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -268,7 +270,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap 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())); @@ -292,7 +294,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { - return BluetoothDevice.setAttributionSource( + return Attributable.setAttributionSource( service.getDevicesMatchingConnectionStates(states, mAttributionSource), mAttributionSource); } catch (RemoteException e) { diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 60d4e2d6d2d..34aac8bfdb2 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -30,6 +30,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; @@ -516,6 +517,7 @@ public final class BluetoothLeScanner { */ @Override public void onScanResult(final ScanResult scanResult) { + Attributable.setAttributionSource(scanResult, mAttributionSource); if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped @@ -533,6 +535,7 @@ public final class BluetoothLeScanner { @Override public void onBatchScanResults(final List results) { + Attributable.setAttributionSource(results, mAttributionSource); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override @@ -544,6 +547,7 @@ public final class BluetoothLeScanner { @Override public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { + Attributable.setAttributionSource(scanResult, mAttributionSource); if (VDBG) { Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); } diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 47f47bb8e36..dea686d18ea 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -25,6 +25,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.Attributable; import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; @@ -220,7 +221,7 @@ public final class PeriodicAdvertisingManager { return new IPeriodicAdvertisingCallback.Stub() { public void onSyncEstablished(int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status) { - + Attributable.setAttributionSource(device, mAttributionSource); handler.post(new Runnable() { @Override public void run() { diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 57dad1a0259..52284562848 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -16,8 +16,11 @@ package android.bluetooth.le; +import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothDevice; +import android.content.Attributable; +import android.content.AttributionSource; import android.os.Parcel; import android.os.Parcelable; @@ -26,7 +29,7 @@ import java.util.Objects; /** * ScanResult for Bluetooth LE scan. */ -public final class ScanResult implements Parcelable { +public final class ScanResult implements Parcelable, Attributable { /** * For chained advertisements, inidcates tha the data contained in this @@ -195,6 +198,11 @@ public final class ScanResult implements Parcelable { return 0; } + /** {@hide} */ + public void setAttributionSource(@NonNull AttributionSource attributionSource) { + Attributable.setAttributionSource(mDevice, attributionSource); + } + /** * Returns the remote Bluetooth device identified by the Bluetooth device address. */ -- GitLab From db38eb73db2ffef87bcab51aa8ceb7d6fa6a3ca0 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 24 May 2021 10:00:23 -0600 Subject: [PATCH 1289/1408] CloseGuard for more Bluetooth components. We've seen evidence of IBluetoothProfileServiceConnection and IBluetoothStateChangeCallback references being leaked, so attempt to unregister them when an object is finalized without closing. Bug: 189091551 Test: manual Change-Id: I23792d48d94578acd7fc7a5164a95171801ee721 --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++++++++ .../android/bluetooth/BluetoothProfileConnector.java | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 3bf517c046f..5be83269499 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.CloseGuard; import android.util.Log; import java.util.ArrayList; @@ -338,6 +339,8 @@ public final class BluetoothHeadset implements BluetoothProfile { private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; + private final CloseGuard mCloseGuard = new CloseGuard(); + private Context mContext; private ServiceListener mServiceListener; private volatile IBluetoothHeadset mService; @@ -385,6 +388,7 @@ public final class BluetoothHeadset implements BluetoothProfile { } doBind(); + mCloseGuard.open("close"); } private boolean doBind() { @@ -438,6 +442,14 @@ public final class BluetoothHeadset implements BluetoothProfile { } mServiceListener = null; doUnbind(); + mCloseGuard.close(); + } + + /** {@hide} */ + @Override + protected void finalize() throws Throwable { + mCloseGuard.warnIfOpen(); + close(); } /** diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index beff841f2fc..a254291f57d 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.util.CloseGuard; import android.util.Log; /** @@ -36,6 +37,7 @@ import android.util.Log; */ @SuppressLint("AndroidFrameworkBluetoothPermission") public abstract class BluetoothProfileConnector { + private final CloseGuard mCloseGuard = new CloseGuard(); private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; private final BluetoothProfile mProfileProxy; @@ -82,11 +84,19 @@ public abstract class BluetoothProfileConnector { mServiceName = serviceName; } + /** {@hide} */ + @Override + public void finalize() { + mCloseGuard.warnIfOpen(); + doUnbind(); + } + @SuppressLint("AndroidFrameworkRequiresPermission") private boolean doBind() { synchronized (mConnection) { if (mService == null) { logDebug("Binding service..."); + mCloseGuard.open("doUnbind"); try { Intent intent = new Intent(mServiceName); ComponentName comp = intent.resolveSystemService( @@ -110,6 +120,7 @@ public abstract class BluetoothProfileConnector { synchronized (mConnection) { if (mService != null) { logDebug("Unbinding service..."); + mCloseGuard.close(); try { mContext.unbindService(mConnection); } catch (IllegalArgumentException ie) { -- GitLab From 1b3ac7733b50556d440ec8a5453645c074301c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Tue, 26 Jan 2021 06:39:08 +0000 Subject: [PATCH 1290/1408] Bluetooth: add Volume Control Profile boilerpalate This is very simple API to allow Android to connect VCP profile. Bug: 150670922 Test: compilation Sponsor: jpawlowski@ CTS-Coverage-Bug: 190833351 Change-Id: Ib20d967fcf6797077abf83b40b0eda526e5ab89d Merged-In: Ib20d967fcf6797077abf83b40b0eda526e5ab89d --- .../android/bluetooth/BluetoothAdapter.java | 8 + .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/BluetoothVolumeControl.java | 320 ++++++++++++++++++ 3 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothVolumeControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 63221c5d894..0dd6b4f6365 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2815,6 +2815,9 @@ public final class BluetoothAdapter { return true; } return false; + } else if (profile == BluetoothProfile.VOLUME_CONTROL) { + BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); + return true; } else { return false; } @@ -2899,6 +2902,11 @@ public final class BluetoothAdapter { case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); + break; + case BluetoothProfile.VOLUME_CONTROL: + BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; + vcs.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 201d6c495d9..d9791026ad6 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -214,13 +214,19 @@ public interface BluetoothProfile { */ int LE_AUDIO = 22; + /** + * Volume Control profile + * + */ + int VOLUME_CONTROL = 23; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 22; + int MAX_PROFILE_ID = 23; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java new file mode 100644 index 00000000000..4eb28ad3995 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -0,0 +1,320 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth Volume Control service. + * + *

    BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothVolumeControl proxy object. + * @hide + */ +@SystemApi +public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothVolumeControl"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * Intent used to broadcast the change in connection state of the Volume Control + * profile. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED"; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG, + IBluetoothVolumeControl.class.getName()) { + @Override + public IBluetoothVolumeControl getServiceInterface(IBinder service) { + return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothVolumeControl proxy object for interacting with the local + * Bluetooth Volume Control service. + */ + /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /*package*/ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); } + + /** + * Get the list of connected devices. Currently at most one. + * + * @return list of connected devices + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull List getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled()) { + try { + return service.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == 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 + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled()) { + try { + return service.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * Get connection state of device + * + * @return device connection state + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getConnectionState(" + device + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Tells remote device to set an absolute volume. + * + * @param volume Absolute volume to be set on remote device. + * Minimum value is 0 and maximum value is 255 + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void setVolume(@Nullable BluetoothDevice device, + @IntRange(from = 0, to = 255) int volume) { + if (DBG) + log("setVolume(" + volume + ")"); + final IBluetoothVolumeControl service = getService(); + try { + if (service != null && isEnabled()) { + service.setVolume(device, volume); + return; + } + if (service == null) + Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + } + + /** + * 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 + * @param priority + * @return true if priority is set, false on error + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + try { + return service.setConnectionPolicy(device, connectionPolicy); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == 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_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getPriority(BluetoothDevice device) { + if (VDBG) log("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(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionPolicy(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} -- GitLab From 3728cb64ab3dfbcb0edcf6ed52613ae348b8c814 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 10 Jun 2021 17:28:38 -0700 Subject: [PATCH 1291/1408] Update nullability checks to use Objects#requireNonNull instead of deprecated method in Preconditions class Tag: #feature Bug: 190767948 Test: Manual Change-Id: Ie7f7282b89c13f587fdfe1bf3288eb4a3c7dcc6e --- .../android/bluetooth/BluetoothAdapter.java | 12 ++++----- framework/java/android/bluetooth/OobData.java | 26 +++++++++---------- .../java/android/bluetooth/le/ScanFilter.java | 7 ++--- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 054b63fbad5..ded5e6e27a6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -17,6 +17,8 @@ package android.bluetooth; +import static java.util.Objects.requireNonNull; + import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; @@ -62,8 +64,6 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; -import com.android.internal.util.Preconditions; - import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -3314,8 +3314,8 @@ public final class BluetoothAdapter { */ WrappedOobDataCallback(@NonNull OobDataCallback callback, @NonNull @CallbackExecutor Executor executor) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(executor); + requireNonNull(callback); + requireNonNull(executor); mCallback = callback; mExecutor = executor; } @@ -3385,7 +3385,7 @@ public final class BluetoothAdapter { != BluetoothDevice.TRANSPORT_LE) { throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); } - Preconditions.checkNotNull(callback); + requireNonNull(callback); if (!isEnabled()) { Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); @@ -3522,7 +3522,7 @@ public final class BluetoothAdapter { * @hide */ public static boolean isAddressRandomStatic(@NonNull String address) { - Preconditions.checkNotNull(address); + requireNonNull(address); return checkBluetoothAddress(address) && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 2dfa91dcba3..4e5ede74ce4 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static java.util.Objects.requireNonNull; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,8 +25,6 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.util.Preconditions; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -214,7 +214,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setDeviceName(@NonNull byte[] deviceName) { - Preconditions.checkNotNull(deviceName); + requireNonNull(deviceName); this.mDeviceName = deviceName; return this; } @@ -308,8 +308,8 @@ public final class OobData implements Parcelable { @SystemApi public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { - Preconditions.checkNotNull(confirmationHash); - Preconditions.checkNotNull(deviceAddressWithType); + requireNonNull(confirmationHash); + requireNonNull(deviceAddressWithType); if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { throw new IllegalArgumentException("confirmationHash must be " + OobData.CONFIRMATION_OCTETS + " octets in length."); @@ -344,7 +344,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) { - Preconditions.checkNotNull(leTemporaryKey); + requireNonNull(leTemporaryKey); if (leTemporaryKey.length != LE_TK_OCTETS) { throw new IllegalArgumentException("leTemporaryKey must be " + LE_TK_OCTETS + " octets in length."); @@ -366,7 +366,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - Preconditions.checkNotNull(randomizerHash); + requireNonNull(randomizerHash); if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { throw new IllegalArgumentException("randomizerHash must be " + OobData.RANDOMIZER_OCTETS + " octets in length."); @@ -534,9 +534,9 @@ public final class OobData implements Parcelable { @SystemApi public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { - Preconditions.checkNotNull(confirmationHash); - Preconditions.checkNotNull(classicLength); - Preconditions.checkNotNull(deviceAddressWithType); + requireNonNull(confirmationHash); + requireNonNull(classicLength); + requireNonNull(deviceAddressWithType); if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { throw new IllegalArgumentException("confirmationHash must be " + OobData.CONFIRMATION_OCTETS + " octets in length."); @@ -567,7 +567,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - Preconditions.checkNotNull(randomizerHash); + requireNonNull(randomizerHash); if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { throw new IllegalArgumentException("randomizerHash must be " + OobData.RANDOMIZER_OCTETS + " octets in length."); @@ -592,7 +592,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) { - Preconditions.checkNotNull(deviceName); + requireNonNull(deviceName); this.mDeviceName = deviceName; return this; } @@ -617,7 +617,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) { - Preconditions.checkNotNull(classOfDevice); + requireNonNull(classOfDevice); if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) { throw new IllegalArgumentException("classOfDevice must be " + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length."); diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index dfef47ddc16..cb3bf297670 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,6 +16,8 @@ package android.bluetooth.le; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -27,7 +29,6 @@ import android.os.ParcelUuid; import android.os.Parcelable; import com.android.internal.util.BitUtils; -import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.List; @@ -646,7 +647,7 @@ public final class ScanFilter implements Parcelable { public Builder setDeviceAddress(@NonNull String deviceAddress, @AddressType int addressType, @NonNull byte[] irk) { - Preconditions.checkNotNull(irk); + requireNonNull(irk); if (irk.length != LEN_IRK_OCTETS) { throw new IllegalArgumentException("'irk' is invalid length!"); } @@ -678,7 +679,7 @@ public final class ScanFilter implements Parcelable { @Nullable byte[] irk) { // Make sure our deviceAddress is valid! - Preconditions.checkNotNull(deviceAddress); + requireNonNull(deviceAddress); if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { throw new IllegalArgumentException("invalid device address " + deviceAddress); } -- GitLab From 508506d886e5bc53c0b39ffabaaa8d4aa9881551 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Thu, 10 Jun 2021 17:28:38 -0700 Subject: [PATCH 1292/1408] Update nullability checks to use Objects#requireNonNull instead of deprecated method in Preconditions class Tag: #feature Bug: 190767948 Test: Manual Merged-In: Ie7f7282b89c13f587fdfe1bf3288eb4a3c7dcc6e Change-Id: Ie7f7282b89c13f587fdfe1bf3288eb4a3c7dcc6e --- .../android/bluetooth/BluetoothAdapter.java | 12 ++++----- framework/java/android/bluetooth/OobData.java | 26 +++++++++---------- .../java/android/bluetooth/le/ScanFilter.java | 8 +++--- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 63221c5d894..331fd07a6af 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -17,6 +17,8 @@ package android.bluetooth; +import static java.util.Objects.requireNonNull; + import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -53,8 +55,6 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; -import com.android.internal.util.Preconditions; - import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -3091,8 +3091,8 @@ public final class BluetoothAdapter { */ WrappedOobDataCallback(@NonNull OobDataCallback callback, @NonNull @CallbackExecutor Executor executor) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(executor); + requireNonNull(callback); + requireNonNull(executor); mCallback = callback; mExecutor = executor; } @@ -3158,7 +3158,7 @@ public final class BluetoothAdapter { != BluetoothDevice.TRANSPORT_LE) { throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); } - Preconditions.checkNotNull(callback); + requireNonNull(callback); if (!isEnabled()) { Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); callback.onError(OOB_ERROR_ADAPTER_DISABLED); @@ -3293,7 +3293,7 @@ public final class BluetoothAdapter { * @hide */ public static boolean isAddressRandomStatic(@NonNull String address) { - Preconditions.checkNotNull(address); + requireNonNull(address); return checkBluetoothAddress(address) && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 2dfa91dcba3..4e5ede74ce4 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static java.util.Objects.requireNonNull; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,8 +25,6 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.util.Preconditions; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -214,7 +214,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setDeviceName(@NonNull byte[] deviceName) { - Preconditions.checkNotNull(deviceName); + requireNonNull(deviceName); this.mDeviceName = deviceName; return this; } @@ -308,8 +308,8 @@ public final class OobData implements Parcelable { @SystemApi public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole) { - Preconditions.checkNotNull(confirmationHash); - Preconditions.checkNotNull(deviceAddressWithType); + requireNonNull(confirmationHash); + requireNonNull(deviceAddressWithType); if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { throw new IllegalArgumentException("confirmationHash must be " + OobData.CONFIRMATION_OCTETS + " octets in length."); @@ -344,7 +344,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) { - Preconditions.checkNotNull(leTemporaryKey); + requireNonNull(leTemporaryKey); if (leTemporaryKey.length != LE_TK_OCTETS) { throw new IllegalArgumentException("leTemporaryKey must be " + LE_TK_OCTETS + " octets in length."); @@ -366,7 +366,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - Preconditions.checkNotNull(randomizerHash); + requireNonNull(randomizerHash); if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { throw new IllegalArgumentException("randomizerHash must be " + OobData.RANDOMIZER_OCTETS + " octets in length."); @@ -534,9 +534,9 @@ public final class OobData implements Parcelable { @SystemApi public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { - Preconditions.checkNotNull(confirmationHash); - Preconditions.checkNotNull(classicLength); - Preconditions.checkNotNull(deviceAddressWithType); + requireNonNull(confirmationHash); + requireNonNull(classicLength); + requireNonNull(deviceAddressWithType); if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { throw new IllegalArgumentException("confirmationHash must be " + OobData.CONFIRMATION_OCTETS + " octets in length."); @@ -567,7 +567,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - Preconditions.checkNotNull(randomizerHash); + requireNonNull(randomizerHash); if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { throw new IllegalArgumentException("randomizerHash must be " + OobData.RANDOMIZER_OCTETS + " octets in length."); @@ -592,7 +592,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) { - Preconditions.checkNotNull(deviceName); + requireNonNull(deviceName); this.mDeviceName = deviceName; return this; } @@ -617,7 +617,7 @@ public final class OobData implements Parcelable { @NonNull @SystemApi public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) { - Preconditions.checkNotNull(classOfDevice); + requireNonNull(classOfDevice); if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) { throw new IllegalArgumentException("classOfDevice must be " + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length."); diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index a74c663a9ce..ddc93327b69 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -16,7 +16,8 @@ package android.bluetooth.le; -import android.annotation.IntDef; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -28,7 +29,6 @@ import android.os.ParcelUuid; import android.os.Parcelable; import com.android.internal.util.BitUtils; -import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.List; @@ -647,7 +647,7 @@ public final class ScanFilter implements Parcelable { public Builder setDeviceAddress(@NonNull String deviceAddress, @AddressType int addressType, @NonNull byte[] irk) { - Preconditions.checkNotNull(irk); + requireNonNull(irk); if (irk.length != LEN_IRK_OCTETS) { throw new IllegalArgumentException("'irk' is invalid length!"); } @@ -679,7 +679,7 @@ public final class ScanFilter implements Parcelable { @Nullable byte[] irk) { // Make sure our deviceAddress is valid! - Preconditions.checkNotNull(deviceAddress); + requireNonNull(deviceAddress); if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { throw new IllegalArgumentException("invalid device address " + deviceAddress); } -- GitLab From 0b3e97779759de878cc2b711e464a2e94f9ab387 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Wed, 19 May 2021 21:01:27 -0700 Subject: [PATCH 1293/1408] Fix NPE in logging Bug: 191268917 Test: Manual test app, log OobData Tag: #stability Change-Id: I975722642298cb580081de9a324302d7a3282d3c --- framework/java/android/bluetooth/OobData.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/OobData.java b/framework/java/android/bluetooth/OobData.java index 4e5ede74ce4..bb0b95649b1 100644 --- a/framework/java/android/bluetooth/OobData.java +++ b/framework/java/android/bluetooth/OobData.java @@ -937,17 +937,18 @@ public final class OobData implements Parcelable { } @NonNull - private String toHexString(@NonNull int b) { + private String toHexString(int b) { return toHexString(new byte[] {(byte) b}); } @NonNull - private String toHexString(@NonNull byte b) { + private String toHexString(byte b) { return toHexString(new byte[] {b}); } @NonNull - private String toHexString(@NonNull byte[] array) { + private String toHexString(byte[] array) { + if (array == null) return "null"; StringBuilder builder = new StringBuilder(array.length * 2); for (byte b: array) { builder.append(String.format("%02x", b)); -- GitLab From 2dd8f397af3e604736bdb7f8c1e9d16c6cf7e0ed Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 18 Jun 2021 09:01:42 +0200 Subject: [PATCH 1294/1408] DO NOT MERGE Revert "Bluetooth: add Volume Control Profile boilerpalate" This reverts commit 1b3ac7733b50556d440ec8a5453645c074301c7b. Merged-In: Ib20d967fcf6797077abf83b40b0eda526e5ab89d Change-Id: I164fcdeddab6579cf10174c1123984e856fa6f20 --- .../android/bluetooth/BluetoothAdapter.java | 8 - .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/BluetoothVolumeControl.java | 320 ------------------ 3 files changed, 1 insertion(+), 335 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothVolumeControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b0b247868b0..ded5e6e27a6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3054,9 +3054,6 @@ public final class BluetoothAdapter { return true; } return false; - } else if (profile == BluetoothProfile.VOLUME_CONTROL) { - BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); - return true; } else { return false; } @@ -3145,11 +3142,6 @@ public final class BluetoothAdapter { case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); - break; - case BluetoothProfile.VOLUME_CONTROL: - BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; - vcs.close(); - break; } } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f9f65d09c1b..161c843f039 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -212,19 +212,13 @@ public interface BluetoothProfile { */ int LE_AUDIO = 22; - /** - * Volume Control profile - * - */ - int VOLUME_CONTROL = 23; - /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 23; + int MAX_PROFILE_ID = 22; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java deleted file mode 100644 index 4eb28ad3995..00000000000 --- a/framework/java/android/bluetooth/BluetoothVolumeControl.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright 2021 HIMSA II K/S - www.himsa.com. - * Represented by EHIMA - www.ehima.com - * - * 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.Manifest; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class provides the public APIs to control the Bluetooth Volume Control service. - * - *

    BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothVolumeControl proxy object. - * @hide - */ -@SystemApi -public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable { - private static final String TAG = "BluetoothVolumeControl"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private CloseGuard mCloseGuard; - - /** - * Intent used to broadcast the change in connection state of the Volume Control - * profile. - * - *

    This intent will have 3 extras: - *

      - *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • - *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • - *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • - *
    - * - *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * @hide - */ - @SystemApi - @SuppressLint("ActionValue") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED"; - - private BluetoothAdapter mAdapter; - private final BluetoothProfileConnector mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG, - IBluetoothVolumeControl.class.getName()) { - @Override - public IBluetoothVolumeControl getServiceInterface(IBinder service) { - return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service)); - } - }; - - /** - * Create a BluetoothVolumeControl proxy object for interacting with the local - * Bluetooth Volume Control service. - */ - /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - } - - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - /*package*/ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); } - - /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public @NonNull List getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); - final IBluetoothVolumeControl service = getService(); - if (service != null && isEnabled()) { - try { - return service.getConnectedDevices(); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == 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 - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public List getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothVolumeControl service = getService(); - if (service != null && isEnabled()) { - try { - return service.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return new ArrayList(); - } - - /** - * Get connection state of device - * - * @return device connection state - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothVolumeControl service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getConnectionState(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.STATE_DISCONNECTED; - } - - /** - * Tells remote device to set an absolute volume. - * - * @param volume Absolute volume to be set on remote device. - * Minimum value is 0 and maximum value is 255 - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void setVolume(@Nullable BluetoothDevice device, - @IntRange(from = 0, to = 255) int volume) { - if (DBG) - log("setVolume(" + volume + ")"); - final IBluetoothVolumeControl service = getService(); - try { - if (service != null && isEnabled()) { - service.setVolume(device, volume); - return; - } - if (service == null) - Log.w(TAG, "Proxy not attached to service"); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } - } - - /** - * 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 - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("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(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothVolumeControl service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - try { - return service.setConnectionPolicy(device, connectionPolicy); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; - } - } - if (service == 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_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("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(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothVolumeControl service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getConnectionPolicy(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(@Nullable BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} -- GitLab From 40ef4d5c4d3f72a207b4beeb9a21568b28aa9496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Tue, 26 Jan 2021 06:39:08 +0000 Subject: [PATCH 1295/1408] DO NOT MERGE Bluetooth: add Volume Control Profile boilerpalate This is very simple API to allow Android to connect VCP profile. Bug: 150670922 Test: compilation Sponsor: jpawlowski@ CTS-Coverage-Bug: 190833351 Merged-In: Ib20d967fcf6797077abf83b40b0eda526e5ab89d Change-Id: Ib20d967fcf6797077abf83b40b0eda526e5ab89d --- .../android/bluetooth/BluetoothAdapter.java | 8 + .../android/bluetooth/BluetoothProfile.java | 8 +- .../bluetooth/BluetoothVolumeControl.java | 355 ++++++++++++++++++ 3 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothVolumeControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ded5e6e27a6..b0b247868b0 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3054,6 +3054,9 @@ public final class BluetoothAdapter { return true; } return false; + } else if (profile == BluetoothProfile.VOLUME_CONTROL) { + BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); + return true; } else { return false; } @@ -3142,6 +3145,11 @@ public final class BluetoothAdapter { case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); + break; + case BluetoothProfile.VOLUME_CONTROL: + BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; + vcs.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 161c843f039..f9f65d09c1b 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -212,13 +212,19 @@ public interface BluetoothProfile { */ int LE_AUDIO = 22; + /** + * Volume Control profile + * + */ + int VOLUME_CONTROL = 23; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 22; + int MAX_PROFILE_ID = 23; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java new file mode 100644 index 00000000000..6d515a16bf0 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -0,0 +1,355 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.Attributable; +import android.content.AttributionSource; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth Volume Control service. + * + *

    BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothVolumeControl proxy object. + * @hide + */ +@SystemApi +public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothVolumeControl"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * Intent used to broadcast the change in connection state of the Volume Control + * profile. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + @RequiresBluetoothConnectPermission + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED"; + + private BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG, + IBluetoothVolumeControl.class.getName()) { + @Override + public IBluetoothVolumeControl getServiceInterface(IBinder service) { + return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothVolumeControl proxy object for interacting with the local + * Bluetooth Volume Control service. + */ + /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /*package*/ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); } + + /** + * Get the list of connected devices. Currently at most one. + * + * @return list of connected devices + * + * @hide + */ + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @NonNull List getConnectedDevices() { + if (DBG) log("getConnectedDevices()"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled()) { + try { + return Attributable.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == 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 + * + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + public List getDevicesMatchingConnectionStates(int[] states) { + if (DBG) log("getDevicesMatchingStates()"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled()) { + try { + return Attributable.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * Get connection state of device + * + * @return device connection state + * + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + public int getConnectionState(BluetoothDevice device) { + if (DBG) log("getConnectionState(" + device + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Tells remote device to set an absolute volume. + * + * @param volume Absolute volume to be set on remote device. + * Minimum value is 0 and maximum value is 255 + * @hide + */ + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public void setVolume(@Nullable BluetoothDevice device, + @IntRange(from = 0, to = 255) int volume) { + if (DBG) + log("setVolume(" + volume + ")"); + final IBluetoothVolumeControl service = getService(); + try { + if (service != null && isEnabled()) { + service.setVolume(device, volume, mAttributionSource); + return; + } + if (service == null) + Log.w(TAG, "Proxy not attached to service"); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + } + + /** + * 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 + * @param priority + * @return true if priority is set, false on error + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("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 + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + try { + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == 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_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public int getPriority(BluetoothDevice device) { + if (VDBG) log("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 + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); + final IBluetoothVolumeControl service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionPolicy(device, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} -- GitLab From 4dcf558a0fc5fa2bdf30f11f3b8a8f490c3afeda Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Thu, 6 May 2021 15:13:35 +0800 Subject: [PATCH 1296/1408] Unhide BluetoothProfile.LE_AUDIO and getGroupId for the App usage. This change contains two item, 1. public BluetoothProfile.LE_AUDIO that App can use BluetoothProfile.ServiceLister with LE Audio profile, such as HFP, A2DP and hearing aid profile. 2. public getGroupId API that App can use this api to identify which devices are in the same group Bug: 150670922 Test: Manual test Ignore-AOSP-First: prevent merge conflict Change-Id: I32865720a8195b7c5ae29411cd1f3de95e7fc9b5 --- framework/java/android/bluetooth/BluetoothLeAudio.java | 1 - framework/java/android/bluetooth/BluetoothProfile.java | 1 - 2 files changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 9de27ff97fe..c438dd34f81 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -356,7 +356,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * earbud) * @param device LE Audio capable device * @return group id that this device currently belongs to - * @hide */ @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index f9f65d09c1b..973ae3ce6bd 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -208,7 +208,6 @@ public interface BluetoothProfile { /** * LE Audio Device * - * @hide */ int LE_AUDIO = 22; -- GitLab From 56390d231ca7ac49c8e3e5b2af74a522e6975224 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 18 Jun 2021 10:31:26 -0700 Subject: [PATCH 1297/1408] Adds rahulsabnis@ as an owner for all Bluetooth files in frameworks/base Tag: #feature Bug: 191481598 Test: Manual Change-Id: I6c8606c8e4802b74e80555e8ebf78932863d3f5b --- framework/java/android/bluetooth/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS index 3523ee0640a..2239100a552 100644 --- a/framework/java/android/bluetooth/OWNERS +++ b/framework/java/android/bluetooth/OWNERS @@ -2,3 +2,4 @@ zachoverflow@google.com siyuanh@google.com +rahulsabnis@google.com -- GitLab From e284f6e699debacaaf4035cb6191eaa55b6b1e3d Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Fri, 18 Jun 2021 10:31:26 -0700 Subject: [PATCH 1298/1408] Adds rahulsabnis@ as an owner for all Bluetooth files in frameworks/base Tag: #feature Bug: 191481598 Test: Manual Merged-In: I6c8606c8e4802b74e80555e8ebf78932863d3f5b Change-Id: I6c8606c8e4802b74e80555e8ebf78932863d3f5b --- framework/java/android/bluetooth/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS index 3523ee0640a..2239100a552 100644 --- a/framework/java/android/bluetooth/OWNERS +++ b/framework/java/android/bluetooth/OWNERS @@ -2,3 +2,4 @@ zachoverflow@google.com siyuanh@google.com +rahulsabnis@google.com -- GitLab From 3d3c696f75aa6c6b4644899034f90a16e7ad75a7 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 18 Jun 2021 00:15:47 +0200 Subject: [PATCH 1299/1408] Bluetooth: fix comments on Volume Control Profile review Bug: 150670922 Test: compilation Merged-In: I72b36b77aa97aa9501d13ecdfe0b9b7f49bcce2c Change-Id: I72b36b77aa97aa9501d13ecdfe0b9b7f49bcce2c --- .../android/bluetooth/BluetoothProfile.java | 2 ++ .../bluetooth/BluetoothVolumeControl.java | 34 ------------------- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index d9791026ad6..4d93c5c6b7d 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -217,7 +217,9 @@ public interface BluetoothProfile { /** * Volume Control profile * + * @hide */ + @SystemApi int VOLUME_CONTROL = 23; /** diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java index 4eb28ad3995..e5092833d7a 100644 --- a/framework/java/android/bluetooth/BluetoothVolumeControl.java +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -107,7 +107,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose close(); } - /*package*/ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void close() { mProfileConnector.disconnect(); @@ -211,23 +210,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose } } - /** - * 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 - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - /** * Set connection policy of the profile * @@ -262,22 +244,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose return false; } - /** - * Get the priority of the profile. - * - *

    The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - /** * Get the connection policy of the profile. * -- GitLab From 807fd1cfb8a763074199be85e3096a2541ded8e9 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 18 Jun 2021 00:09:00 +0200 Subject: [PATCH 1300/1408] Bluetooth: fix comments on Volume Control Profile review Bug: 150670922 Test: compilation Merged-In: I72b36b77aa97aa9501d13ecdfe0b9b7f49bcce2c Change-Id: I72b36b77aa97aa9501d13ecdfe0b9b7f49bcce2c (cherry picked from commit dc50891255ed0168d6068b328b48cc027cd97ca5) --- .../android/bluetooth/BluetoothProfile.java | 2 + .../bluetooth/BluetoothVolumeControl.java | 42 ------------------- 2 files changed, 2 insertions(+), 42 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 973ae3ce6bd..83a272fce31 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -214,7 +214,9 @@ public interface BluetoothProfile { /** * Volume Control profile * + * @hide */ + @SystemApi int VOLUME_CONTROL = 23; /** diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java index 6d515a16bf0..678c11a59f3 100644 --- a/framework/java/android/bluetooth/BluetoothVolumeControl.java +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -113,7 +113,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose close(); } - /*package*/ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public void close() { mProfileConnector.disconnect(); @@ -230,27 +229,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose } } - /** - * 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 - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - /** * Set connection policy of the profile * @@ -289,26 +267,6 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose return false; } - /** - * Get the priority of the profile. - * - *

    The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - /** * Get the connection policy of the profile. * -- GitLab From 0804461a972d16e81978d1aba8b344958998d4b1 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Thu, 6 May 2021 15:13:35 +0800 Subject: [PATCH 1301/1408] Unhide BluetoothProfile.LE_AUDIO and getGroupId for the App usage. This change contains two item, 1. public BluetoothProfile.LE_AUDIO that App can use BluetoothProfile.ServiceLister with LE Audio profile, such as HFP, A2DP and hearing aid profile. 2. public getGroupId API that App can use this api to identify which devices are in the same group Bug: 150670922 Test: Manual test Change-Id: I32865720a8195b7c5ae29411cd1f3de95e7fc9b5 Merged-In: I32865720a8195b7c5ae29411cd1f3de95e7fc9b5 --- framework/java/android/bluetooth/BluetoothLeAudio.java | 1 - framework/java/android/bluetooth/BluetoothProfile.java | 1 - 2 files changed, 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 3f00fa6f418..75fcab93593 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -335,7 +335,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * earbud) * @param device LE Audio capable device * @return group id that this device currently belongs to - * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getGroupId(@NonNull BluetoothDevice device) { diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 4d93c5c6b7d..b76d6b86913 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -210,7 +210,6 @@ public interface BluetoothProfile { /** * LE Audio Device * - * @hide */ int LE_AUDIO = 22; -- GitLab From fe3d6da465f03900006100efa8618502d16334ff Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 23 Jun 2021 15:09:45 -0700 Subject: [PATCH 1302/1408] Add BluetoothLeAudio to BluetoothAdapter#getProfileProxy and BluetoothAdapter#closeProfileProxy Tag: #feature Bug: 15083918 Test: Manual Merged-In: Ia46dc4e50d42dbd574588b531045cb680aa09d94 Change-Id: Ia46dc4e50d42dbd574588b531045cb680aa09d94 --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ded5e6e27a6..5b72b76c357 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3054,6 +3054,9 @@ public final class BluetoothAdapter { return true; } return false; + } else if (profile == BluetoothProfile.LE_AUDIO) { + BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); + return true; } else { return false; } @@ -3142,6 +3145,10 @@ public final class BluetoothAdapter { case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); + break; + case BluetoothProfile.LE_AUDIO: + BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; + leAudio.close(); } } -- GitLab From 842f394c3d34f5e5779f027bdcde8fcaccd195bd Mon Sep 17 00:00:00 2001 From: Johanna Ye Date: Tue, 29 Jun 2021 16:19:53 +0200 Subject: [PATCH 1303/1408] Add synchronization to all mDeviceBusy state changes. Tag: #stability Test: make Bug: 169559728 Change-Id: I5c1b926a4c774b4bc5f24a9aaa66aa5f7780e121 --- .../java/android/bluetooth/BluetoothGatt.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 381318b26da..1d08ddb65cf 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1153,7 +1153,9 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } @@ -1187,7 +1189,9 @@ public final class BluetoothGatt implements BluetoothProfile { new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } @@ -1234,7 +1238,9 @@ public final class BluetoothGatt implements BluetoothProfile { AUTHENTICATION_NONE, characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } @@ -1276,7 +1282,9 @@ public final class BluetoothGatt implements BluetoothProfile { descriptor.getInstanceId(), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } @@ -1317,7 +1325,9 @@ public final class BluetoothGatt implements BluetoothProfile { AUTHENTICATION_NONE, descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } @@ -1384,7 +1394,9 @@ public final class BluetoothGatt implements BluetoothProfile { mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } -- GitLab From 8bc7d18ae11c21a37e7eb9cc034a69cc936bdea9 Mon Sep 17 00:00:00 2001 From: Johanna Ye Date: Wed, 30 Jun 2021 17:31:01 +0200 Subject: [PATCH 1304/1408] Fix long characteristic write concurrency bug. Tag: #stability Test: Bramble (pixel phone) with test app Bug: 169559728 Ignore-AOSP-First: Current security annotation parity Merged-In: I3e3a5ff0965c1ca4f79e70e6163bc5d6917edbd1 Change-Id: Ia2d821c5d34d8d035fbbb0b33b9bf4fc8c08b354 --- .../java/android/bluetooth/BluetoothGatt.java | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1d08ddb65cf..b3a7c8830d7 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -75,6 +75,9 @@ public final class BluetoothGatt implements BluetoothProfile { private static final int CONN_STATE_DISCONNECTING = 3; private static final int CONN_STATE_CLOSED = 4; + private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5; + private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds + private List mServices; /** A GATT operation completed successfully */ @@ -126,6 +129,27 @@ public final class BluetoothGatt implements BluetoothProfile { /** Connection parameter update - Request low power, reduced data rate connection parameters. */ public static final int CONNECTION_PRIORITY_LOW_POWER = 2; + /** + * A GATT writeCharacteristic request is started successfully. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_SUCCESS = 0; + + /** + * A GATT writeCharacteristic request failed to start. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_FAIL = 1; + + /** + * A GATT writeCharacteristic request is issued to a busy remote device. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_BUSY = 2; + /** * No authentication required. * @@ -428,9 +452,19 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.writeCharacteristic(mClientIf, address, handle, - characteristic.getWriteType(), authReq, - characteristic.getValue()); + int requestStatus = GATT_WRITE_REQUEST_FAIL; + for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { + requestStatus = mService.writeCharacteristic(mClientIf, address, + handle, characteristic.getWriteType(), authReq, + characteristic.getValue()); + if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + break; + } + try { + Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); + } catch (InterruptedException e) { + } + } mAuthRetryState++; return; } catch (RemoteException e) { @@ -1228,14 +1262,26 @@ public final class BluetoothGatt implements BluetoothProfile { if (device == null) return false; synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; + if (mDeviceBusy) { + return false; + } mDeviceBusy = true; } + int requestStatus = GATT_WRITE_REQUEST_FAIL; try { - mService.writeCharacteristic(mClientIf, device.getAddress(), + for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { + requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(), characteristic.getInstanceId(), characteristic.getWriteType(), AUTHENTICATION_NONE, characteristic.getValue()); + if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + break; + } + try { + Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); + } catch (InterruptedException e) { + } + } } catch (RemoteException e) { Log.e(TAG, "", e); synchronized (mDeviceBusyLock) { @@ -1244,7 +1290,7 @@ public final class BluetoothGatt implements BluetoothProfile { return false; } - return true; + return requestStatus == GATT_WRITE_REQUEST_SUCCESS; } /** -- GitLab From 5708b05dd7c110aa2cbf99d0902ba93140f71498 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 19 May 2021 21:18:59 -0700 Subject: [PATCH 1305/1408] Update BluetoothAdapter and BluetoothDevice documentation Update BluetoothAdapter#startDiscovery and BluetoothDevice#fetchUuidsWithSdp documentation to indicate that it queues the request if a device is currently bonding Tag: #feature Bug: 187165224 Test: Manual Change-Id: I3dbcdacff062f6c33c2fdc8d64170bf60b2fbf6f --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 ++++--- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e305aa8e33a..ce384868d44 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1698,9 +1698,10 @@ public final class BluetoothAdapter { * discoverable (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. *

    If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. + * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED} + * with {@link #STATE_ON} to get the updated value. + *

    If a device is currently bonding, this request will be queued and executed once that + * device has finished bonding. If a request is already queued, this request will be ignored. * * @return true on success, false on error */ diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 11b45e32c42..07dbe52a2c6 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1608,7 +1608,8 @@ public final class BluetoothDevice implements Parcelable { * in getting the SDP records or if the process takes a long time, or the device is bonding and * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. + * if service discovery is not to be performed. If there is an ongoing bonding process, + * service discovery or device inquiry, the request will be queued. * * @return False if the check fails, True if the process of initiating an ACL connection * to the remote device was started or cached UUIDs will be broadcast. -- GitLab From 2cae3b9cd95f617b366621a6a3c9123e2605002e Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Thu, 1 Jul 2021 22:39:36 +0000 Subject: [PATCH 1306/1408] Update BluetoothAdapter and BluetoothDevice documentation Update BluetoothAdapter#startDiscovery and BluetoothDevice#fetchUuidsWithSdp documentation to indicate that it queues the request if a device is currently bonding Tag: #feature Bug: 187165224 Test: Manual Merged-In: I3dbcdacff062f6c33c2fdc8d64170bf60b2fbf6f Change-Id: I7e598417ba96a5acc9f13fb6d29a0612740f31b4 --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 ++++--- framework/java/android/bluetooth/BluetoothDevice.java | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 5b72b76c357..421b4de9d62 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1799,9 +1799,10 @@ public final class BluetoothAdapter { * discoverable (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. *

    If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. + * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED} + * with {@link #STATE_ON} to get the updated value. + *

    If a device is currently bonding, this request will be queued and executed once that + * device has finished bonding. If a request is already queued, this request will be ignored. * * @return true on success, false on error */ diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 21ec91865d0..bbb550fd634 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1775,7 +1775,8 @@ public final class BluetoothDevice implements Parcelable, Attributable { * in getting the SDP records or if the process takes a long time, or the device is bonding and * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. + * if service discovery is not to be performed. If there is an ongoing bonding process, + * service discovery or device inquiry, the request will be queued. * * @return False if the check fails, True if the process of initiating an ACL connection * to the remote device was started or cached UUIDs will be broadcast. -- GitLab From 0140aae62534c1c6f15edb3d32193a0a70b8ef45 Mon Sep 17 00:00:00 2001 From: Johanna Ye Date: Tue, 22 Jun 2021 15:23:03 +0200 Subject: [PATCH 1307/1408] Fix long characteristic write concurrency bug. Tag: #stability Test: ADT3 with test app Bug: 169559728 Ignore-AOSP-First: Current security annotation parity Merged-In: I3e3a5ff0965c1ca4f79e70e6163bc5d6917edbd1 Change-Id: I3e3a5ff0965c1ca4f79e70e6163bc5d6917edbd1 --- .../java/android/bluetooth/BluetoothGatt.java | 62 ++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index aea82102ca3..e6457f7140b 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -82,6 +82,9 @@ public final class BluetoothGatt implements BluetoothProfile { private static final int CONN_STATE_DISCONNECTING = 3; private static final int CONN_STATE_CLOSED = 4; + private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5; + private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds + private List mServices; /** A GATT operation completed successfully */ @@ -133,6 +136,27 @@ public final class BluetoothGatt implements BluetoothProfile { /** Connection parameter update - Request low power, reduced data rate connection parameters. */ public static final int CONNECTION_PRIORITY_LOW_POWER = 2; + /** + * A GATT writeCharacteristic request is started successfully. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_SUCCESS = 0; + + /** + * A GATT writeCharacteristic request failed to start. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_FAIL = 1; + + /** + * A GATT writeCharacteristic request is issued to a busy remote device. + * + * @hide + */ + public static final int GATT_WRITE_REQUEST_BUSY = 2; + /** * No authentication required. * @@ -440,9 +464,19 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.writeCharacteristic(mClientIf, address, handle, - characteristic.getWriteType(), authReq, - characteristic.getValue(), mAttributionSource); + int requestStatus = GATT_WRITE_REQUEST_FAIL; + for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { + requestStatus = mService.writeCharacteristic(mClientIf, address, + handle, characteristic.getWriteType(), authReq, + characteristic.getValue(), mAttributionSource); + if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + break; + } + try { + Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); + } catch (InterruptedException e) { + } + } mAuthRetryState++; return; } catch (RemoteException e) { @@ -1264,21 +1298,35 @@ public final class BluetoothGatt implements BluetoothProfile { if (device == null) return false; synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; + if (mDeviceBusy) { + return false; + } mDeviceBusy = true; } + int requestStatus = GATT_WRITE_REQUEST_FAIL; try { - mService.writeCharacteristic(mClientIf, device.getAddress(), + for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { + requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(), characteristic.getInstanceId(), characteristic.getWriteType(), AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); + if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + break; + } + try { + Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); + } catch (InterruptedException e) { + } + } } catch (RemoteException e) { Log.e(TAG, "", e); - mDeviceBusy = false; + synchronized (mDeviceBusyLock) { + mDeviceBusy = false; + } return false; } - return true; + return requestStatus == GATT_WRITE_REQUEST_SUCCESS; } /** -- GitLab From 6106c3602038e5cb2f6a283322a3be382425cd59 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 7 Jul 2021 17:17:04 -0600 Subject: [PATCH 1308/1408] Tag some "new Binder()" instances to detect leaks. We've seen evidence of a Binder leak, and our hunch is that it's caused by one of these anonymous "new Binder()" sites. Adding descriptors will help us identify the leak cause. Bug: 192415943 Test: atest BluetoothInstrumentationTests Change-Id: I30cd15f084cf50f67edd833b27b853c4b22e1db1 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 421b4de9d62..f5ab2ab7448 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -120,6 +120,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -805,7 +806,7 @@ public final class BluetoothAdapter { mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); mLeScanClients = new HashMap(); - mToken = new Binder(); + mToken = new Binder(DESCRIPTOR); } /** -- GitLab From 45713f6c7eb43e0dd018907259c824a64ff2a611 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 13 Jul 2021 22:05:05 +0200 Subject: [PATCH 1309/1408] Get rid of ResultStorageDescriptor in startScan It's never used Bug: 192615539 Merged-In: Ia2d65def69b13150a20e8a2077c3fe630f8ec86e Change-Id: Ia2d65def69b13150a20e8a2077c3fe630f8ec86e --- .../bluetooth/le/BluetoothLeScanner.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 34aac8bfdb2..e6de3d7cfb3 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -151,7 +151,7 @@ public final class BluetoothLeScanner { @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { - startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); + startScan(filters, settings, null, callback, /*callbackIntent=*/ null); } /** @@ -185,7 +185,7 @@ public final class BluetoothLeScanner { @NonNull PendingIntent callbackIntent) { return startScan(filters, settings != null ? settings : new ScanSettings.Builder().build(), - null, null, callbackIntent, null); + null, null, callbackIntent); } /** @@ -231,14 +231,13 @@ public final class BluetoothLeScanner { @SuppressLint("AndroidFrameworkRequiresPermission") public void startScanFromSource(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback) { - startScan(filters, settings, workSource, callback, null, null); + startScan(filters, settings, workSource, callback, null); } @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) private int startScan(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, - final PendingIntent callbackIntent, - List> resultStorages) { + final PendingIntent callbackIntent) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null && callbackIntent == null) { throw new IllegalArgumentException("callback is null"); @@ -274,7 +273,7 @@ public final class BluetoothLeScanner { } if (callback != null) { BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, workSource, callback, resultStorages); + settings, workSource, callback); wrapper.startRegistration(); } else { try { @@ -366,13 +365,10 @@ public final class BluetoothLeScanner { final ScanCallback callback) { int filterSize = truncatedFilters.size(); List scanFilters = new ArrayList(filterSize); - List> scanStorages = - new ArrayList>(filterSize); for (TruncatedFilter filter : truncatedFilters) { scanFilters.add(filter.getFilter()); - scanStorages.add(filter.getStorageDescriptors()); } - startScan(scanFilters, settings, null, callback, null, scanStorages); + startScan(scanFilters, settings, null, callback, null); } /** @@ -397,7 +393,6 @@ public final class BluetoothLeScanner { private final WorkSource mWorkSource; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; - private List> mResultStorages; // mLeHandle 0: not registered // -2: registration failed because app is scanning to frequently @@ -407,15 +402,13 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, - WorkSource workSource, ScanCallback scanCallback, - List> resultStorages) { + WorkSource workSource, ScanCallback scanCallback) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; mWorkSource = workSource; mScanCallback = scanCallback; mScannerId = 0; - mResultStorages = resultStorages; } public void startRegistration() { @@ -493,7 +486,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mResultStorages, mAttributionSource); + mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); -- GitLab From ecc512a64488f9b91fcda6f7ee025e36e3920e0a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Tue, 13 Jul 2021 22:05:45 +0200 Subject: [PATCH 1310/1408] Bluetooth: deprecate TruncatedFilter and ResultStorageDescriptor It's not used anywhere, and the implementation actually never do anything. Bug: 192615539 Merged-In: Icf8906c972cf18d65a22d30c3a628fd54db59179 Change-Id: Icf8906c972cf18d65a22d30c3a628fd54db59179 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 +++ .../java/android/bluetooth/le/ResultStorageDescriptor.java | 3 +++ framework/java/android/bluetooth/le/TruncatedFilter.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index e6de3d7cfb3..ee173dbc4ad 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -356,8 +356,11 @@ public final class BluetoothLeScanner { /** * Start truncated scan. * + * @deprecated this is not used anywhere + * * @hide */ + @Deprecated @SystemApi @RequiresBluetoothScanPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java index 796c815d69b..f65048975de 100644 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -23,8 +23,11 @@ import android.os.Parcelable; /** * Describes the way to store scan result. * + * @deprecated this is not used anywhere + * * @hide */ +@Deprecated @SystemApi public final class ResultStorageDescriptor implements Parcelable { private int mType; diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java index 93f526bb9f0..25925888a0d 100644 --- a/framework/java/android/bluetooth/le/TruncatedFilter.java +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -24,8 +24,11 @@ import java.util.List; /** * A special scan filter that lets the client decide how the scan record should be stored. * + * @deprecated this is not used anywhere + * * @hide */ +@Deprecated @SystemApi @SuppressLint("AndroidFrameworkBluetoothPermission") public final class TruncatedFilter { -- GitLab From 72e1ebeff5a68424410504b3085264f4a95ad876 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 14 Jul 2021 12:44:07 +0200 Subject: [PATCH 1311/1408] Get rid of ResultStorageDescriptor in startScan It's never used Bug: 192615539 Merged-In: Ia2d65def69b13150a20e8a2077c3fe630f8ec86e Change-Id: Ia2d65def69b13150a20e8a2077c3fe630f8ec86e --- .../bluetooth/le/BluetoothLeScanner.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 2888fbd8a36..26d63db879c 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -144,7 +144,7 @@ public final class BluetoothLeScanner { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List filters, ScanSettings settings, final ScanCallback callback) { - startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null); + startScan(filters, settings, null, callback, /*callbackIntent=*/ null); } /** @@ -175,7 +175,7 @@ public final class BluetoothLeScanner { @NonNull PendingIntent callbackIntent) { return startScan(filters, settings != null ? settings : new ScanSettings.Builder().build(), - null, null, callbackIntent, null); + null, null, callbackIntent); } /** @@ -210,13 +210,12 @@ public final class BluetoothLeScanner { Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback) { - startScan(filters, settings, workSource, callback, null, null); + startScan(filters, settings, workSource, callback, null); } private int startScan(List filters, ScanSettings settings, final WorkSource workSource, final ScanCallback callback, - final PendingIntent callbackIntent, - List> resultStorages) { + final PendingIntent callbackIntent) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); if (callback == null && callbackIntent == null) { throw new IllegalArgumentException("callback is null"); @@ -252,7 +251,7 @@ public final class BluetoothLeScanner { } if (callback != null) { BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, workSource, callback, resultStorages); + settings, workSource, callback); wrapper.startRegistration(); } else { try { @@ -335,13 +334,10 @@ public final class BluetoothLeScanner { final ScanCallback callback) { int filterSize = truncatedFilters.size(); List scanFilters = new ArrayList(filterSize); - List> scanStorages = - new ArrayList>(filterSize); for (TruncatedFilter filter : truncatedFilters) { scanFilters.add(filter.getFilter()); - scanStorages.add(filter.getStorageDescriptors()); } - startScan(scanFilters, settings, null, callback, null, scanStorages); + startScan(scanFilters, settings, null, callback, null); } /** @@ -364,7 +360,6 @@ public final class BluetoothLeScanner { private final WorkSource mWorkSource; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; - private List> mResultStorages; // mLeHandle 0: not registered // -2: registration failed because app is scanning to frequently @@ -374,15 +369,13 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List filters, ScanSettings settings, - WorkSource workSource, ScanCallback scanCallback, - List> resultStorages) { + WorkSource workSource, ScanCallback scanCallback) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; mWorkSource = workSource; mScanCallback = scanCallback; mScannerId = 0; - mResultStorages = resultStorages; } public void startRegistration() { @@ -458,7 +451,7 @@ public final class BluetoothLeScanner { } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mResultStorages, mOpPackageName, mFeatureId); + mOpPackageName, mFeatureId); } } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); -- GitLab From 4492b749eec6cfe544c15a39c8f067a33f98d01e Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 14 Jul 2021 13:03:25 +0200 Subject: [PATCH 1312/1408] Bluetooth: deprecate TruncatedFilter and ResultStorageDescriptor It's not used anywhere, and the implementation actually never do anything. Bug: 192615539 Merged-In: Icf8906c972cf18d65a22d30c3a628fd54db59179 Change-Id: Icf8906c972cf18d65a22d30c3a628fd54db59179 --- framework/java/android/bluetooth/le/BluetoothLeScanner.java | 3 +++ .../java/android/bluetooth/le/ResultStorageDescriptor.java | 3 +++ framework/java/android/bluetooth/le/TruncatedFilter.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index 26d63db879c..32de821c0ae 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -327,8 +327,11 @@ public final class BluetoothLeScanner { /** * Start truncated scan. * + * @deprecated this is not used anywhere + * * @hide */ + @Deprecated @SystemApi public void startTruncatedScan(List truncatedFilters, ScanSettings settings, final ScanCallback callback) { diff --git a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java index 796c815d69b..f65048975de 100644 --- a/framework/java/android/bluetooth/le/ResultStorageDescriptor.java +++ b/framework/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -23,8 +23,11 @@ import android.os.Parcelable; /** * Describes the way to store scan result. * + * @deprecated this is not used anywhere + * * @hide */ +@Deprecated @SystemApi public final class ResultStorageDescriptor implements Parcelable { private int mType; diff --git a/framework/java/android/bluetooth/le/TruncatedFilter.java b/framework/java/android/bluetooth/le/TruncatedFilter.java index a753aa6fef1..00f2f8a89c9 100644 --- a/framework/java/android/bluetooth/le/TruncatedFilter.java +++ b/framework/java/android/bluetooth/le/TruncatedFilter.java @@ -23,8 +23,11 @@ import java.util.List; /** * A special scan filter that lets the client decide how the scan record should be stored. * + * @deprecated this is not used anywhere + * * @hide */ +@Deprecated @SystemApi public final class TruncatedFilter { private final ScanFilter mFilter; -- GitLab From 1f29a9b36139b6ae6b6a6bf29fca5ee59201c8b9 Mon Sep 17 00:00:00 2001 From: Yunsik Bae Date: Tue, 20 Jul 2021 06:56:50 +0000 Subject: [PATCH 1313/1408] Unify the mismatch in the byte order of the address. Bluetooth address should check MSB for AddrType based on Spec. (BT Core Spec v 5.2 | Vol 6, Part B, 1.3 DEVICE ADDRESS) Bluetooth address meaning is not unified in the current Android framework layer. Because of this, cannot register scanfilter with the intended address format. Bug: 180466950 Test: manual Tag: #compatibility Change-Id: I59a1b5538e4f3fea77a98cba2aa46649fc32ac6b --- .../java/android/bluetooth/BluetoothAdapter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index ce384868d44..d487025631c 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3289,22 +3289,22 @@ public final class BluetoothAdapter { } /** - * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0" + * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00" * is a RANDOM STATIC address. * - * RANDOM STATIC: (addr & 0b11) == 0b11 - * RANDOM RESOLVABLE: (addr & 0b11) == 0b10 - * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00 + * RANDOM STATIC: (addr & 0xC0) == 0xC0 + * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40 + * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00 * * @param address Bluetooth address as string - * @return true if the 2 Least Significant Bits of the address equals 0b11. + * @return true if the 2 Most Significant Bits of the address equals 0xC0. * * @hide */ public static boolean isAddressRandomStatic(@NonNull String address) { requireNonNull(address); return checkBluetoothAddress(address) - && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; + && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0; } @UnsupportedAppUsage -- GitLab From f90d578773ae80c01eb94f2a34b1c1718a9d4ed7 Mon Sep 17 00:00:00 2001 From: Yunsik Bae Date: Tue, 20 Jul 2021 06:56:50 +0000 Subject: [PATCH 1314/1408] Unify the mismatch in the byte order of the address. Bluetooth address should check MSB for AddrType based on Spec. (BT Core Spec v 5.2 | Vol 6, Part B, 1.3 DEVICE ADDRESS) Bluetooth address meaning is not unified in the current Android framework layer. Because of this, cannot register scanfilter with the intended address format. Bug: 180466950 Test: manual Tag: #compatibility Change-Id: I59a1b5538e4f3fea77a98cba2aa46649fc32ac6b Merged-In: I59a1b5538e4f3fea77a98cba2aa46649fc32ac6b --- .../java/android/bluetooth/BluetoothAdapter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index f5ab2ab7448..8398be1af49 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3518,22 +3518,22 @@ public final class BluetoothAdapter { } /** - * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0" + * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00" * is a RANDOM STATIC address. * - * RANDOM STATIC: (addr & 0b11) == 0b11 - * RANDOM RESOLVABLE: (addr & 0b11) == 0b10 - * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00 + * RANDOM STATIC: (addr & 0xC0) == 0xC0 + * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40 + * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00 * * @param address Bluetooth address as string - * @return true if the 2 Least Significant Bits of the address equals 0b11. + * @return true if the 2 Most Significant Bits of the address equals 0xC0. * * @hide */ public static boolean isAddressRandomStatic(@NonNull String address) { requireNonNull(address); return checkBluetoothAddress(address) - && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; + && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0; } /** {@hide} */ -- GitLab From 4ee20f1ff52c5f67f88ae11fa616411f199e9584 Mon Sep 17 00:00:00 2001 From: wescande Date: Fri, 18 Jun 2021 10:51:19 +0200 Subject: [PATCH 1315/1408] remove getSystemConfigEnabledProfilesForPackage Remove hidden api for mainline project. This commit is only for getSystemConfigEnabledProfilesForPackage because this will trigger a SEDenial Bug: 190440540 Test: Manual Tag: #refactor Change-Id: Icb822ab703e7617d2c96275d94f682d30171998f --- .../bluetooth/BluetoothManagerService.java | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b4912bbc898..2bcd22de6fc 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -25,9 +25,9 @@ import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGRO import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; -import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -93,7 +93,6 @@ import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; @@ -804,35 +803,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mIsHearingAidProfileSupported; } - @Override - /** @hide */ - public java.util.List getSystemConfigEnabledProfilesForPackage(String packageName) { - if (Binder.getCallingUid() != Process.BLUETOOTH_UID) { - Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth"); - return null; - } - - SystemConfig systemConfig = SystemConfig.getInstance(); - if (systemConfig == null) { - return null; - } - - android.util.ArrayMap componentEnabledStates = - systemConfig.getComponentsEnabledStates(packageName); - if (componentEnabledStates == null) { - return null; - } - - ArrayList enabledProfiles = new ArrayList(); - for (Map.Entry entry : componentEnabledStates.entrySet()) { - if (entry.getValue()) { - enabledProfiles.add(entry.getKey()); - } - } - - return enabledProfiles; - } - private boolean isDeviceProvisioned() { return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0; -- GitLab From 063511f1584840e30d6cb62c2e1f7ac3b59cc9a5 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 27 Jul 2021 15:49:04 -0700 Subject: [PATCH 1316/1408] Require calls to BluetoothA2dp#setBufferLengthMillis to pass a non-negative value Tag: #feature Bug: 194837771 Test: Manual Change-Id: I3c1d2da1d28a494d8b393c4502521b3f0699adf6 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 65cdca9eb7d..1dd32fec251 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -1025,6 +1025,10 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); + if (value < 0) { + Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value); + return false; + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { -- GitLab From 5349e1fe4bee4b3af0d3a7f359dd5cbe59bbf03d Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Thu, 15 Jul 2021 12:36:20 +0000 Subject: [PATCH 1317/1408] Prevent create bond for null MAC address Tag: #feature Bug: 193702115 Test: atest BluetoothInstrumentationTests Change-Id: I5684f00cf4a567a899c958b94af22ccf5067b476 --- framework/java/android/bluetooth/BluetoothDevice.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 07dbe52a2c6..0969ec2269b 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1035,6 +1035,8 @@ public final class BluetoothDevice implements Parcelable { /** Address is either resolvable, non-resolvable or static. */ public static final int ADDRESS_TYPE_RANDOM = 1; + private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00"; + /** * Lazy initialization. Guaranteed final after first object constructed, or * getService() called. @@ -1371,6 +1373,10 @@ public final class BluetoothDevice implements Parcelable { Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); return false; } + if (NULL_MAC_ADDRESS.equals(mAddress)) { + Log.e(TAG, "Unable to create bond, invalid address " + mAddress); + return false; + } try { return service.createBond(this, transport, remoteP192Data, remoteP256Data); } catch (RemoteException e) { -- GitLab From 3478d7a8aecd834487cd402984f7d2a11fcaa517 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Wed, 28 Jul 2021 13:54:10 -0700 Subject: [PATCH 1318/1408] Add implementation for IRK to parcelable functions. Bug: 194432570 Test: Manual test app; nRF connect Tag: #feature Change-Id: I54ac5e5f105d155a2d2fc826bf1f64cf34573ff9 --- .../java/android/bluetooth/le/ScanFilter.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index ddc93327b69..9da6ff3b275 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -168,6 +168,15 @@ public final class ScanFilter implements Parcelable { dest.writeByteArray(mManufacturerDataMask); } } + + // IRK + if (mDeviceAddress != null) { + dest.writeInt(mAddressType); + dest.writeInt(mIrk == null ? 0 : 1); + if (mIrk != null) { + dest.writeByteArray(mIrk); + } + } } /** @@ -187,8 +196,10 @@ public final class ScanFilter implements Parcelable { if (in.readInt() == 1) { builder.setDeviceName(in.readString()); } + String address = null; + // If we have a non-null address if (in.readInt() == 1) { - builder.setDeviceAddress(in.readString()); + address = in.readString(); } if (in.readInt() == 1) { ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); @@ -245,6 +256,17 @@ public final class ScanFilter implements Parcelable { } } + // IRK + if (address != null) { + final int addressType = in.readInt(); + if (in.readInt() == 1) { + final byte[] irk = new byte[16]; + in.readByteArray(irk); + builder.setDeviceAddress(address, addressType, irk); + } else { + builder.setDeviceAddress(address, addressType); + } + } return builder.build(); } }; -- GitLab From 79988a59bf7b3bf099fc397a2f81067059d9bf13 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Wed, 28 Jul 2021 13:54:10 -0700 Subject: [PATCH 1319/1408] Add implementation for IRK to parcelable functions. Bug: 194432570 Test: Manual test app; nRF connect Tag: #feature Change-Id: I54ac5e5f105d155a2d2fc826bf1f64cf34573ff9 Merged-In: I54ac5e5f105d155a2d2fc826bf1f64cf34573ff9 --- .../java/android/bluetooth/le/ScanFilter.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index cb3bf297670..8ff018121ab 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -168,6 +168,15 @@ public final class ScanFilter implements Parcelable { dest.writeByteArray(mManufacturerDataMask); } } + + // IRK + if (mDeviceAddress != null) { + dest.writeInt(mAddressType); + dest.writeInt(mIrk == null ? 0 : 1); + if (mIrk != null) { + dest.writeByteArray(mIrk); + } + } } /** @@ -187,8 +196,10 @@ public final class ScanFilter implements Parcelable { if (in.readInt() == 1) { builder.setDeviceName(in.readString()); } + String address = null; + // If we have a non-null address if (in.readInt() == 1) { - builder.setDeviceAddress(in.readString()); + address = in.readString(); } if (in.readInt() == 1) { ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); @@ -245,6 +256,17 @@ public final class ScanFilter implements Parcelable { } } + // IRK + if (address != null) { + final int addressType = in.readInt(); + if (in.readInt() == 1) { + final byte[] irk = new byte[16]; + in.readByteArray(irk); + builder.setDeviceAddress(address, addressType, irk); + } else { + builder.setDeviceAddress(address, addressType); + } + } return builder.build(); } }; -- GitLab From d7cf1c83e412d47a3b24c601d18f6cf7bdf6f870 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Wed, 2 Jun 2021 14:30:37 +0800 Subject: [PATCH 1320/1408] Broadcast Bluetooth state to OFF properly In current logic, we broadcast Bluetooth state OFF at BLE_ON state. However, if Bluetooth gets crashed, we won't have the chance to send OFF intent as we never get to BLE_ON. This change makes the BluetoothManagerService skips the OFF intent only if the previous state is a BLE state. Bug: 189271317 Test: manual Change-Id: I3936766ab8bdd45d59550dacb64bdc0323bb424d Merged-In: I3936766ab8bdd45d59550dacb64bdc0323bb424d --- .../bluetooth/BluetoothManagerService.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 992ef2657a6..8dcd9410a6b 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2452,6 +2452,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM); } + private boolean isBleState(int state) { + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_BLE_TURNING_OFF: + return true; + } + return false; + } + private void bluetoothStateChangeHandler(int prevState, int newState) { boolean isStandardBroadcast = true; if (prevState == newState) { // No change. Nothing to do. @@ -2470,8 +2480,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothServiceDownCallback(); unbindAndFinish(); sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; + + /* Currently, the OFF intent is broadcasted externally only when we transition + * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state, + * we are guaranteed that the OFF intent has been broadcasted earlier and we + * can safely skip it. + * Conversely, if the previous state is not a BLE state, it indicates that some + * sort of crash has occurred, moving us directly to STATE_OFF without ever + * passing through BLE_ON. We should broadcast the OFF intent in this case. */ + isStandardBroadcast = !isBleState(prevState); } else if (!intermediate_off) { // connect to GattService @@ -2524,6 +2541,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Show prevState of BLE_ON as OFF to standard users prevState = BluetoothAdapter.STATE_OFF; } + if (DBG) { + Slog.d(TAG, + "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > " + + BluetoothAdapter.nameForState(newState)); + } Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); -- GitLab From 5666c0d4fe4785bc3049b660cdb167be29fc46e3 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Wed, 2 Jun 2021 14:30:37 +0800 Subject: [PATCH 1321/1408] Broadcast Bluetooth state to OFF properly In current logic, we broadcast Bluetooth state OFF at BLE_ON state. However, if Bluetooth gets crashed, we won't have the chance to send OFF intent as we never get to BLE_ON. This change makes the BluetoothManagerService skips the OFF intent only if the previous state is a BLE state. Bug: 189271317 Bug: 195032127 Test: manual Change-Id: I3936766ab8bdd45d59550dacb64bdc0323bb424d (cherry picked from commit 1b2f8bc9843b2d6ba7ba199a89412c8fb632f508) --- .../bluetooth/BluetoothManagerService.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 85ff2be43be..b7c61a0d25c 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2505,6 +2505,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions()); } + private boolean isBleState(int state) { + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_BLE_TURNING_OFF: + return true; + } + return false; + } + @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, @@ -2527,8 +2537,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { sendBluetoothServiceDownCallback(); unbindAndFinish(); sendBleStateChanged(prevState, newState); - // Don't broadcast as it has already been broadcast before - isStandardBroadcast = false; + + /* Currently, the OFF intent is broadcasted externally only when we transition + * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state, + * we are guaranteed that the OFF intent has been broadcasted earlier and we + * can safely skip it. + * Conversely, if the previous state is not a BLE state, it indicates that some + * sort of crash has occurred, moving us directly to STATE_OFF without ever + * passing through BLE_ON. We should broadcast the OFF intent in this case. */ + isStandardBroadcast = !isBleState(prevState); } else if (!intermediate_off) { // connect to GattService @@ -2581,6 +2598,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Show prevState of BLE_ON as OFF to standard users prevState = BluetoothAdapter.STATE_OFF; } + if (DBG) { + Slog.d(TAG, + "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > " + + BluetoothAdapter.nameForState(newState)); + } Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); -- GitLab From ea7ccc2a888e1fc64dcb851d3dbfdf01acdef861 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Tue, 3 Aug 2021 12:48:43 +0200 Subject: [PATCH 1322/1408] Bluetooth: Add GATT_INSUFFICIENT_AUTHORIZATION error code Bug: 150670922 Sponsor: jpawlowski@ Tag: #feature Test: compilation Change-Id: I25bc13c658caba48cc37b0f1f253a77fd1336b29 --- framework/java/android/bluetooth/BluetoothGatt.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 1d08ddb65cf..6f2aba25eb9 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -98,6 +98,9 @@ public final class BluetoothGatt implements BluetoothProfile { /** A read or write operation was requested with an invalid offset */ public static final int GATT_INVALID_OFFSET = 0x7; + /** Insufficient authorization for a given operation */ + public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8; + /** A write operation exceeds the maximum length of the attribute */ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; -- GitLab From bedd8317c4fc514eb9c3b061b3f5b9d3662d8bd6 Mon Sep 17 00:00:00 2001 From: Jakub Tyszkowski Date: Mon, 1 Mar 2021 13:02:25 +0000 Subject: [PATCH 1323/1408] Bluetooth: Add Media Control Profile Tag: #feature Bug: 150670922 Sponsor: jpawlowski@ Test: compilation Change-Id: Icabed06e6b1973ecee9cd1d6eeec80c6d2a9d72e --- framework/java/android/bluetooth/BluetoothProfile.java | 9 ++++++++- framework/java/android/bluetooth/BluetoothUuid.java | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index b76d6b86913..bea32abd06c 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -221,13 +221,20 @@ public interface BluetoothProfile { @SystemApi int VOLUME_CONTROL = 23; + /** + * @hide + * Media Control Profile server + * + */ + int MCP_SERVER = 24; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 23; + int MAX_PROFILE_ID = 24; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index d82cf19e882..d07259393b5 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -172,6 +172,16 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid GENERIC_MEDIA_CONTROL = + ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi + public static final ParcelUuid MEDIA_CONTROL = + ParcelUuid.fromString("00001848-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From a1157b502512cc381d271b3fdc7f74faf62c3d7b Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 27 Jul 2021 15:49:04 -0700 Subject: [PATCH 1324/1408] Require calls to BluetoothA2dp#setBufferLengthMillis to pass a non-negative value Tag: #feature Bug: 194837771 Test: Manual Merged-In: I3c1d2da1d28a494d8b393c4502521b3f0699adf6 Change-Id: I3c1d2da1d28a494d8b393c4502521b3f0699adf6 --- framework/java/android/bluetooth/BluetoothA2dp.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 16413e1a1db..4dcb3dc4f1d 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -946,6 +946,10 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); + if (value < 0) { + Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value); + return false; + } try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { -- GitLab From 56a86434a2277555bb8051443a38c9061a947d9b Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 10 Aug 2021 16:50:52 -0600 Subject: [PATCH 1325/1408] Register IBluetoothManagerCallback per-process. As part of introducing AttributionSource across the Bluetooth stack earlier this year, each BluetoothAdapter instance is now associated with a specific AttributionSource, and several instances of shared static code were made BluetoothAdapter-specific so they could be augmented with the relevant AttributionSource. However, processes that create many BluetoothAdapter instances can overload the system, since a IBluetoothManagerCallback was registered for each instance. This change mitigates this by only registering a single IBluetoothManagerCallback for the entire process, and it then reuses the existing sProxyServiceStateCallbacks list for dispatching events to all active adapters within the process. Since it's so late in the release, we keep both mService and sService intact to minimize the size of this CL; future work should refactor to a more robust design, such as Supplier. Bug: 195286998, 172022978 Test: atest BluetoothInstrumentationTests Change-Id: I012f3f65e61eaf55e40436486806e56506c928ee --- .../android/bluetooth/BluetoothAdapter.java | 184 ++++++++++++------ .../android/bluetooth/BluetoothSocket.java | 6 +- 2 files changed, 127 insertions(+), 63 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8398be1af49..5094498dbee 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -64,6 +64,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,6 +80,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -715,10 +718,21 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private final AttributionSource mAttributionSource; + // Yeah, keeping both mService and sService isn't pretty, but it's too late + // in the current release for a major refactoring, so we leave them both + // intact until this can be cleaned up in a future release + @UnsupportedAppUsage + @GuardedBy("mServiceLock") private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("sServiceLock") + private static boolean sServiceRegistered; + @GuardedBy("sServiceLock") + private static IBluetooth sService; + private static final Object sServiceLock = new Object(); + private final Object mLock = new Object(); private final Map mLeScanClients; private final Map>> @@ -792,19 +806,11 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - if (managerService == null) { - throw new IllegalArgumentException("bluetooth manager service is null"); - } - try { - mServiceLock.writeLock().lock(); - mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.writeLock().unlock(); - } mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); + synchronized (mServiceLock.writeLock()) { + mService = getBluetoothService(mManagerCallback); + } mLeScanClients = new HashMap(); mToken = new Binder(DESCRIPTOR); } @@ -3154,21 +3160,16 @@ public final class BluetoothAdapter { } } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothManagerCallback mManagerCallback = + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { - @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); } - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + synchronized (sServiceLock) { + sService = bluetoothService; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -3180,6 +3181,56 @@ public final class BluetoothAdapter { } } } + } + + public void onBluetoothServiceDown() { + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown"); + } + + synchronized (sServiceLock) { + sService = null; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + + public void onBrEdrDown() { + if (VDBG) { + Log.i(TAG, "onBrEdrDown"); + } + + synchronized (sServiceLock) { + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBrEdrDown(); + } else { + Log.d(TAG, "onBrEdrDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + }; + + private final IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + synchronized (mServiceLock.writeLock()) { + mService = bluetoothService; + } synchronized (mMetadataListeners) { mMetadataListeners.forEach((device, pair) -> { try { @@ -3204,12 +3255,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown: " + mService); - } - - try { - mServiceLock.writeLock().lock(); + synchronized (mServiceLock.writeLock()) { mService = null; if (mLeScanClients != null) { mLeScanClients.clear(); @@ -3220,29 +3266,10 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } - } finally { - mServiceLock.writeLock().unlock(); - } - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } } } public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown: " + mService); - } } }; @@ -3477,15 +3504,12 @@ public final class BluetoothAdapter { protected void finalize() throws Throwable { try { - mManagerService.unregisterAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + removeServiceStateCallback(mManagerCallback); } finally { super.finalize(); } } - /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" *

    Alphabetic characters must be uppercase to be valid. @@ -3549,24 +3573,64 @@ public final class BluetoothAdapter { return mAttributionSource; } - private final ArrayList mProxyServiceStateCallbacks = - new ArrayList(); + @GuardedBy("sServiceLock") + private static final WeakHashMap sProxyServiceStateCallbacks = + new WeakHashMap<>(); + + /*package*/ IBluetooth getBluetoothService() { + synchronized (sServiceLock) { + if (sProxyServiceStateCallbacks.isEmpty()) { + throw new IllegalStateException( + "Anonymous service access requires at least one lifecycle in process"); + } + return sService; + } + } @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - if (cb == null) { - Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); - } else if (!mProxyServiceStateCallbacks.contains(cb)) { - mProxyServiceStateCallbacks.add(cb); - } + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.put(cb, null); + registerOrUnregisterAdapterLocked(); + return sService; } - return mService; } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - mProxyServiceStateCallbacks.remove(cb); + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.remove(cb); + registerOrUnregisterAdapterLocked(); + } + } + + /** + * Handle registering (or unregistering) a single process-wide + * {@link IBluetoothManagerCallback} based on the presence of local + * {@link #sProxyServiceStateCallbacks} clients. + */ + @GuardedBy("sServiceLock") + private void registerOrUnregisterAdapterLocked() { + final boolean isRegistered = sServiceRegistered; + final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); + + if (isRegistered != wantRegistered) { + if (wantRegistered) { + try { + sService = mManagerService.registerAdapter(sManagerCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + try { + mManagerService.unregisterAdapter(sManagerCallback); + sService = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + sServiceRegistered = wantRegistered; } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index bb409d5360f..1655b62bbfe 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -399,7 +399,7 @@ public final class BluetoothSocket implements Closeable { try { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -438,7 +438,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { Log.e(TAG, "bindListen fail, reason: bluetooth is off"); return -1; @@ -706,7 +706,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("socket closed"); } IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { throw new IOException("Bluetooth is off"); } -- GitLab From a1157dbeedfecfdb039802cee22a5f5e1abf0cf8 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 10 Aug 2021 16:50:52 -0600 Subject: [PATCH 1326/1408] Register IBluetoothManagerCallback per-process. As part of introducing AttributionSource across the Bluetooth stack earlier this year, each BluetoothAdapter instance is now associated with a specific AttributionSource, and several instances of shared static code were made BluetoothAdapter-specific so they could be augmented with the relevant AttributionSource. However, processes that create many BluetoothAdapter instances can overload the system, since a IBluetoothManagerCallback was registered for each instance. This change mitigates this by only registering a single IBluetoothManagerCallback for the entire process, and it then reuses the existing sProxyServiceStateCallbacks list for dispatching events to all active adapters within the process. Since it's so late in the release, we keep both mService and sService intact to minimize the size of this CL; future work should refactor to a more robust design, such as Supplier. Bug: 195286998, 172022978 Test: atest BluetoothInstrumentationTests Change-Id: I012f3f65e61eaf55e40436486806e56506c928ee (cherry picked from commit f0fa7f9b4aaee876053486d988909df9dc64e990) --- .../android/bluetooth/BluetoothAdapter.java | 184 ++++++++++++------ .../android/bluetooth/BluetoothSocket.java | 6 +- 2 files changed, 127 insertions(+), 63 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8398be1af49..5094498dbee 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -64,6 +64,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,6 +80,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -715,10 +718,21 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private final AttributionSource mAttributionSource; + // Yeah, keeping both mService and sService isn't pretty, but it's too late + // in the current release for a major refactoring, so we leave them both + // intact until this can be cleaned up in a future release + @UnsupportedAppUsage + @GuardedBy("mServiceLock") private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("sServiceLock") + private static boolean sServiceRegistered; + @GuardedBy("sServiceLock") + private static IBluetooth sService; + private static final Object sServiceLock = new Object(); + private final Object mLock = new Object(); private final Map mLeScanClients; private final Map>> @@ -792,19 +806,11 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - if (managerService == null) { - throw new IllegalArgumentException("bluetooth manager service is null"); - } - try { - mServiceLock.writeLock().lock(); - mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.writeLock().unlock(); - } mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); + synchronized (mServiceLock.writeLock()) { + mService = getBluetoothService(mManagerCallback); + } mLeScanClients = new HashMap(); mToken = new Binder(DESCRIPTOR); } @@ -3154,21 +3160,16 @@ public final class BluetoothAdapter { } } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothManagerCallback mManagerCallback = + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { - @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); } - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + synchronized (sServiceLock) { + sService = bluetoothService; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -3180,6 +3181,56 @@ public final class BluetoothAdapter { } } } + } + + public void onBluetoothServiceDown() { + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown"); + } + + synchronized (sServiceLock) { + sService = null; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + + public void onBrEdrDown() { + if (VDBG) { + Log.i(TAG, "onBrEdrDown"); + } + + synchronized (sServiceLock) { + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBrEdrDown(); + } else { + Log.d(TAG, "onBrEdrDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + }; + + private final IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + synchronized (mServiceLock.writeLock()) { + mService = bluetoothService; + } synchronized (mMetadataListeners) { mMetadataListeners.forEach((device, pair) -> { try { @@ -3204,12 +3255,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown: " + mService); - } - - try { - mServiceLock.writeLock().lock(); + synchronized (mServiceLock.writeLock()) { mService = null; if (mLeScanClients != null) { mLeScanClients.clear(); @@ -3220,29 +3266,10 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } - } finally { - mServiceLock.writeLock().unlock(); - } - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } } } public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown: " + mService); - } } }; @@ -3477,15 +3504,12 @@ public final class BluetoothAdapter { protected void finalize() throws Throwable { try { - mManagerService.unregisterAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + removeServiceStateCallback(mManagerCallback); } finally { super.finalize(); } } - /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" *

    Alphabetic characters must be uppercase to be valid. @@ -3549,24 +3573,64 @@ public final class BluetoothAdapter { return mAttributionSource; } - private final ArrayList mProxyServiceStateCallbacks = - new ArrayList(); + @GuardedBy("sServiceLock") + private static final WeakHashMap sProxyServiceStateCallbacks = + new WeakHashMap<>(); + + /*package*/ IBluetooth getBluetoothService() { + synchronized (sServiceLock) { + if (sProxyServiceStateCallbacks.isEmpty()) { + throw new IllegalStateException( + "Anonymous service access requires at least one lifecycle in process"); + } + return sService; + } + } @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - if (cb == null) { - Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); - } else if (!mProxyServiceStateCallbacks.contains(cb)) { - mProxyServiceStateCallbacks.add(cb); - } + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.put(cb, null); + registerOrUnregisterAdapterLocked(); + return sService; } - return mService; } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - mProxyServiceStateCallbacks.remove(cb); + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.remove(cb); + registerOrUnregisterAdapterLocked(); + } + } + + /** + * Handle registering (or unregistering) a single process-wide + * {@link IBluetoothManagerCallback} based on the presence of local + * {@link #sProxyServiceStateCallbacks} clients. + */ + @GuardedBy("sServiceLock") + private void registerOrUnregisterAdapterLocked() { + final boolean isRegistered = sServiceRegistered; + final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); + + if (isRegistered != wantRegistered) { + if (wantRegistered) { + try { + sService = mManagerService.registerAdapter(sManagerCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + try { + mManagerService.unregisterAdapter(sManagerCallback); + sService = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + sServiceRegistered = wantRegistered; } } diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index bb409d5360f..1655b62bbfe 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -399,7 +399,7 @@ public final class BluetoothSocket implements Closeable { try { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -438,7 +438,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { Log.e(TAG, "bindListen fail, reason: bluetooth is off"); return -1; @@ -706,7 +706,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("socket closed"); } IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { throw new IOException("Bluetooth is off"); } -- GitLab From a44dee9fc7fc187eb74dadbdbd033feed71fa3e3 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 13 Aug 2021 16:03:12 -0700 Subject: [PATCH 1327/1408] Allow SHELL_UID to disable Bluetooth for testing Bug: 189952422 Test: atest VtsHalBluetoothV1_0TargetTest Change-Id: Id45a43b824b2be49fb38686f9582484931024187 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 707ac322d5e..bf28603b067 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2430,7 +2430,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { foregroundUser = ActivityManager.getCurrentUser(); valid = (callingUser == foregroundUser) || parentUser == foregroundUser - || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; + || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid + || callingAppId == Process.SHELL_UID; if (DBG && !valid) { Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser + " parentUser=" + parentUser + " foregroundUser=" -- GitLab From 0f404a34257d5ad2d0382c2e9c94666d273b88e0 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Fri, 13 Aug 2021 00:17:17 +0800 Subject: [PATCH 1328/1408] Add a system api to fetch uuids by the specific transport The functionality is similar with fetchUuidsWithSdp which uses TRANSPORT_AUTO. Bt stack depends on the devie type and address type to do sdp or gatt service discovery. Add an api to specific the transport. It would be fexible to handle the dual mode device. Ignore-AOSP-First: avoid merge conflict Tag: #feature Bug: 194447999 Test: atest BluetoothInstrumentationTests Test: Take two headphone to test the dual mode behavior Change-Id: I32c8dde68969e0bdc42e4e898c43a4f2c1d3379a --- .../android/bluetooth/BluetoothDevice.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f68a8b28e9d..12211485cc0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1791,13 +1791,41 @@ public final class BluetoothDevice implements Parcelable, Attributable { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean fetchUuidsWithSdp() { + return fetchUuidsWithSdp(TRANSPORT_AUTO); + } + + /** + * Perform a service discovery on the remote device to get the UUIDs supported with the + * specific transport. + * + *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP or GATT records or if the process takes a long time, or the device + * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the + * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids} + * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding + * process, service discovery or device inquiry, the request will be queued. + * + * @param transport - provide type of transport (e.g. LE or Classic). + * @return False if the check fails, True if the process of initiating an ACL connection + * to the remote device was started or cached UUIDs will be broadcast with the specific + * transport. + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public boolean fetchUuidsWithSdp(@Transport int transport) { final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } try { - return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); + return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 08fbafc03fcb06156224966f45d14af522e9dcb6 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 13 Aug 2021 16:03:12 -0700 Subject: [PATCH 1329/1408] Allow SHELL_UID to disable Bluetooth for testing Bug: 189952422 Test: atest VtsHalBluetoothV1_0TargetTest Change-Id: Id45a43b824b2be49fb38686f9582484931024187 Merged-In: Id45a43b824b2be49fb38686f9582484931024187 --- .../com/android/server/bluetooth/BluetoothManagerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index b7c61a0d25c..dc7904ddf8e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -2479,7 +2479,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { foregroundUser = ActivityManager.getCurrentUser(); valid = (callingUser == foregroundUser) || parentUser == foregroundUser - || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid; + || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid + || callingAppId == Process.SHELL_UID; if (DBG && !valid) { Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" + callingUser + " parentUser=" + parentUser + " foregroundUser=" -- GitLab From 4e7449ce893cf3f3019b0201b766d0a784da1754 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 1330/1408] Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 --- framework/java/android/bluetooth/BluetoothDevice.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 594e5ffa77a..ffdd356e333 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1081,7 +1081,10 @@ public final class BluetoothDevice implements Parcelable { if (alias == null) { return getName(); } - return alias; + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 674b377c2ee78b3c435697c970f3f7c2e4e6ae4c Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Fri, 13 Aug 2021 00:17:17 +0800 Subject: [PATCH 1331/1408] Add a system api to fetch uuids by the specific transport The functionality is similar with fetchUuidsWithSdp which uses TRANSPORT_AUTO. Bt stack depends on the devie type and address type to do sdp or gatt service discovery. Add an api to specific the transport. It would be fexible to handle the dual mode device. Tag: #feature Bug: 197195548 Test: atest BluetoothInstrumentationTests Test: Take two headphone to test the dual mode behavior Change-Id: I32c8dde68969e0bdc42e4e898c43a4f2c1d3379a Merged-In: I32c8dde68969e0bdc42e4e898c43a4f2c1d3379a --- .../android/bluetooth/BluetoothDevice.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 0969ec2269b..38fb90d9c4a 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1622,13 +1622,38 @@ public final class BluetoothDevice implements Parcelable { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { + return fetchUuidsWithSdp(TRANSPORT_AUTO); + } + + /** + * Perform a service discovery on the remote device to get the UUIDs supported with the + * specific transport. + * + *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP or GATT records or if the process takes a long time, or the device + * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the + * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids} + * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding + * process, service discovery or device inquiry, the request will be queued. + * + * @param transport - provide type of transport (e.g. LE or Classic). + * @return False if the check fails, True if the process of initiating an ACL connection + * to the remote device was started or cached UUIDs will be broadcast with the specific + * transport. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean fetchUuidsWithSdp(@Transport int transport) { final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } try { - return service.fetchRemoteUuids(this); + return service.fetchRemoteUuids(this, transport); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 40d5e9db3581ae80f5c006bcc0cf2e6fd26ee475 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Fri, 13 Aug 2021 00:17:17 +0800 Subject: [PATCH 1332/1408] Add a system api to fetch uuids by the specific transport The functionality is similar with fetchUuidsWithSdp which uses TRANSPORT_AUTO. Bt stack depends on the devie type and address type to do sdp or gatt service discovery. Add an api to specific the transport. It would be fexible to handle the dual mode device. Ignore-AOSP-First: avoid merge conflict Tag: #feature Bug: 194447999 Test: atest BluetoothInstrumentationTests Test: Take two headphone to test the dual mode behavior Change-Id: I32c8dde68969e0bdc42e4e898c43a4f2c1d3379a Merged-In: I32c8dde68969e0bdc42e4e898c43a4f2c1d3379a --- .../android/bluetooth/BluetoothDevice.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index f68a8b28e9d..12211485cc0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1791,13 +1791,41 @@ public final class BluetoothDevice implements Parcelable, Attributable { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean fetchUuidsWithSdp() { + return fetchUuidsWithSdp(TRANSPORT_AUTO); + } + + /** + * Perform a service discovery on the remote device to get the UUIDs supported with the + * specific transport. + * + *

    This API is asynchronous and {@link #ACTION_UUID} intent is sent, + * with the UUIDs supported by the remote end. If there is an error + * in getting the SDP or GATT records or if the process takes a long time, or the device + * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the + * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids} + * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding + * process, service discovery or device inquiry, the request will be queued. + * + * @param transport - provide type of transport (e.g. LE or Classic). + * @return False if the check fails, True if the process of initiating an ACL connection + * to the remote device was started or cached UUIDs will be broadcast with the specific + * transport. + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public boolean fetchUuidsWithSdp(@Transport int transport) { final IBluetooth service = sService; if (service == null || !isBluetoothEnabled()) { Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); return false; } try { - return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); + return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From fd45c86706760a0ba2d77413e32b9c12806cb3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 23 Aug 2021 12:01:47 +0000 Subject: [PATCH 1333/1408] csip: Add constants for the Coordinated Set Identification Profile Bug: 150670922 Tag: #feature Test: compilation Sponsor: jpawlowski@ Change-Id: I331fe270b75f306d57a15933ab4afa2e48c8bf50 --- framework/java/android/bluetooth/BluetoothProfile.java | 8 +++++++- framework/java/android/bluetooth/BluetoothUuid.java | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index bea32abd06c..c4649b4276f 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -228,13 +228,19 @@ public interface BluetoothProfile { */ int MCP_SERVER = 24; + /** + * Coordinated Set Identification Profile set coordinator + * + */ + int CSIP_SET_COORDINATOR = 25; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 24; + int MAX_PROFILE_ID = 25; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index d07259393b5..7e4ee9e8469 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -182,6 +182,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid COORDINATED_SET = + ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From 060e692bc6f6f9999bb0b368dd5623fea67e01e9 Mon Sep 17 00:00:00 2001 From: wescande Date: Fri, 18 Jun 2021 10:51:19 +0200 Subject: [PATCH 1334/1408] remove getSystemConfigEnabledProfilesForPackage Remove hidden api for mainline project. This commit is only for getSystemConfigEnabledProfilesForPackage because this will trigger a SEDenial Bug: 190440540 Test: Manual Tag: #refactor Merged-In: Icb822ab703e7617d2c96275d94f682d30171998f Change-Id: Icb822ab703e7617d2c96275d94f682d30171998f --- .../bluetooth/BluetoothManagerService.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 12a94e19962..8723ba3f981 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -81,7 +81,6 @@ import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; @@ -786,35 +785,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mIsHearingAidProfileSupported; } - @Override - /** @hide */ - public java.util.List getSystemConfigEnabledProfilesForPackage(String packageName) { - if (Binder.getCallingUid() != Process.BLUETOOTH_UID) { - Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth"); - return null; - } - - SystemConfig systemConfig = SystemConfig.getInstance(); - if (systemConfig == null) { - return null; - } - - android.util.ArrayMap componentEnabledStates = - systemConfig.getComponentsEnabledStates(packageName); - if (componentEnabledStates == null) { - return null; - } - - ArrayList enabledProfiles = new ArrayList(); - for (Map.Entry entry : componentEnabledStates.entrySet()) { - if (entry.getValue()) { - enabledProfiles.add(entry.getKey()); - } - } - - return enabledProfiles; - } - private boolean isDeviceProvisioned() { return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0; -- GitLab From e5fd3e413cde4507d83730c440ba098bad2001c2 Mon Sep 17 00:00:00 2001 From: wescande Date: Fri, 18 Jun 2021 10:51:19 +0200 Subject: [PATCH 1335/1408] remove getSystemConfigEnabledProfilesForPackage Remove hidden api for mainline project. This commit is only for getSystemConfigEnabledProfilesForPackage because this will trigger a SEDenial Bug: 190440540 Test: Manual Tag: #refactor Merged-In: Icb822ab703e7617d2c96275d94f682d30171998f Change-Id: Icb822ab703e7617d2c96275d94f682d30171998f --- .../bluetooth/BluetoothManagerService.java | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 0d3b0644894..608cbf2d3a7 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -25,9 +25,9 @@ import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGRO import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; -import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -93,7 +93,6 @@ import com.android.server.pm.UserRestrictionsUtils; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; @@ -804,35 +803,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mIsHearingAidProfileSupported; } - @Override - /** @hide */ - public java.util.List getSystemConfigEnabledProfilesForPackage(String packageName) { - if (Binder.getCallingUid() != Process.BLUETOOTH_UID) { - Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth"); - return null; - } - - SystemConfig systemConfig = SystemConfig.getInstance(); - if (systemConfig == null) { - return null; - } - - android.util.ArrayMap componentEnabledStates = - systemConfig.getComponentsEnabledStates(packageName); - if (componentEnabledStates == null) { - return null; - } - - ArrayList enabledProfiles = new ArrayList(); - for (Map.Entry entry : componentEnabledStates.entrySet()) { - if (entry.getValue()) { - enabledProfiles.add(entry.getKey()); - } - } - - return enabledProfiles; - } - private boolean isDeviceProvisioned() { return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0; -- GitLab From 71f9c1233495b8f9d3f5a6db89bec2c24611192b Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 1336/1408] DO NOT MERGE Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 Merged-In: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 --- framework/java/android/bluetooth/BluetoothDevice.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 388161d7630..362727731c9 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1059,7 +1059,14 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteAlias(this); + String alias = service.getRemoteAlias(this); + if (alias == null) { + return getName(); + } + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 0fc4505c7d0e3cb404bbea565b903569ff91896e Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 1337/1408] DO NOT MERGE Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 --- framework/java/android/bluetooth/BluetoothDevice.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 3cf80746652..8916b537ff2 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -894,7 +894,14 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteAlias(this); + String alias = service.getRemoteAlias(this); + if (alias == null) { + return getName(); + } + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From f6e7a62747ace70b47cfd8688a6eb950b9e5431c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Fri, 14 May 2021 12:19:07 +0000 Subject: [PATCH 1338/1408] le_audio: Introduce connection state handling for LE Audio group Patch implements connection state handling for LE Audio device group. Tag: #feature Test: Set LE audio device as active Sponsor: jpawlowski@ Bug: 150670922 Change-Id: I11222ca07e265ac8b6dc3c21650874ebeffa473c --- .../android/bluetooth/BluetoothLeAudio.java | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index c438dd34f81..3ea865bfd6e 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -96,6 +96,77 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; + /** + * Intent used to broadcast group node status information. + * + *

    This intent will have 3 extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status.
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED"; + + + /** + * Intent used to broadcast group status information. + * + *

    This intent will have 4 extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status.
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED"; + + /** + * Intent used to broadcast group audio configuration changed information. + * + *

    This intent will have 5 extra: + *

      + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask.
    • + *
    • {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned + * Numbers
    • + *
    • {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned + * Numbers
    • + *
    • {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per + * Bluetooth Assigned Numbers
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONF_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONF_CHANGED"; + + /** + * Indicates conversation between humans as, for example, in telephony or video calls. + * @hide + */ + public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002; + + /** + * Indicates media as, for example, in music, public radio, podcast or video soundtrack. + * @hide + */ + public static final int CONTEXT_TYPE_MEDIA = 0x0004; + /** * This represents an invalid group ID. * @@ -103,6 +174,71 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + /** + * Contains group id. + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_ID = + "android.bluetooth.extra.LE_AUDIO_GROUP_ID"; + + /** + * Contains group node status, can be any of + *

    + *

      + *
    • {@link #GROUP_NODE_ADDED}
    • + *
    • {@link #GROUP_NODE_REMOVED}
    • + *
    + *

    + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS"; + + /** + * Contains group status, can be any of + * + *

    + *

      + *
    • {@link #GROUP_STATUS_IDLE}
    • + *
    • {@link #GROUP_STATUS_STREAMING}
    • + *
    • {@link #GROUP_STATUS_SUSPENDED}
    • + *
    • {@link #GROUP_STATUS_RECONFIGURED}
    • + *
    • {@link #GROUP_STATUS_DESTROYED}
    • + *
    + *

    + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS"; + + /** + * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source. + * @hide + */ + public static final String EXTRA_LE_AUDIO_DIRECTION = + "android.bluetooth.extra.LE_AUDIO_DIRECTION"; + + /** + * Contains source location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION"; + + /** + * Contains sink location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SINK_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION"; + + /** + * Contains available context types for group as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = + "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; + private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = -- GitLab From be39b6044356344f4af96b1d449b1127299ca174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Fri, 14 May 2021 12:19:07 +0000 Subject: [PATCH 1339/1408] le_audio: Introduce connection state handling for LE Audio group Patch implements connection state handling for LE Audio device group. Tag: #feature Test: Set LE audio device as active Sponsor: jpawlowski@ Bug: 150670922 Merged-In: I11222ca07e265ac8b6dc3c21650874ebeffa473c Change-Id: I11222ca07e265ac8b6dc3c21650874ebeffa473c --- .../android/bluetooth/BluetoothLeAudio.java | 142 +++++++++++++++++- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 75fcab93593..6bbe95e3127 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -65,9 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = @@ -82,15 +79,83 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * be null if no device is active. * * - *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; + /** + * Intent used to broadcast group node status information. + * + *

    This intent will have 3 extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status.
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED"; + + + /** + * Intent used to broadcast group status information. + * + *

    This intent will have 4 extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status.
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED"; + + /** + * Intent used to broadcast group audio configuration changed information. + * + *

    This intent will have 5 extra: + *

      + *
    • {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id.
    • + *
    • {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask.
    • + *
    • {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned + * Numbers
    • + *
    • {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned + * Numbers
    • + *
    • {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per + * Bluetooth Assigned Numbers
    • + *
    + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONF_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONF_CHANGED"; + + /** + * Indicates conversation between humans as, for example, in telephony or video calls. + * @hide + */ + public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002; + + /** + * Indicates media as, for example, in music, public radio, podcast or video soundtrack. + * @hide + */ + public static final int CONTEXT_TYPE_MEDIA = 0x0004; + /** * This represents an invalid group ID. * @@ -98,6 +163,71 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + /** + * Contains group id. + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_ID = + "android.bluetooth.extra.LE_AUDIO_GROUP_ID"; + + /** + * Contains group node status, can be any of + *

    + *

      + *
    • {@link #GROUP_NODE_ADDED}
    • + *
    • {@link #GROUP_NODE_REMOVED}
    • + *
    + *

    + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS"; + + /** + * Contains group status, can be any of + * + *

    + *

      + *
    • {@link #GROUP_STATUS_IDLE}
    • + *
    • {@link #GROUP_STATUS_STREAMING}
    • + *
    • {@link #GROUP_STATUS_SUSPENDED}
    • + *
    • {@link #GROUP_STATUS_RECONFIGURED}
    • + *
    • {@link #GROUP_STATUS_DESTROYED}
    • + *
    + *

    + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS"; + + /** + * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source. + * @hide + */ + public static final String EXTRA_LE_AUDIO_DIRECTION = + "android.bluetooth.extra.LE_AUDIO_DIRECTION"; + + /** + * Contains source location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION"; + + /** + * Contains sink location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SINK_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION"; + + /** + * Contains available context types for group as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = + "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", -- GitLab From 0ba52e7126b0da1bffea5a91d413282a69aac019 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 2 Sep 2021 18:40:07 +0800 Subject: [PATCH 1340/1408] Avoid Bluetooth gets turned on by enableBle BluetoothHandler was not able to tell whether the MESSAGE_ENABLE was from enable() or enableBle(), which sometimes lead to Bluetooth gets turned to STATE_ON by an enableBle() call. Fix this problem by adding a BLE specific argument in the message to differ the calls. Bug: 191404687 Test: Manual Change-Id: Ic9df612ec0e396932df2fb3bf3a5874bd4e7443c --- .../bluetooth/BluetoothManagerService.java | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index dc7904ddf8e..51066dd837e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -25,9 +25,9 @@ import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGRO import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; -import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -187,8 +187,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; - private int mWaitForEnableRetry; - private int mWaitForDisableRetry; private BluetoothModeChangeHelper mBluetoothModeChangeHelper; @@ -982,14 +980,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mState == BluetoothAdapter.STATE_ON || mState == BluetoothAdapter.STATE_BLE_ON || mState == BluetoothAdapter.STATE_TURNING_ON - || mState == BluetoothAdapter.STATE_TURNING_OFF) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + || mState == BluetoothAdapter.STATE_TURNING_OFF + || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on"); return true; } synchronized (mReceiver) { // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, true); } return true; } @@ -1802,6 +1801,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private class BluetoothHandler extends Handler { boolean mGetNameAddressOnly = false; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; BluetoothHandler(Looper looper) { super(looper); @@ -1852,11 +1853,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: int quietEnable = msg.arg1; + int isBle = msg.arg2; if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { // We are handling enable or disable right now, wait for it. mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, - quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + quietEnable, isBle), ENABLE_DISABLE_DELAY_MS); break; } @@ -1871,13 +1873,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + boolean isHandled = true; int state = mBluetooth.getState(); - if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(mContext.getAttributionSource()); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - break; + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + if (isBle == 1) { + Slog.i(TAG, "Already at BLE_ON State"); + } else { + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); + mBluetooth.onLeServiceUp(mContext.getAttributionSource()); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + break; + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_TURNING_ON: + case BluetoothAdapter.STATE_ON: + Slog.i(TAG, "MESSAGE_ENABLE: already enabled"); + break; + default: + isHandled = false; + break; } + if (isHandled) break; } } catch (RemoteException e) { Slog.e(TAG, "", e); @@ -2643,7 +2660,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendEnableMsg(boolean quietMode, int reason, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + sendEnableMsg(quietMode, reason, packageName, false); + } + + private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, + isBle ? 1 : 0)); addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } -- GitLab From acbcf787e4908e392312b26e4ddca6078a9b531b Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 2 Sep 2021 18:40:07 +0800 Subject: [PATCH 1341/1408] Avoid Bluetooth gets turned on by enableBle BluetoothHandler was not able to tell whether the MESSAGE_ENABLE was from enable() or enableBle(), which sometimes lead to Bluetooth gets turned to STATE_ON by an enableBle() call. Fix this problem by adding a BLE specific argument in the message to differ the calls. Bug: 191404687 Test: Manual Change-Id: Ic9df612ec0e396932df2fb3bf3a5874bd4e7443c Merged-In: Ic9df612ec0e396932df2fb3bf3a5874bd4e7443c --- .../bluetooth/BluetoothManagerService.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 00cc7535263..bdeb4d56bfa 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -175,8 +175,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; - private int mWaitForEnableRetry; - private int mWaitForDisableRetry; private BluetoothModeChangeHelper mBluetoothModeChangeHelper; @@ -933,14 +931,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mState == BluetoothAdapter.STATE_ON || mState == BluetoothAdapter.STATE_BLE_ON || mState == BluetoothAdapter.STATE_TURNING_ON - || mState == BluetoothAdapter.STATE_TURNING_OFF) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + || mState == BluetoothAdapter.STATE_TURNING_OFF + || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on"); return true; } synchronized (mReceiver) { // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, true); } return true; } @@ -1734,6 +1733,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private class BluetoothHandler extends Handler { boolean mGetNameAddressOnly = false; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; BluetoothHandler(Looper looper) { super(looper); @@ -1781,11 +1782,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: int quietEnable = msg.arg1; + int isBle = msg.arg2; if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { // We are handling enable or disable right now, wait for it. mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, - quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + quietEnable, isBle), ENABLE_DISABLE_DELAY_MS); break; } @@ -1800,13 +1802,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + boolean isHandled = true; int state = mBluetooth.getState(); - if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - break; + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + if (isBle == 1) { + Slog.i(TAG, "Already at BLE_ON State"); + } else { + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); + mBluetooth.onLeServiceUp(); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + break; + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_TURNING_ON: + case BluetoothAdapter.STATE_ON: + Slog.i(TAG, "MESSAGE_ENABLE: already enabled"); + break; + default: + isHandled = false; + break; } + if (isHandled) break; } } catch (RemoteException e) { Slog.e(TAG, "", e); @@ -2559,7 +2576,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendEnableMsg(boolean quietMode, int reason, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + sendEnableMsg(quietMode, reason, packageName, false); + } + + private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, + isBle ? 1 : 0)); addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } -- GitLab From d19ee50bfa14ca999ff5ad58d1f1c90b578f3ba5 Mon Sep 17 00:00:00 2001 From: Ugo Yu Date: Thu, 2 Sep 2021 18:40:07 +0800 Subject: [PATCH 1342/1408] Avoid Bluetooth gets turned on by enableBle BluetoothHandler was not able to tell whether the MESSAGE_ENABLE was from enable() or enableBle(), which sometimes lead to Bluetooth gets turned to STATE_ON by an enableBle() call. Fix this problem by adding a BLE specific argument in the message to differ the calls. Bug: 191404687 Test: Manual Change-Id: Ic9df612ec0e396932df2fb3bf3a5874bd4e7443c (cherry picked from commit 0ba52e7126b0da1bffea5a91d413282a69aac019) --- .../bluetooth/BluetoothManagerService.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 608cbf2d3a7..d911388de3e 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -186,8 +186,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; - private int mWaitForEnableRetry; - private int mWaitForDisableRetry; private BluetoothModeChangeHelper mBluetoothModeChangeHelper; @@ -956,14 +954,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mState == BluetoothAdapter.STATE_ON || mState == BluetoothAdapter.STATE_BLE_ON || mState == BluetoothAdapter.STATE_TURNING_ON - || mState == BluetoothAdapter.STATE_TURNING_OFF) { - Log.d(TAG, "enableBLE(): Bluetooth already enabled"); + || mState == BluetoothAdapter.STATE_TURNING_OFF + || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { + Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on"); return true; } synchronized (mReceiver) { // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); + sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, + packageName, true); } return true; } @@ -1776,6 +1775,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private class BluetoothHandler extends Handler { boolean mGetNameAddressOnly = false; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; BluetoothHandler(Looper looper) { super(looper); @@ -1826,11 +1827,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { case MESSAGE_ENABLE: int quietEnable = msg.arg1; + int isBle = msg.arg2; if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { // We are handling enable or disable right now, wait for it. mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, - quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + quietEnable, isBle), ENABLE_DISABLE_DELAY_MS); break; } @@ -1845,13 +1847,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.readLock().lock(); if (mBluetooth != null) { + boolean isHandled = true; int state = mBluetooth.getState(); - if (state == BluetoothAdapter.STATE_BLE_ON) { - Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(mContext.getAttributionSource()); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - break; + switch (state) { + case BluetoothAdapter.STATE_BLE_ON: + if (isBle == 1) { + Slog.i(TAG, "Already at BLE_ON State"); + } else { + Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); + mBluetooth.onLeServiceUp(mContext.getAttributionSource()); + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + break; + case BluetoothAdapter.STATE_BLE_TURNING_ON: + case BluetoothAdapter.STATE_TURNING_ON: + case BluetoothAdapter.STATE_ON: + Slog.i(TAG, "MESSAGE_ENABLE: already enabled"); + break; + default: + isHandled = false; + break; } + if (isHandled) break; } } catch (RemoteException e) { Slog.e(TAG, "", e); @@ -2617,7 +2634,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } private void sendEnableMsg(boolean quietMode, int reason, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + sendEnableMsg(quietMode, reason, packageName, false); + } + + private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, + isBle ? 1 : 0)); addActiveLog(reason, packageName, true); mLastEnabledTime = SystemClock.elapsedRealtime(); } -- GitLab From ffab9955602ddb4c4b9278d37913d2b13679129e Mon Sep 17 00:00:00 2001 From: Roopa Sattiraju Date: Fri, 20 Aug 2021 15:06:57 -0700 Subject: [PATCH 1343/1408] Changing BluetoothHeadset APIs from @hide to @SystemApi For isInbandringing API - it is used in the call journey and would change the behavior(undeterministic) of the call if not used. Stop/StartScoUsingVirtualVoiceCall is used in the BluetoothShim layer specifically for testing and mocking in the Android Apps. Seems reasonable to keep this as a SystemAPI given that it has no params and unlikely to be changed. Bug: 195160939 Tag: #feature Test: Manual Change-Id: I02b2e7e5e0be3b462f7c25b655e669e2c7fe47eb --- .../java/android/bluetooth/BluetoothHeadset.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index b594ae34436..c0463243f41 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,17 +16,16 @@ package android.bluetooth; -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.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; @@ -1105,13 +1104,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE, }) - @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1140,13 +1139,13 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE, }) - @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1343,9 +1342,10 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if in-band ringing is enabled, false if in-band ringing is disabled * @hide */ + @SystemApi @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled() { if (DBG) { log("isInbandRingingEnabled()"); -- GitLab From 84f2c2e0f5c8abb1be68a37a4b16e33733f929ff Mon Sep 17 00:00:00 2001 From: Roopa Sattiraju Date: Fri, 20 Aug 2021 15:06:57 -0700 Subject: [PATCH 1344/1408] Changing BluetoothHeadset APIs from @hide to @SystemApi For isInbandringing API - it is used in the call journey and would change the behavior(undeterministic) of the call if not used. Stop/StartScoUsingVirtualVoiceCall is used in the BluetoothShim layer specifically for testing and mocking in the Android Apps. Seems reasonable to keep this as a SystemAPI given that it has no params and unlikely to be changed. Bug: 195160939 Tag: #feature Test: Manual Merged-In: I02b2e7e5e0be3b462f7c25b655e669e2c7fe47eb Change-Id: I02b2e7e5e0be3b462f7c25b655e669e2c7fe47eb --- framework/java/android/bluetooth/BluetoothHeadset.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 632572dea3c..18aa23a07b8 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -1018,8 +1018,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1048,8 +1048,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * - binder is dead or Bluetooth is disabled or other error * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; @@ -1227,7 +1227,8 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if in-band ringing is enabled, false if in-band ringing is disabled * @hide */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled() { if (DBG) { log("isInbandRingingEnabled()"); -- GitLab From a43e3aeb15a2af130d5e436a20cb5194b3ce9a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 24 May 2021 06:36:13 +0000 Subject: [PATCH 1345/1408] csip: Add Coordinated Set Identification Profile Tag: #feature Test: atest CsipSetCoordinatorStateMachineTest CsipSetCoordinatorServiceTest Bug: 150670922 Sponsor: jpawlowski@ Change-Id: I67536ddcc32ace82d63e19426dce19f2bc69cdea --- .../android/bluetooth/BluetoothAdapter.java | 9 + .../BluetoothCsipSetCoordinator.java | 553 ++++++++++++++++++ 2 files changed, 562 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2ed26a9f4da..1c96b8bec21 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3062,6 +3062,10 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; + } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) { + BluetoothCsipSetCoordinator csipSetCoordinator = + new BluetoothCsipSetCoordinator(context, listener, this); + return true; } else { return false; } @@ -3159,6 +3163,11 @@ public final class BluetoothAdapter { BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); break; + case BluetoothProfile.CSIP_SET_COORDINATOR: + BluetoothCsipSetCoordinator csipSetCoordinator = + (BluetoothCsipSetCoordinator) proxy; + csipSetCoordinator.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java new file mode 100644 index 00000000000..f0a8df0fa72 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java @@ -0,0 +1,553 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; +import android.content.AttributionSource; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the public APIs to control the Bluetooth CSIP set coordinator. + * + *

    BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothCsipSetCoordinator proxy object. + * + */ +public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothCsipSetCoordinator"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * @hide + */ + @SystemApi + public interface ClientLockCallback { + /** + * @hide + */ + @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked); + } + + private static class BluetoothCsipSetCoordinatorLockCallbackDelegate + extends IBluetoothCsipSetCoordinatorLockCallback.Stub { + private final ClientLockCallback mCallback; + private final Executor mExecutor; + + BluetoothCsipSetCoordinatorLockCallbackDelegate( + Executor executor, ClientLockCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) { + mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked)); + } + }; + + /** + * Intent used to broadcast the change in connection state of the CSIS + * Client. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to expose broadcast receiving device. + * + *

    This intent will have 2 extras: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device.
    • + *
    • {@link #EXTRA_CSIS_GROUP_ID} - Group identifier.
    • + *
    • {@link #EXTRA_CSIS_GROUP_SIZE} - Group size.
    • + *
    • {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID.
    • + *
    + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_DEVICE_AVAILABLE = + "android.bluetooth.action.CSIS_DEVICE_AVAILABLE"; + + /** + * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * Contains the group id. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID"; + + /** + * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE"; + + /** + * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_TYPE_UUID = + "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID"; + + /** + * Intent used to broadcast information about identified set member + * ready to connect. + * + *

    This intent will have one extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_CSIS_GROUP_ID} - Group identifier.
    • + *
    + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = + "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; + + /** + * Indicating that group was locked with success. + * + * @hide + */ + public static final int GROUP_LOCK_SUCCESS = 0; + + /** + * Indicating that group locked failed due to invalid group ID. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1; + + /** + * Indicating that group locked failed due to empty group. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2; + + /** + * Indicating that group locked failed due to group members being disconnected. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3; + + /** + * Indicating that group locked failed due to group member being already locked. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4; + + /** + * Indicating that group locked failed due to other reason. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5; + + /** + * Indicating that group member in locked state was lost. + * + * @hide + */ + public static final int LOCKED_GROUP_MEMBER_LOST = 6; + + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG, + IBluetoothCsipSetCoordinator.class.getName()) { + @Override + public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) { + return IBluetoothCsipSetCoordinator.Stub.asInterface( + Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local + * Bluetooth CSIS service. + */ + /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener, BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * @hide + */ + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothCsipSetCoordinator getService() { + return mProfileConnector.getService(); + } + + /** + * Lock the set. + * @param groupId group ID to lock, + * @param executor callback executor, + * @param cb callback to report lock and unlock events - stays valid until the app unlocks + * using the returned lock identifier or the lock timeouts on the remote side, + * as per CSIS specification, + * @return unique lock identifier used for unlocking or null if lock has failed. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public + @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor, + @Nullable ClientLockCallback cb) { + if (VDBG) { + log("groupLockSet()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + IBluetoothCsipSetCoordinatorLockCallback delegate = null; + if ((executor != null) && (cb != null)) { + delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); + } + return service.groupLock(groupId, delegate, mAttributionSource).getUuid(); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } + } + + /** + * Unlock the set. + * @param lockUuid unique lock identifier + * @return true if unlocked, false on error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean groupUnlock(@NonNull UUID lockUuid) { + if (VDBG) { + log("groupLockSet()"); + } + if (lockUuid == null) { + return false; + } + + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource); + return true; + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get device's groups. + * @param device the active device + * @return Map of groups ids and related UUIDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getGroupUuidMapByDevice()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getGroupUuidMapByDevice(device, mAttributionSource); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new HashMap<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new HashMap<>(); + } + } + + /** + * Get group id for the given UUID + * @param uuid + * @return list of group IDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull List getAllGroupIds(@Nullable ParcelUuid uuid) { + if (VDBG) { + log("getAllGroupIds()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getAllGroupIds(uuid, mAttributionSource); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List getConnectedDevices() { + if (VDBG) { + log("getConnectedDevices()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getConnectedDevices(mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) { + log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getDevicesMatchingConnectionStates(states, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @BluetoothProfile.BtProfileState int getConnectionState( + @Nullable BluetoothDevice device) { + if (VDBG) { + log("getState(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * 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_PRIVILEGED) + public boolean setConnectionPolicy( + @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { + if (DBG) { + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * 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_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getConnectionPolicy(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getConnectionPolicy(device, mAttributionSource); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} -- GitLab From eb8b2bb4f444f8c11a2f97bb5e891e4ba384858b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 24 May 2021 06:36:13 +0000 Subject: [PATCH 1346/1408] csip: Add Coordinated Set Identification Profile Tag: #feature Test: atest CsipSetCoordinatorStateMachineTest CsipSetCoordinatorServiceTest Bug: 150670922 Sponsor: jpawlowski@ Merged-In: I67536ddcc32ace82d63e19426dce19f2bc69cdea Change-Id: I67536ddcc32ace82d63e19426dce19f2bc69cdea --- .../android/bluetooth/BluetoothAdapter.java | 9 + .../BluetoothCsipSetCoordinator.java | 550 ++++++++++++++++++ 2 files changed, 559 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index d487025631c..313dd3e2bed 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2819,6 +2819,10 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; + } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) { + BluetoothCsipSetCoordinator csipSetCoordinator = + new BluetoothCsipSetCoordinator(context, listener); + return true; } else { return false; } @@ -2908,6 +2912,11 @@ public final class BluetoothAdapter { BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); break; + case BluetoothProfile.CSIP_SET_COORDINATOR: + BluetoothCsipSetCoordinator csipSetCoordinator = + (BluetoothCsipSetCoordinator) proxy; + csipSetCoordinator.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java new file mode 100644 index 00000000000..cb542e5ba38 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java @@ -0,0 +1,550 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the public APIs to control the Bluetooth CSIP set coordinator. + * + *

    BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothCsipSetCoordinator proxy object. + * + */ +public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothCsipSetCoordinator"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * @hide + */ + @SystemApi + public interface ClientLockCallback { + /** + * @hide + */ + @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked); + } + + private static class BluetoothCsipSetCoordinatorLockCallbackDelegate + extends IBluetoothCsipSetCoordinatorLockCallback.Stub { + private final ClientLockCallback mCallback; + private final Executor mExecutor; + + BluetoothCsipSetCoordinatorLockCallbackDelegate( + Executor executor, ClientLockCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) { + mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked)); + } + }; + + /** + * Intent used to broadcast the change in connection state of the CSIS + * Client. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to expose broadcast receiving device. + * + *

    This intent will have 2 extras: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device.
    • + *
    • {@link #EXTRA_CSIS_GROUP_ID} - Group identifier.
    • + *
    • {@link #EXTRA_CSIS_GROUP_SIZE} - Group size.
    • + *
    • {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID.
    • + *
    + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_DEVICE_AVAILABLE = + "android.bluetooth.action.CSIS_DEVICE_AVAILABLE"; + + /** + * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * Contains the group id. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID"; + + /** + * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE"; + + /** + * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. + * + * @hide + */ + public static final String EXTRA_CSIS_GROUP_TYPE_UUID = + "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID"; + + /** + * Intent used to broadcast information about identified set member + * ready to connect. + * + *

    This intent will have one extra: + *

      + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active.
    • + *
    • {@link #EXTRA_CSIS_GROUP_ID} - Group identifier.
    • + *
    + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = + "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; + + /** + * Indicating that group was locked with success. + * + * @hide + */ + public static final int GROUP_LOCK_SUCCESS = 0; + + /** + * Indicating that group locked failed due to invalid group ID. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1; + + /** + * Indicating that group locked failed due to empty group. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2; + + /** + * Indicating that group locked failed due to group members being disconnected. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3; + + /** + * Indicating that group locked failed due to group member being already locked. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4; + + /** + * Indicating that group locked failed due to other reason. + * + * @hide + */ + public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5; + + /** + * Indicating that group member in locked state was lost. + * + * @hide + */ + public static final int LOCKED_GROUP_MEMBER_LOST = 6; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG, + IBluetoothCsipSetCoordinator.class.getName()) { + @Override + public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) { + return IBluetoothCsipSetCoordinator.Stub.asInterface( + Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local + * Bluetooth CSIS service. + */ + /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * @hide + */ + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothCsipSetCoordinator getService() { + return mProfileConnector.getService(); + } + + /** + * Lock the set. + * @param groupId group ID to lock, + * @param executor callback executor, + * @param cb callback to report lock and unlock events - stays valid until the app unlocks + * using the returned lock identifier or the lock timeouts on the remote side, + * as per CSIS specification, + * @return unique lock identifier used for unlocking or null if lock has failed. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public + @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor, + @Nullable ClientLockCallback cb) { + if (VDBG) { + log("groupLockSet()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + IBluetoothCsipSetCoordinatorLockCallback delegate = null; + if ((executor != null) && (cb != null)) { + delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); + } + return service.groupLock(groupId, delegate).getUuid(); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } + } + + /** + * Unlock the set. + * @param lockUuid unique lock identifier + * @return true if unlocked, false on error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean groupUnlock(@NonNull UUID lockUuid) { + if (VDBG) { + log("groupLockSet()"); + } + if (lockUuid == null) { + return false; + } + + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + service.groupUnlock(new ParcelUuid(lockUuid)); + return true; + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get device's groups. + * @param device the active device + * @return Map of groups ids and related UUIDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getGroupUuidMapByDevice()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getGroupUuidMapByDevice(device); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new HashMap<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new HashMap<>(); + } + } + + /** + * Get group id for the given UUID + * @param uuid + * @return list of group IDs + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @NonNull List getAllGroupIds(@Nullable ParcelUuid uuid) { + if (VDBG) { + log("getAllGroupIds()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled()) { + return service.getAllGroupIds(uuid); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List getConnectedDevices() { + if (VDBG) { + log("getConnectedDevices()"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) { + log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled()) { + try { + return service.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public + @BluetoothProfile.BtProfileState int getConnectionState( + @Nullable BluetoothDevice device) { + if (VDBG) { + log("getState(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * 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_PRIVILEGED) + public boolean setConnectionPolicy( + @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { + if (DBG) { + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * 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_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) { + log("getConnectionPolicy(" + device + ")"); + } + final IBluetoothCsipSetCoordinator service = getService(); + try { + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getConnectionPolicy(device); + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} -- GitLab From 48b07878b044f2c7c6713458a15650da3514bf07 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Wed, 18 Aug 2021 16:35:00 -0700 Subject: [PATCH 1347/1408] Bluetooth: Fix formatting in getAlias() Bug: 180747689 Test: manual Change-Id: Ic309f4aad116fd424d5d0d0e2016d61be8826b78 (cherry picked from commit 4e7449ce893cf3f3019b0201b766d0a784da1754) --- framework/java/android/bluetooth/BluetoothDevice.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index bbb550fd634..5a5fac4a766 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1329,7 +1329,10 @@ public final class BluetoothDevice implements Parcelable, Attributable { if (alias == null) { return getName(); } - return alias; + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From dc0e2a44fcfc89fedf7ee6ac4a0deb7d13dec3df Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Thu, 19 Aug 2021 12:51:55 -0700 Subject: [PATCH 1348/1408] Make createBond(int) a system API BUG: 195156317 Test: Manual Change-Id: I10d9ac305cbc1bb28c8150e1b167a0e61ad3f04e --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 12211485cc0..9c4251dd4c0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1445,7 +1445,7 @@ public final class BluetoothDevice implements Parcelable, Attributable { * @throws IllegalArgumentException if an invalid transport was specified * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) -- GitLab From 8f988e7e3f92c118855eb2d1e515ac4a03182219 Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Thu, 19 Aug 2021 12:51:55 -0700 Subject: [PATCH 1349/1408] Make createBond(int) a system API BUG: 195156317 Test: Manual Change-Id: I10d9ac305cbc1bb28c8150e1b167a0e61ad3f04e Merged-In: I10d9ac305cbc1bb28c8150e1b167a0e61ad3f04e --- framework/java/android/bluetooth/BluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 38fb90d9c4a..6b98bbc6a25 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1326,7 +1326,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IllegalArgumentException if an invalid transport was specified * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond(int transport) { return createBondInternal(transport, null, null); -- GitLab From f032cdfc357dbeaec4ec2350c7a68fab54b45fad Mon Sep 17 00:00:00 2001 From: Roopa Sattiraju Date: Thu, 16 Sep 2021 23:36:05 -0700 Subject: [PATCH 1350/1408] Adding sattiraju@ as OWNER and also changing the order of ldaps to be alphabetical Tag: #feature Bug: 200240100 Test: Manual Change-Id: I15ccfeae1e1a061a002be9c2e202104352b1ce98 --- framework/java/android/bluetooth/OWNERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS index 2239100a552..fbee5777317 100644 --- a/framework/java/android/bluetooth/OWNERS +++ b/framework/java/android/bluetooth/OWNERS @@ -1,5 +1,6 @@ # Bug component: 27441 -zachoverflow@google.com -siyuanh@google.com rahulsabnis@google.com +sattiraju@google.com +siyuanh@google.com +zachoverflow@google.com -- GitLab From d6cc3d43210c0e863d31abf4211b4b24dbc80678 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Tue, 19 Jan 2021 22:50:31 +0800 Subject: [PATCH 1351/1408] csip: Expose an extra data with intent and store in CachedBluetoothDevice Bug: 178981521 Bug: 150670922 Test: Discover and pair with the CSIP supported device, and check the pairing string in the dialog Change-Id: I3e99c59e0cb974409291e1b4c28393106784e133 --- framework/java/android/bluetooth/BluetoothDevice.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 9c4251dd4c0..437d9ff8e49 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -110,7 +110,7 @@ public final class BluetoothDevice implements Parcelable, Attributable { *

    Sent when a remote device is found during discovery. *

    Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or - * {@link #EXTRA_RSSI} if they are available. + * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available. */ // TODO: Change API to not broadcast RSSI if not available (incoming connection) @RequiresLegacyBluetoothPermission @@ -278,6 +278,15 @@ public final class BluetoothDevice implements Parcelable, Attributable { */ public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; + /** + * Used as an bool extra field in {@link #ACTION_FOUND} intents. + * It contains the information if device is discovered as member of a coordinated set or not. + * Pairing with device that belongs to a set would trigger pairing with the rest of set members. + * See Bluetooth CSIP specification for more details. + */ + public static final String EXTRA_IS_COORDINATED_SET_MEMBER = + "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; + /** * Used as a Parcelable {@link BluetoothClass} extra field in {@link * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. -- GitLab From 61c3851140d76bc750b82afb602e4128fbc1e822 Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Tue, 19 Jan 2021 22:50:31 +0800 Subject: [PATCH 1352/1408] csip: Expose an extra data with intent and store in CachedBluetoothDevice Bug: 178981521 Bug: 150670922 Test: Discover and pair with the CSIP supported device, and check the pairing string in the dialog Merged-In: I3e99c59e0cb974409291e1b4c28393106784e133 Change-Id: I3e99c59e0cb974409291e1b4c28393106784e133 --- framework/java/android/bluetooth/BluetoothDevice.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 38fb90d9c4a..b5ede8d1cdb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -106,7 +106,7 @@ public final class BluetoothDevice implements Parcelable { *

    Sent when a remote device is found during discovery. *

    Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or - * {@link #EXTRA_RSSI} if they are available. + * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available. *

    Requires {@link android.Manifest.permission#BLUETOOTH} and * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive. */ @@ -256,6 +256,15 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; + /** + * Used as an bool extra field in {@link #ACTION_FOUND} intents. + * It contains the information if device is discovered as member of a coordinated set or not. + * Pairing with device that belongs to a set would trigger pairing with the rest of set members. + * See Bluetooth CSIP specification for more details. + */ + public static final String EXTRA_IS_COORDINATED_SET_MEMBER = + "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; + /** * Used as a Parcelable {@link BluetoothClass} extra field in {@link * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. -- GitLab From 199a385e223710fd5d3fd02e10d18f1217a233d8 Mon Sep 17 00:00:00 2001 From: William Escande Date: Wed, 22 Sep 2021 20:22:36 +0200 Subject: [PATCH 1353/1408] Use AttributionSource Builder Attribution source constructor are hidden api Add a Builder option to take a AttributionSource as parameter Test: atest BluetoothInstrumentationTests Bug: 195144968 Tag: #refactor Ignore-AOSP-First: No such thing on aosp Change-Id: I901c8afff6a40bd8484fd8e10baf290aa483c280 --- framework/java/android/bluetooth/BluetoothManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index c21362cd89f..20152f3d247 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -88,8 +88,9 @@ public final class BluetoothManager { uid = android.os.Process.SYSTEM_UID; } try { - res = new AttributionSource(uid, - AppGlobals.getPackageManager().getPackagesForUid(uid)[0], null); + res = new AttributionSource.Builder(uid) + .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0]) + .build(); } catch (RemoteException ignored) { } } -- GitLab From d00916c7fe16c0ae0c107b7b343e85085836e27d Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 28 Sep 2021 16:16:07 -0700 Subject: [PATCH 1354/1408] Add BluetoothDevice#connect and BluetoothDevice#disconnect as System APIs Tag: #feature Bug: 201462141 Test: Manual Change-Id: I79332c63e99efd9e90036ba2c693835151cc1240 --- .../android/bluetooth/BluetoothAdapter.java | 65 -------------- .../android/bluetooth/BluetoothDevice.java | 84 +++++++++++++++++++ .../bluetooth/BluetoothStatusCodes.java | 2 +- 3 files changed, 85 insertions(+), 66 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 77abbe96dbd..e9be0ecd204 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2004,71 +2004,6 @@ public final class BluetoothAdapter { return false; } - /** - * Connects all enabled and supported bluetooth profiles between the local and remote device. - * Connection is asynchronous and you should listen to each profile's broadcast intent - * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example, - * to verify a2dp is connected, you would listen for - * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} - * - * @param device is the remote device with which to connect these profiles - * @return true if message sent to try to connect all profiles, false if an error occurred - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.connectAllEnabledProfiles(device, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; - } - - /** - * Disconnects all enabled and supported bluetooth profiles between the local and remote device. - * Disconnection is asynchronous and you should listen to each profile's broadcast intent - * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, - * to verify a2dp is disconnected, you would listen for - * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} - * - * @param device is the remote device with which to disconnect these profiles - * @return true if message sent to try to disconnect all profiles, false if an error occurred - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.disconnectAllEnabledProfiles(device, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; - } - /** * Return true if the multi advertisement is supported by the chipset * diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 437d9ff8e49..c71fcc637cb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1680,6 +1680,90 @@ public final class BluetoothDevice implements Parcelable, Attributable { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED + }) + public @interface ConnectionReturnValues{} + + /** + * Connects all user enabled and supported bluetooth profiles between the local and remote + * device. If no profiles are user enabled (e.g. first connection), we connect all supported + * profiles. If the device is not already connected, this will page the device before initiating + * profile connections. Connection is asynchronous and you should listen to each profile's + * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. + * For example, to verify a2dp is connected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} + * + * @return whether the messages were successfully sent to try to connect all profiles + * @throws IllegalArgumentException if the device address is invalid + * + * @hide + */ + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + android.Manifest.permission.MODIFY_PHONE_STATE, + }) + public @ConnectionReturnValues int connect() { + if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { + throw new IllegalArgumentException("device cannot have an invalid address"); + } + + try { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot connect to remote device."); + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + return sService.connectAllEnabledProfiles(this, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Disconnects all connected bluetooth profiles between the local and remote device. + * Disconnection is asynchronous and you should listen to each profile's broadcast intent + * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, + * to verify a2dp is disconnected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} + * + * @return whether the messages were successfully sent to try to disconnect all profiles + * @throws IllegalArgumentException if the device address is invalid + * + * @hide + */ + @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @ConnectionReturnValues int disconnect() { + if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { + throw new IllegalArgumentException("device cannot have an invalid address"); + } + + try { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot disconnect from remote device."); + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + return sService.disconnectAllEnabledProfiles(this, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "", e); + throw e.rethrowFromSystemServer(); + } + } + /** * Returns whether there is an open connection to this device. * diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java index 31bb0f68c6f..3e46c498e33 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -21,7 +21,7 @@ import android.annotation.SystemApi; /** * A class with constants representing possible return values for Bluetooth APIs. General return * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999. - * API-specific return values start at 1000. The exception to this is the "other" error code which + * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which * occupies the max integer value. */ public final class BluetoothStatusCodes { -- GitLab From fd3e858be2c3686d0b5a5b4ea2e80257fa204c92 Mon Sep 17 00:00:00 2001 From: wescande Date: Wed, 11 Aug 2021 14:56:06 +0200 Subject: [PATCH 1355/1408] Add getActiveDevices api Bug: 195149213 Test: Manual Tag: #feature Change-Id: I0836f7bd0009a49b4db7f08e9f347fe3e1a76f84 --- .../android/bluetooth/BluetoothAdapter.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e9be0ecd204..1eadf954ba5 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -511,6 +511,12 @@ public final class BluetoothAdapter { @SystemApi public static final int ACTIVE_DEVICE_ALL = 2; + /** @hide */ + @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP, + BluetoothProfile.HEARING_AID}) + @Retention(RetentionPolicy.SOURCE) + public @interface ActiveDeviceProfile {} + /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -2004,6 +2010,51 @@ public final class BluetoothAdapter { return false; } + /** + * Get the active devices for the BluetoothProfile specified + * + * @param profile is the profile from which we want the active devices. + * Possible values are: + * {@link BluetoothProfile#HEADSET}, + * {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#HEARING_AID} + * @return A list of active bluetooth devices + * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @NonNull List getActiveDevices(@ActiveDeviceProfile int profile) { + if (profile != BluetoothProfile.HEADSET + && profile != BluetoothProfile.A2DP + && profile != BluetoothProfile.HEARING_AID) { + Log.e(TAG, "Invalid profile param value in getActiveDevices"); + throw new IllegalArgumentException("Profiles must be one of " + + "BluetoothProfile.A2DP, " + + "BluetoothProfile.HEARING_AID, or" + + "BluetoothProfile.HEARING_AID"); + } + try { + mServiceLock.readLock().lock(); + if (mService != null) { + if (DBG) { + Log.d(TAG, "getActiveDevices(profile= " + + BluetoothProfile.getProfileName(profile) + ")"); + } + return mService.getActiveDevices(profile, mAttributionSource); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return new ArrayList<>(); + } + /** * Return true if the multi advertisement is supported by the chipset * -- GitLab From f599dc49a2b0e598cfaacefebb4c24cbabf06299 Mon Sep 17 00:00:00 2001 From: Sal Savage Date: Thu, 2 Sep 2021 11:52:23 -0700 Subject: [PATCH 1356/1408] Make BluetoothAdapter#disable(boolean persist) a @SystemApi Bug: 196235708 Test: build, flash car hardware, test basic functionality Change-Id: I952c3d2ce3b7ec70a384e9a96e172d6ab90c23e8 --- framework/java/android/bluetooth/BluetoothAdapter.java | 8 ++++++-- .../android/server/bluetooth/BluetoothManagerService.java | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index e9be0ecd204..9c69903d599 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -1245,13 +1245,17 @@ public final class BluetoothAdapter { /** * Turn off the local Bluetooth adapter and don't persist the setting. * + * @param persist Indicate whether the off state should be persisted following the next reboot * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage(trackingBug = 171933273) + @SystemApi @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disable(boolean persist) { try { diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index d911388de3e..f62935ab1b1 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1154,6 +1154,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public boolean disable(AttributionSource attributionSource, boolean persist) throws RemoteException { + if (!persist) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, + "Need BLUETOOTH_PRIVILEGED permission"); + } + final String packageName = attributionSource.getPackageName(); if (!checkBluetoothPermissions(attributionSource, "disable", true)) { if (DBG) { @@ -2599,7 +2604,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions()); } } -- GitLab From d22eafa0d088eaf857b8cc3530eb5e308282fa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 8 Oct 2021 20:38:44 +0000 Subject: [PATCH 1357/1408] leaudio: update UUID Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: I7e553bd9ffec52c1a2f70e80a194ad7214fe905a --- framework/java/android/bluetooth/BluetoothUuid.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 67b72522120..325a7710725 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -155,12 +155,11 @@ public final class BluetoothUuid { @SystemApi public static final ParcelUuid HEARING_AID = ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); - /** Placeholder until specification is released - * @hide */ + /** @hide */ @NonNull @SystemApi public static final ParcelUuid LE_AUDIO = - ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE"); + ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB"); /** @hide */ @NonNull @SystemApi -- GitLab From 3a7a78ddbd2920062f4b372acc48487866738627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 8 Oct 2021 20:37:41 +0000 Subject: [PATCH 1358/1408] leaudio: Fix link to intent Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: I7782f95cca7478dcd585d435f585bba6a453f1ab --- framework/java/android/bluetooth/BluetoothLeAudio.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 3ea865bfd6e..41617000805 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -433,7 +433,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { *

    This API returns false in scenarios like the profile on the * device is not connected or Bluetooth is not turned on. * When this API returns true, it is guaranteed that the - * {@link #ACTION_LEAUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * {@link #ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted * with the active device. * * -- GitLab From 5b50e4d72308096246029f4962fd20309972923d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 8 Oct 2021 20:19:12 +0000 Subject: [PATCH 1359/1408] leaudio: Add set volume API Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: Icb2e7681e4b5e7ba2e796671ff6f4c59bbff29d7 --- .../android/bluetooth/BluetoothLeAudio.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 41617000805..a139eba3451 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -511,6 +511,30 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { } } + /** + * Set volume for the streaming devices + * + * @param volume volume to set + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) + public void setVolume(int volume) { + if (VDBG) log("setVolume(vol: " + volume + " )"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + service.setVolume(volume, mAttributionSource); + return; + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return; + } + } + /** * Set connection policy of the profile * -- GitLab From 99f601c2ea541189d941a7b5d2572e13ba33df86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 8 Oct 2021 21:11:45 +0000 Subject: [PATCH 1360/1408] leaudio: Remove not needed group status Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: Id9145e6e2631d0eae102bcc502ae4aa233b61d7b --- .../android/bluetooth/BluetoothLeAudio.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index a139eba3451..c30c933b6ef 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -199,11 +199,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * *

    *

      - *
    • {@link #GROUP_STATUS_IDLE}
    • - *
    • {@link #GROUP_STATUS_STREAMING}
    • - *
    • {@link #GROUP_STATUS_SUSPENDED}
    • - *
    • {@link #GROUP_STATUS_RECONFIGURED}
    • - *
    • {@link #GROUP_STATUS_DESTROYED}
    • + *
    • {@link #GROUP_STATUS_ACTIVE}
    • + *
    • {@link #GROUP_STATUS_INACTIVE}
    • *
    *

    * @hide @@ -241,6 +238,19 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; + /** + * Indicating that group is Active ( Audio device is available ) + * @hide + */ + public static final int GROUP_STATUS_ACTIVE = IBluetoothLeAudio.GROUP_STATUS_ACTIVE; + + /** + * Indicating that group is Inactive ( Audio device is not available ) + * @hide + */ + public static final int GROUP_STATUS_INACTIVE = IBluetoothLeAudio.GROUP_STATUS_INACTIVE; + + private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", IBluetoothLeAudio.class.getName()) { -- GitLab From 84b11f5a14e1c0877012e9c9758c672d82c6d32d Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 7 Oct 2021 14:24:08 +0200 Subject: [PATCH 1361/1408] Remove bytes matcher from base Bytes matcher has been moved to frameworks/libs/modules-utils in this CL: aosp/1821533 Bug: 200202780 Tag: #refactor Test: atest cts/tests/tests/bluetooth/src/android/bluetooth/cts/ScanRecordTest.java frameworks/base/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java Change-Id: I4d374217100635a49918f8e88f2cb5327e758e9f --- framework/tests/Android.bp | 5 ++++- framework/tests/src/android/bluetooth/le/ScanRecordTest.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/tests/Android.bp b/framework/tests/Android.bp index a2e4dff8f82..68416dd6546 100644 --- a/framework/tests/Android.bp +++ b/framework/tests/Android.bp @@ -15,7 +15,10 @@ android_test { "android.test.runner", "android.test.base", ], - static_libs: ["junit"], + static_libs: [ + "junit", + "modules-utils-bytesmatcher", + ], platform_apis: true, certificate: "platform", } diff --git a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java index c287ea9d856..4e817d4a0d9 100644 --- a/framework/tests/src/android/bluetooth/le/ScanRecordTest.java +++ b/framework/tests/src/android/bluetooth/le/ScanRecordTest.java @@ -16,11 +16,11 @@ package android.bluetooth.le; -import android.os.BytesMatcher; import android.os.ParcelUuid; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.util.HexDump; +import com.android.modules.utils.BytesMatcher; import junit.framework.TestCase; -- GitLab From 7e33f238b2fd191a74183b11bf6abf35e7668665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Mon, 11 Oct 2021 11:58:12 +0000 Subject: [PATCH 1362/1408] bluetooth: Fix common typo sponsor: jpawlowski@ test: NA Change-Id: I0e93b87371f434c229f211c480727cc26434a5e0 --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 3b744a73e75..06ce0530d47 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2099,7 +2099,7 @@ public final class BluetoothAdapter { try { return mManagerService.isBleScanAlwaysAvailable(); } catch (RemoteException e) { - Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e); + Log.e(TAG, "remote exception when calling isBleScanAlwaysAvailable", e); return false; } } @@ -2307,7 +2307,7 @@ public final class BluetoothAdapter { try { return mManagerService.isHearingAidProfileSupported(); } catch (RemoteException e) { - Log.e(TAG, "remote expection when calling isHearingAidProfileSupported", e); + Log.e(TAG, "remote exception when calling isHearingAidProfileSupported", e); return false; } } -- GitLab From 48c38333b177614688dea8090cc6eee24b047b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 8 Oct 2021 21:12:19 +0000 Subject: [PATCH 1363/1408] leaudio: Add API to add/remove group node Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: Ib385b24d6b10754d9cadea2363e81c78b4382a44 --- .../android/bluetooth/BluetoothLeAudio.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index c30c933b6ef..92fcb6633ec 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -250,6 +250,17 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int GROUP_STATUS_INACTIVE = IBluetoothLeAudio.GROUP_STATUS_INACTIVE; + /** + * Indicating that node has been added to the group. + * @hide + */ + public static final int GROUP_NODE_ADDED = IBluetoothLeAudio.GROUP_NODE_ADDED; + + /** + * Indicating that node has been removed from the group. + * @hide + */ + public static final int GROUP_NODE_REMOVED = IBluetoothLeAudio.GROUP_NODE_REMOVED; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", @@ -545,6 +556,61 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { } } + /** + * Add device to the given group. + * @param group_id group ID the device is being added to + * @param device the active device + * @return true on success, otherwise false + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED + }) + public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) { + if (VDBG) log("groupAddNode()"); + final IBluetoothLeAudio service = getService(); + try { + if (service != null && mAdapter.isEnabled()) { + return service.groupAddNode(group_id, device, mAttributionSource); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Remove device from a given group. + * @param group_id group ID the device is being removed from + * @param device the active device + * @return true on success, otherwise false + * + * @hide + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED + }) + public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) { + if (VDBG) log("groupRemoveNode()"); + final IBluetoothLeAudio service = getService(); + try { + if (service != null && mAdapter.isEnabled()) { + return service.groupRemoveNode(group_id, device, mAttributionSource); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + /** * Set connection policy of the profile * -- GitLab From 8766c525e4e540502e5bb5299592424a6a3ae19b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Wed, 13 Oct 2021 08:46:21 +0000 Subject: [PATCH 1364/1408] bluetoothUuid: Add placeholder for CAP uuid Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: I69a9cb6a341d42eff3e5ae92d9c99ab5fa3bf9a3 --- framework/java/android/bluetooth/BluetoothUuid.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 325a7710725..858819e15ab 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -188,6 +188,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid CAP = + ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); -- GitLab From dd17b23efa9f85bbf41bc3b2d71f91e6fce03a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Wed, 13 Oct 2021 08:08:13 +0000 Subject: [PATCH 1365/1408] leaudio: Add missing context types BYPASS_INCLUSIVE_LANGUAGE_REASON=exact wording for constant from BT specification Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: compile Change-Id: If0e5861e882636afba6a29c0ae34362d179035ea --- .../android/bluetooth/BluetoothLeAudio.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 92fcb6633ec..d7940eb9d3a 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -155,6 +155,12 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public static final String ACTION_LE_AUDIO_CONF_CHANGED = "android.bluetooth.action.LE_AUDIO_CONF_CHANGED"; + /** + * Indicates unspecified audio content. + * @hide + */ + public static final int CONTEXT_TYPE_UNSPECIFIED = 0x0001; + /** * Indicates conversation between humans as, for example, in telephony or video calls. * @hide @@ -167,6 +173,66 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int CONTEXT_TYPE_MEDIA = 0x0004; + /** + * Indicates instructional audio as, for example, in navigation, traffic announcements + * or user guidance. + * @hide + */ + public static final int CONTEXT_TYPE_INSTRUCTIONAL = 0x0008; + + /** + * Indicates attention seeking audio as, for example, in beeps signalling arrival of a message + * or keyboard clicks. + * @hide + */ + public static final int CONTEXT_TYPE_ATTENTION_SEEKING = 0x0010; + + /** + * Indicates immediate alerts as, for example, in a low battery alarm, timer expiry or alarm + * clock. + * @hide + */ + public static final int CONTEXT_TYPE_IMMEDIATE_ALERT = 0x0020; + + /** + * Indicates man machine communication as, for example, with voice recognition or virtual + * assistant. + * @hide + */ + public static final int CONTEXT_TYPE_MAN_MACHINE = 0x0040; + + /** + * Indicates emergency alerts as, for example, with fire alarms or other urgent alerts. + * @hide + */ + public static final int CONTEXT_TYPE_EMERGENCY_ALERT = 0x0080; + + /** + * Indicates ringtone as in a call alert. + * @hide + */ + public static final int CONTEXT_TYPE_RINGTONE = 0x0100; + + /** + * Indicates audio associated with a television program and/or with metadata conforming to the + * Bluetooth Broadcast TV profile. + * @hide + */ + public static final int CONTEXT_TYPE_TV = 0x0200; + + /** + * Indicates audio associated with a low latency live audio stream. + * + * @hide + */ + public static final int CONTEXT_TYPE_LIVE = 0x0400; + + /** + * Indicates audio associated with a video game stream. + * @hide + */ + public static final int CONTEXT_TYPE_GAME = 0x0800; + /** * This represents an invalid group ID. * -- GitLab From d269b065bcea08f3c6e53866b1f4ab9c4d1589bf Mon Sep 17 00:00:00 2001 From: Mehmet Murat Sevim Date: Mon, 11 May 2020 13:42:07 +0100 Subject: [PATCH 1366/1408] Add scan result logs. Some GMSCore modules are missing some scan result despite the peer device is discovered by the Android BT stack. These logs will help debug such problems. Bug: 154942993 Bug: 199827901 Test: IOP and BCST Tag: #stability Change-Id: I6bd98df09ea2beb695aa9b4ac63bde307e77b182 (cherry picked from commit f204effb5cc6f1cc8e5e3f75ef343db3bd339b67) --- .../android/bluetooth/le/BluetoothLeScanner.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index ee173dbc4ad..e9bd64364e6 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -514,16 +514,27 @@ public final class BluetoothLeScanner { @Override public void onScanResult(final ScanResult scanResult) { Attributable.setAttributionSource(scanResult, mAttributionSource); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId); + } if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { - if (mScannerId <= 0) return; + if (mScannerId <= 0) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Ignoring result as scan stopped."); + } + return; + }; } Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onScanResult() - handler run"); + } mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); } }); -- GitLab From 39639385f90678e97aa6848ae3b43de6bfb3b49e Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Wed, 13 Oct 2021 10:46:12 +0000 Subject: [PATCH 1367/1408] Removed BluetoothDevice#prepareToEnterProcess Tag: #feature Bug: 200202780 Test: manual Change-Id: I8d4be1da1bcb5b819c324f1a3a89c7dc317c31d6 --- .../java/android/bluetooth/BluetoothDevice.java | 5 ----- .../java/android/bluetooth/BluetoothManager.java | 13 ++++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index c71fcc637cb..9caeb297ace 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1186,11 +1186,6 @@ public final class BluetoothDevice implements Parcelable, Attributable { mAttributionSource = attributionSource; } - /** {@hide} */ - public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) { - setAttributionSource(attributionSource); - } - @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 20152f3d247..b5df4db2460 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,15 +62,15 @@ public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = false; - private final AttributionSource mAttributionSource; + private static AttributionSource sAttributionSource = null; private final BluetoothAdapter mAdapter; /** * @hide */ public BluetoothManager(Context context) { - mAttributionSource = resolveAttributionSource(context); - mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); + sAttributionSource = resolveAttributionSource(context); + mAdapter = BluetoothAdapter.createAdapter(sAttributionSource); } /** {@hide} */ @@ -79,6 +79,9 @@ public final class BluetoothManager { if (context != null) { res = context.getAttributionSource(); } + else if (sAttributionSource != null) { + return sAttributionSource; + } if (res == null) { res = ActivityThread.currentAttributionSource(); } @@ -198,8 +201,8 @@ public final class BluetoothManager { IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; devices = Attributable.setAttributionSource( - iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), - mAttributionSource); + iGatt.getDevicesMatchingConnectionStates(states, sAttributionSource), + sAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From a9a39e3e5e264c1340497b2e7e06c5c7c9a65369 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Tue, 21 Sep 2021 13:23:25 +0000 Subject: [PATCH 1368/1408] Adding @SystemApi annotation to Bluetooth energy stats. Added @SystemApi annotation to BluetoothActivityEnergyInfo and UidTraffic getters for mainline compatibility. Replaced raw array by List. Deleted unused constructors. Tag: #feature Bug: 200201373 Test: Manual Change-Id: Id93215f48bf08d31ac269af9e064cf598f50b6d3 --- .../BluetoothActivityEnergyInfo.java | 65 ++++++++++++++----- .../java/android/bluetooth/UidTraffic.java | 29 +++++++-- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index df065bf1bf8..f371c6d7822 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -16,18 +16,25 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.List; /** * Record of energy and activity information from controller and * underlying bt stack state.Timestamp the record with system - * time + * time. * * @hide */ +@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) public final class BluetoothActivityEnergyInfo implements Parcelable { private final long mTimestamp; private int mBluetoothStackState; @@ -35,13 +42,24 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { private long mControllerRxTimeMs; private long mControllerIdleTimeMs; private long mControllerEnergyUsed; - private UidTraffic[] mUidTraffic; + private List mUidTraffic; + + /** @hide */ + @IntDef(prefix = { "BT_STACK_STATE_" }, value = { + BT_STACK_STATE_INVALID, + BT_STACK_STATE_STATE_ACTIVE, + BT_STACK_STATE_STATE_SCANNING, + BT_STACK_STATE_STATE_IDLE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BluetoothStackState {} public static final int BT_STACK_STATE_INVALID = 0; public static final int BT_STACK_STATE_STATE_ACTIVE = 1; public static final int BT_STACK_STATE_STATE_SCANNING = 2; public static final int BT_STACK_STATE_STATE_IDLE = 3; + /** @hide */ public BluetoothActivityEnergyInfo(long timestamp, int stackState, long txTime, long rxTime, long idleTime, long energyUsed) { mTimestamp = timestamp; @@ -52,17 +70,18 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { mControllerEnergyUsed = energyUsed; } - @SuppressWarnings("unchecked") - BluetoothActivityEnergyInfo(Parcel in) { + /** @hide */ + private BluetoothActivityEnergyInfo(Parcel in) { mTimestamp = in.readLong(); mBluetoothStackState = in.readInt(); mControllerTxTimeMs = in.readLong(); mControllerRxTimeMs = in.readLong(); mControllerIdleTimeMs = in.readLong(); mControllerEnergyUsed = in.readLong(); - mUidTraffic = in.createTypedArray(UidTraffic.CREATOR); + mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR); } + /** @hide */ @Override public String toString() { return "BluetoothActivityEnergyInfo{" @@ -72,11 +91,11 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { + " mControllerRxTimeMs=" + mControllerRxTimeMs + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + " mControllerEnergyUsed=" + mControllerEnergyUsed - + " mUidTraffic=" + Arrays.toString(mUidTraffic) + + " mUidTraffic=" + mUidTraffic + " }"; } - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { return new BluetoothActivityEnergyInfo(in); @@ -87,9 +106,8 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { } }; - + /** @hide */ @Override - @SuppressWarnings("unchecked") public void writeToParcel(Parcel out, int flags) { out.writeLong(mTimestamp); out.writeInt(mBluetoothStackState); @@ -97,17 +115,21 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { out.writeLong(mControllerRxTimeMs); out.writeLong(mControllerIdleTimeMs); out.writeLong(mControllerEnergyUsed); - out.writeTypedArray(mUidTraffic, flags); + out.writeTypedList(mUidTraffic); } + /** @hide */ @Override public int describeContents() { return 0; } /** - * @return bt stack reported state + * Get the Bluetooth stack state associated with the energy info. + * + * @return one of {@link #BluetoothStackState} states */ + @BluetoothStackState public int getBluetoothStackState() { return mBluetoothStackState; } @@ -134,7 +156,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { } /** - * product of current(mA), voltage(V) and time(ms) + * Get the product of current (mA), voltage (V), and time (ms). * * @return energy used */ @@ -143,22 +165,31 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { } /** - * @return timestamp(real time elapsed in milliseconds since boot) of record creation. + * @return timestamp (real time elapsed in milliseconds since boot) of record creation */ public long getTimeStamp() { return mTimestamp; } - public UidTraffic[] getUidTraffic() { + /** + * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}. + * + * @return current {@link List} of {@link android.bluetooth.UidTraffic} + */ + public @NonNull List getUidTraffic() { + if (mUidTraffic == null) { + return Collections.emptyList(); + } return mUidTraffic; } - public void setUidTraffic(UidTraffic[] traffic) { + /** @hide */ + public void setUidTraffic(List traffic) { mUidTraffic = traffic; } /** - * @return if the record is valid + * @return true if the record is valid */ public boolean isValid() { return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0) diff --git a/framework/java/android/bluetooth/UidTraffic.java b/framework/java/android/bluetooth/UidTraffic.java index 2ee786a5909..9982fa6121e 100644 --- a/framework/java/android/bluetooth/UidTraffic.java +++ b/framework/java/android/bluetooth/UidTraffic.java @@ -15,6 +15,7 @@ */ package android.bluetooth; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -23,27 +24,27 @@ import android.os.Parcelable; * * @hide */ -public class UidTraffic implements Cloneable, Parcelable { +@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) +public final class UidTraffic implements Cloneable, Parcelable { private final int mAppUid; private long mRxBytes; private long mTxBytes; - public UidTraffic(int appUid) { - mAppUid = appUid; - } - + /** @hide */ public UidTraffic(int appUid, long rx, long tx) { mAppUid = appUid; mRxBytes = rx; mTxBytes = tx; } - UidTraffic(Parcel in) { + /** @hide */ + private UidTraffic(Parcel in) { mAppUid = in.readInt(); mRxBytes = in.readLong(); mTxBytes = in.readLong(); } + /** @hide */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mAppUid); @@ -51,44 +52,60 @@ public class UidTraffic implements Cloneable, Parcelable { dest.writeLong(mTxBytes); } + /** @hide */ public void setRxBytes(long bytes) { mRxBytes = bytes; } + /** @hide */ public void setTxBytes(long bytes) { mTxBytes = bytes; } + /** @hide */ public void addRxBytes(long bytes) { mRxBytes += bytes; } + /** @hide */ public void addTxBytes(long bytes) { mTxBytes += bytes; } + /** + * @return corresponding app Uid + */ public int getUid() { return mAppUid; } + /** + * @return rx bytes count + */ public long getRxBytes() { return mRxBytes; } + /** + * @return tx bytes count + */ public long getTxBytes() { return mTxBytes; } + /** @hide */ @Override public int describeContents() { return 0; } + /** @hide */ @Override public UidTraffic clone() { return new UidTraffic(mAppUid, mRxBytes, mTxBytes); } + /** @hide */ @Override public String toString() { return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes=" -- GitLab From 1832d6013dfa579e34142f1a18044f942cd7bcbd Mon Sep 17 00:00:00 2001 From: Harshad Ulhas Dhabu Date: Thu, 18 Jun 2020 22:45:18 -0700 Subject: [PATCH 1369/1408] Rectify comment in getCurrentAgEvents Bug: 143946839 Bug: 199827901 Test: IOP and BCST Tag: #refactor Change-Id: I1c91d941f5383e8682e2b664120ffe6de4dfdef4 (cherry picked from commit 322c15bdaaa6ec44bc4cf6ea723b403b0e8ec1a8) --- framework/java/android/bluetooth/BluetoothHeadsetClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index e5b2a1e23cc..d78b0fcfe0e 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -758,7 +758,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG indicators; null if device is not in CONNECTED state */ public Bundle getCurrentAgEvents(BluetoothDevice device) { - if (DBG) log("getCurrentCalls()"); + if (DBG) log("getCurrentAgEvents()"); final IBluetoothHeadsetClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { -- GitLab From 3809d97ada2e5f25eb363d2a6f82eaec88b4e699 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Mon, 25 Oct 2021 10:11:00 +0000 Subject: [PATCH 1370/1408] Replaced BitUtils#maskedEquals hidden API call Added maskedEquals method to BluetoothLeUtils to prevent hidden API usage. Tag: #feature bug: 200200870 Test: Manual Change-Id: I7517cfef0b9e058e36d8d325f4a5ec653b1f804b --- .../android/bluetooth/le/BluetoothLeUtils.java | 18 ++++++++++++++++++ .../java/android/bluetooth/le/ScanFilter.java | 6 ++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java index 6381f557c1b..ed50b09597b 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.UUID; /** * Helper class for Bluetooth LE utils. @@ -137,4 +138,21 @@ public class BluetoothLeUtils { } } + /** + * Compares two UUIDs with a UUID mask. + * + * @param data first {@link #UUID} to compare. + * @param uuid second {@link #UUID} to compare. + * @param mask mask {@link #UUID}. + * @return true if both UUIDs are equals when masked, false otherwise. + */ + static boolean maskedEquals(UUID data, UUID uuid, UUID mask) { + if (mask == null) { + return Objects.equals(data, uuid); + } + return (data.getLeastSignificantBits() & mask.getLeastSignificantBits()) + == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) + && (data.getMostSignificantBits() & mask.getMostSignificantBits()) + == (uuid.getMostSignificantBits() & mask.getMostSignificantBits()); + } } diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java index 8ff018121ab..b059193ae03 100644 --- a/framework/java/android/bluetooth/le/ScanFilter.java +++ b/framework/java/android/bluetooth/le/ScanFilter.java @@ -28,8 +28,6 @@ import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; -import com.android.internal.util.BitUtils; - import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -448,7 +446,7 @@ public final class ScanFilter implements Parcelable { // Check if the uuid pattern matches the particular service uuid. private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - return BitUtils.maskedEquals(data, uuid, mask); + return BluetoothLeUtils.maskedEquals(data, uuid, mask); } /** @@ -478,7 +476,7 @@ public final class ScanFilter implements Parcelable { // Check if the solicitation uuid pattern matches the particular service solicitation uuid. private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid, UUID solicitationUuidMask, UUID data) { - return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask); + return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask); } // Check whether the data pattern matches the parsed data. -- GitLab From cc3d58735326ef2199b0ec6f7e1f25dbe38495a7 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Mon, 25 Oct 2021 21:38:16 +0000 Subject: [PATCH 1371/1408] Revert "Removed BluetoothDevice#prepareToEnterProcess" This reverts commit 39639385f90678e97aa6848ae3b43de6bfb3b49e. Reason for revert: Introducing regression for gms core Test: None Change-Id: I644b10c1869c12e1622300de43bfbdb57fb583d8 --- .../java/android/bluetooth/BluetoothDevice.java | 5 +++++ .../java/android/bluetooth/BluetoothManager.java | 13 +++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 9caeb297ace..c71fcc637cb 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1186,6 +1186,11 @@ public final class BluetoothDevice implements Parcelable, Attributable { mAttributionSource = attributionSource; } + /** {@hide} */ + public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) { + setAttributionSource(attributionSource); + } + @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index b5df4db2460..20152f3d247 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -62,15 +62,15 @@ public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = false; - private static AttributionSource sAttributionSource = null; + private final AttributionSource mAttributionSource; private final BluetoothAdapter mAdapter; /** * @hide */ public BluetoothManager(Context context) { - sAttributionSource = resolveAttributionSource(context); - mAdapter = BluetoothAdapter.createAdapter(sAttributionSource); + mAttributionSource = resolveAttributionSource(context); + mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); } /** {@hide} */ @@ -79,9 +79,6 @@ public final class BluetoothManager { if (context != null) { res = context.getAttributionSource(); } - else if (sAttributionSource != null) { - return sAttributionSource; - } if (res == null) { res = ActivityThread.currentAttributionSource(); } @@ -201,8 +198,8 @@ public final class BluetoothManager { IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; devices = Attributable.setAttributionSource( - iGatt.getDevicesMatchingConnectionStates(states, sAttributionSource), - sAttributionSource); + iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } -- GitLab From 28b7b6464c541f14efb677bb3ec6e40afa07e763 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Tue, 26 Oct 2021 15:52:10 +0000 Subject: [PATCH 1372/1408] Renamed getTimeStamp to getTimestampMillis and added docs. Tag: #feature Test: Manual Bug: 204179568 Change-Id: Ibea9f98eb25447e7ca3a07310615d14caf2c8aa1 --- .../java/android/bluetooth/BluetoothActivityEnergyInfo.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java index f371c6d7822..c17a7b4b3df 100644 --- a/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ b/framework/java/android/bluetooth/BluetoothActivityEnergyInfo.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -167,7 +168,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { /** * @return timestamp (real time elapsed in milliseconds since boot) of record creation */ - public long getTimeStamp() { + public @ElapsedRealtimeLong long getTimestampMillis() { return mTimestamp; } @@ -189,7 +190,7 @@ public final class BluetoothActivityEnergyInfo implements Parcelable { } /** - * @return true if the record is valid + * @return true if the record Tx time, Rx time, and Idle time are more than 0. */ public boolean isValid() { return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0) -- GitLab From f3fc3166c04cbe693abdef14ef3088550f7c4d37 Mon Sep 17 00:00:00 2001 From: Patty Date: Mon, 18 Oct 2021 19:44:36 +0800 Subject: [PATCH 1373/1408] Expose isCISCentralSupported() and isLePeriodicAdvertisingSyncTransferSenderSupported() API Tag: #feature Bug: 200749925 Bug: 150670922 Test: Compile Change-Id: I45c674dcc8719d209f0e790aa9716bbbf0052fce --- .../android/bluetooth/BluetoothAdapter.java | 60 +++++++++++++++++++ .../bluetooth/BluetoothStatusCodes.java | 5 ++ 2 files changed, 65 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 06ce0530d47..dac8ffe5e00 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2272,6 +2272,66 @@ public final class BluetoothAdapter { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, + BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED, + }) + public @interface LeFeatureReturnValues {} + + /** + * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Connected Isochronous Stream Central + * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if + * the feature is not supported or an error code. + * + * @return whether the chipset supports the LE Connected Isochronous Stream Central feature + */ + @RequiresNoPermission + public @LeFeatureReturnValues int isCisCentralSupported() { + if (!getLeAccess()) { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.isCisCentralSupported(); + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothStatusCodes.ERROR_UNKNOWN; + } + + /** + * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender + * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the + * feature is not supported or an error code + * + * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature + */ + @RequiresNoPermission + public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() { + if (!getLeAccess()) { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.isLePeriodicAdvertisingSyncTransferSenderSupported(); + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothStatusCodes.ERROR_UNKNOWN; + } + /** * Return the maximum LE advertising data length in bytes, * if LE Extended Advertising feature is supported, 0 otherwise. diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java index 3e46c498e33..63e84eddc73 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -78,6 +78,11 @@ public final class BluetoothStatusCodes { */ public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7; + /** + * Error code indicating that the feature is not supported. + */ + public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; + /** * If another application has already requested {@link OobData} then another fetch will be * disallowed until the callback is removed. -- GitLab From f95c619d25acce658c84861dbbe8e0bf35b6e1d7 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 13 Oct 2021 10:56:50 -0700 Subject: [PATCH 1374/1408] Deprecate GATT callbacks and methods that were not memory safe and replace with memory safe versions. Tag: #feature Bug: 195157393 Test: Manual CTS-Coverage-Bug: 205190062 Merged-In: I5ae604ec20febcf646bfe6a8f866b218448349c1 Change-Id: I5ae604ec20febcf646bfe6a8f866b218448349c1 --- .../java/android/bluetooth/BluetoothGatt.java | 215 +++++++++++++----- .../bluetooth/BluetoothGattCallback.java | 84 +++++-- .../BluetoothGattCharacteristic.java | 35 ++- .../bluetooth/BluetoothGattDescriptor.java | 7 + .../bluetooth/BluetoothStatusCodes.java | 24 +- 5 files changed, 285 insertions(+), 80 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index ca898bd6a5e..4e7c01ad2db 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -29,6 +31,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -139,27 +143,6 @@ public final class BluetoothGatt implements BluetoothProfile { /** Connection parameter update - Request low power, reduced data rate connection parameters. */ public static final int CONNECTION_PRIORITY_LOW_POWER = 2; - /** - * A GATT writeCharacteristic request is started successfully. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_SUCCESS = 0; - - /** - * A GATT writeCharacteristic request failed to start. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_FAIL = 1; - - /** - * A GATT writeCharacteristic request is issued to a busy remote device. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_BUSY = 2; - /** * No authentication required. * @@ -429,6 +412,9 @@ public final class BluetoothGatt implements BluetoothProfile { final BluetoothGattCallback callback = mCallback; if (callback != null) { if (status == 0) characteristic.setValue(value); + callback.onCharacteristicRead(BluetoothGatt.this, characteristic, + value, status); + // Keep calling deprecated callback to maintain app compatibility callback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } @@ -443,7 +429,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override @SuppressLint("AndroidFrameworkRequiresPermission") - public void onCharacteristicWrite(String address, int status, int handle) { + public void onCharacteristicWrite(String address, int status, int handle, + byte[] value) { if (VDBG) { Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " handle=" + handle + " Status=" + status); @@ -467,12 +454,13 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - int requestStatus = GATT_WRITE_REQUEST_FAIL; + int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { requestStatus = mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), authReq, - characteristic.getValue(), mAttributionSource); - if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + value, mAttributionSource); + if (requestStatus + != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { break; } try { @@ -488,7 +476,6 @@ public final class BluetoothGatt implements BluetoothProfile { } mAuthRetryState = AUTH_RETRY_STATE_IDLE; - runOrQueueCallback(new Runnable() { @Override public void run() { @@ -524,6 +511,9 @@ public final class BluetoothGatt implements BluetoothProfile { final BluetoothGattCallback callback = mCallback; if (callback != null) { characteristic.setValue(value); + callback.onCharacteristicChanged(BluetoothGatt.this, + characteristic, value); + // Keep calling deprecated callback to maintain app compatibility callback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } @@ -578,6 +568,9 @@ public final class BluetoothGatt implements BluetoothProfile { final BluetoothGattCallback callback = mCallback; if (callback != null) { if (status == 0) descriptor.setValue(value); + callback.onDescriptorRead(BluetoothGatt.this, descriptor, status, + value); + // Keep calling deprecated callback to maintain app compatibility callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } @@ -590,7 +583,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override @SuppressLint("AndroidFrameworkRequiresPermission") - public void onDescriptorWrite(String address, int status, int handle) { + public void onDescriptorWrite(String address, int status, int handle, + byte[] value) { if (VDBG) { Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); @@ -614,7 +608,7 @@ public final class BluetoothGatt implements BluetoothProfile { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, - authReq, descriptor.getValue(), mAttributionSource); + authReq, value, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -1194,8 +1188,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the requested characteristic from the associated remote device. * *

    This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} - * callback. + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} callback. * * @param characteristic Characteristic to read from the remote device * @return true, if the read operation was initiated successfully @@ -1240,8 +1234,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the characteristic using its UUID from the associated remote device. * *

    This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} - * callback. + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} callback. * * @param uuid UUID of characteristic to read from the remote device * @return true, if the read operation was initiated successfully @@ -1284,40 +1278,94 @@ public final class BluetoothGatt implements BluetoothProfile { * * @param characteristic Characteristic to write on the remote device * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if characteristic or its value are null + * + * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], + * int)} as this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { - if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 - && (characteristic.getProperties() - & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + try { + return writeCharacteristic(characteristic, characteristic.getValue(), + characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS; + } catch (Exception e) { return false; } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, + BluetoothStatusCodes.ERROR_UNKNOWN + }) + public @interface WriteOperationReturnValues{} + /** + * Writes a given characteristic and its values to the associated remote device. + * + *

    Once the write operation has been completed, the + * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * reporting the result of the operation. + * + * @param characteristic Characteristic to write on the remote device + * @return whether the characteristic was successfully written to + * @throws IllegalArgumentException if characteristic or value are null + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @WriteOperationReturnValues + public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic, + @NonNull byte[] value, int writeType) { + if (characteristic == null) { + throw new IllegalArgumentException("characteristic must not be null"); + } + if (value == null) { + throw new IllegalArgumentException("value must not be null"); + } if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); - if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false; + if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 + && (characteristic.getProperties() + & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED; + } + if (mService == null || mClientIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } BluetoothDevice device = service.getDevice(); - if (device == null) return false; + if (device == null) { + throw new IllegalArgumentException("Service must have a non-null device"); + } synchronized (mDeviceBusyLock) { if (mDeviceBusy) { - return false; + return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; } mDeviceBusy = true; } - int requestStatus = GATT_WRITE_REQUEST_FAIL; + int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; try { for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), characteristic.getWriteType(), - AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); - if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value, + mAttributionSource); + if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { break; } try { @@ -1330,10 +1378,10 @@ public final class BluetoothGatt implements BluetoothProfile { synchronized (mDeviceBusyLock) { mDeviceBusy = false; } - return false; + throw e.rethrowFromSystemServer(); } - return requestStatus == GATT_WRITE_REQUEST_SUCCESS; + return requestStatus; } /** @@ -1384,45 +1432,86 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Write the value of a given descriptor to the associated remote device. * - *

    A {@link BluetoothGattCallback#onDescriptorWrite} callback is - * triggered to report the result of the write operation. + *

    A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the + * result of the write operation. * * @param descriptor Descriptor to write to the associated remote device * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if descriptor or its value are null + * + * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as + * this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { + try { + return writeDescriptor(descriptor, descriptor.getValue()) + == BluetoothStatusCodes.SUCCESS; + } catch (Exception e) { + return false; + } + } + + /** + * Write the value of a given descriptor to the associated remote device. + * + *

    A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the + * result of the write operation. + * + * @param descriptor Descriptor to write to the associated remote device + * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if descriptor or value are null + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @WriteOperationReturnValues + public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor, + @NonNull byte[] value) { + if (descriptor == null) { + throw new IllegalArgumentException("descriptor must not be null"); + } + if (value == null) { + throw new IllegalArgumentException("value must not be null"); + } if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false; + if (mService == null || mClientIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); - if (characteristic == null) return false; + if (characteristic == null) { + throw new IllegalArgumentException("Descriptor must have a non-null characteristic"); + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } BluetoothDevice device = service.getDevice(); - if (device == null) return false; + if (device == null) { + throw new IllegalArgumentException("Service must have a non-null device"); + } synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; + if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; mDeviceBusy = true; } try { - mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource); + return mService.writeDescriptor(mClientIf, device.getAddress(), + descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); synchronized (mDeviceBusyLock) { mDeviceBusy = false; } - return false; + e.rethrowFromSystemServer(); } - - return true; + return BluetoothStatusCodes.ERROR_UNKNOWN; } /** @@ -1431,9 +1520,9 @@ public final class BluetoothGatt implements BluetoothProfile { *

    Once a reliable write transaction has been initiated, all calls * to {@link #writeCharacteristic} are sent to the remote device for * verification and queued up for atomic execution. The application will - * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback - * in response to every {@link #writeCharacteristic} call and is responsible - * for verifying if the value has been transmitted accurately. + * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every + * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is + * responsible for verifying if the value has been transmitted accurately. * *

    After all characteristics have been queued up and verified, * {@link #executeReliableWrite} will execute all writes. If a characteristic @@ -1530,9 +1619,9 @@ public final class BluetoothGatt implements BluetoothProfile { * Enable or disable notifications/indications for a given characteristic. * *

    Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be - * triggered if the remote device indicates that the given characteristic - * has changed. + * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, + * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device + * indicates that the given characteristic has changed. * * @param characteristic The characteristic for which to enable notifications * @param enable Set to true to enable notifications/indications diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java index 1c40cff076f..d0a5a1e729f 100644 --- a/framework/java/android/bluetooth/BluetoothGattCallback.java +++ b/framework/java/android/bluetooth/BluetoothGattCallback.java @@ -80,15 +80,33 @@ public abstract class BluetoothGattCallback { /** * Callback reporting the result of a characteristic read operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} + * @param gatt GATT client invoked + * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} * @param characteristic Characteristic that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully. + * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} as it is memory safe */ + @Deprecated public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } + /** + * Callback reporting the result of a characteristic read operation. + * + * @param gatt GATT client invoked + * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} + * @param characteristic Characteristic that was read from the associated remote device. + * @param value the value of the characteristic + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully. + */ + public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull + BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) { + } + /** * Callback indicating the result of a characteristic write operation. * @@ -98,10 +116,13 @@ public abstract class BluetoothGattCallback { * value to the desired value to be written. If the values don't match, * the application must abort the reliable write transaction. * - * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} + * @param gatt GATT client that invoked + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, + * byte[], int)} * @param characteristic Characteristic that was written to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if + * the + * operation succeeds. */ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { @@ -110,33 +131,68 @@ public abstract class BluetoothGattCallback { /** * Callback triggered as a result of a remote characteristic notification. * - * @param gatt GATT client the characteristic is associated with + * @param gatt GATT client the characteristic is associated with * @param characteristic Characteristic that has been updated as a result of a remote - * notification event. + * notification event. + * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, + * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic + * value at the time of notification. */ + @Deprecated public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } + /** + * Callback triggered as a result of a remote characteristic notification. Note that the value + * within the characteristic object may have changed since receiving the remote characteristic + * notification, so check the parameter value for the value at the time of notification. + * + * @param gatt GATT client the characteristic is associated with + * @param characteristic Characteristic that has been updated as a result of a remote + * notification event. + * @param value notified characteristic value + */ + public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, + @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) { + } + /** * Callback reporting the result of a descriptor read operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} * @param descriptor Descriptor that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully + * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt, + * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor + * value at the time it was read. */ + @Deprecated public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { } + /** + * Callback reporting the result of a descriptor read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param descriptor Descriptor that was read from the associated remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully + * @param value the descriptor value at the time of the read operation + */ + public void onDescriptorRead(@NonNull BluetoothGatt gatt, + @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) { + } + /** * Callback indicating the result of a descriptor write operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} * @param descriptor Descriptor that was writte to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { diff --git a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java index 8a7d4baf5ad..c5e986e895b 100644 --- a/framework/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/framework/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -451,8 +451,8 @@ public class BluetoothGattCharacteristic implements Parcelable { * Set the write type for this characteristic * *

    Setting the write type of a characteristic determines how the - * {@link BluetoothGatt#writeCharacteristic} function write this - * characteristic. + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function + * write this characteristic. * * @param writeType The write type to for this characteristic. Can be one of: {@link * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}. @@ -504,7 +504,10 @@ public class BluetoothGattCharacteristic implements Parcelable { * operation or if a characteristic update notification has been received. * * @return Cached value of the characteristic + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead */ + @Deprecated public byte[] getValue() { return mValue; } @@ -521,7 +524,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType The format type used to interpret the characteristic value. * @param offset Offset at which the integer value can be found. * @return Cached value of the characteristic or null of offset exceeds value size. + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public Integer getIntValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; @@ -558,7 +565,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param offset Offset at which the float value can be found. * @return Cached value of the characteristic at a given offset or null if the requested offset * exceeds the value size. + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public Float getFloatValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; @@ -580,7 +591,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @param offset Offset at which the string value can be found. * @return Cached value of the characteristic + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public String getStringValue(int offset) { if (mValue == null || offset > mValue.length) return null; byte[] strBytes = new byte[mValue.length - offset]; @@ -599,7 +614,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param value New value for this characteristic * @return true if the locally stored value has been set, false if the requested value could not * be stored locally. + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(byte[] value) { mValue = value; return true; @@ -613,7 +632,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType Integer format type used to transform the value parameter * @param offset Offset at which the value should be placed * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(int value, int formatType, int offset) { int len = offset + getTypeLen(formatType); if (mValue == null) mValue = new byte[len]; @@ -660,7 +683,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType Float format type used to transform the value parameter * @param offset Offset at which the value should be placed * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(int mantissa, int exponent, int formatType, int offset) { int len = offset + getTypeLen(formatType); if (mValue == null) mValue = new byte[len]; @@ -697,7 +724,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @param value New value for this characteristic * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(String value) { mValue = value.getBytes(); return true; diff --git a/framework/java/android/bluetooth/BluetoothGattDescriptor.java b/framework/java/android/bluetooth/BluetoothGattDescriptor.java index ed5ea087302..a35d5b99fd7 100644 --- a/framework/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/framework/java/android/bluetooth/BluetoothGattDescriptor.java @@ -260,7 +260,10 @@ public class BluetoothGattDescriptor implements Parcelable { * operation. * * @return Cached value of the descriptor + * + * @deprecated Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead */ + @Deprecated public byte[] getValue() { return mValue; } @@ -276,7 +279,11 @@ public class BluetoothGattDescriptor implements Parcelable { * @param value New value for this descriptor * @return true if the locally stored value has been set, false if the requested value could not * be stored locally. + * + * @deprecated Pass the descriptor value directly into + * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} */ + @Deprecated public boolean setValue(byte[] value) { mValue = value; return true; diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java index 63e84eddc73..ca01784efd8 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -78,10 +78,32 @@ public final class BluetoothStatusCodes { */ public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7; + /** + * Error code indicating that the caller does not have the + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission + */ + public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; + + /** + * Error code indicating that the profile service is not bound. You can bind a profile service + * by calling {@link BluetoothAdapter#getProfileProxy} + */ + public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; + /** * Error code indicating that the feature is not supported. */ - public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; + public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; + + /** + * A GATT writeCharacteristic request is not permitted on the remote device. + */ + public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; + + /** + * A GATT writeCharacteristic request is issued to a busy remote device. + */ + public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; /** * If another application has already requested {@link OobData} then another fetch will be -- GitLab From 99d1a066c74f1f782948612ff42d6e6dfbf1239c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Rymanowski?= Date: Fri, 12 Nov 2021 11:55:00 +0000 Subject: [PATCH 1375/1408] BluetoothAdapter: Extend getActiveDevice with LeAudio Bug: 150670922 Sponsor: jpawlowski@ Test: NA Change-Id: I82662de5fe7f86b28dee1edff38761cf48343c5a --- framework/java/android/bluetooth/BluetoothAdapter.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index dac8ffe5e00..cf00cbd584a 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2022,6 +2022,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}, * {@link BluetoothProfile#HEARING_AID} + * {@link BluetoothProfile#LE_AUDIO} * @return A list of active bluetooth devices * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} * @hide @@ -2034,12 +2035,14 @@ public final class BluetoothAdapter { public @NonNull List getActiveDevices(@ActiveDeviceProfile int profile) { if (profile != BluetoothProfile.HEADSET && profile != BluetoothProfile.A2DP - && profile != BluetoothProfile.HEARING_AID) { + && profile != BluetoothProfile.HEARING_AID + && profile != BluetoothProfile.LE_AUDIO) { Log.e(TAG, "Invalid profile param value in getActiveDevices"); throw new IllegalArgumentException("Profiles must be one of " + "BluetoothProfile.A2DP, " + "BluetoothProfile.HEARING_AID, or" - + "BluetoothProfile.HEARING_AID"); + + "BluetoothProfile.HEARING_AID" + + "BluetoothProfile.LE_AUDIO"); } try { mServiceLock.readLock().lock(); -- GitLab From 89375419e2b6392e1c39450f7979de1919e76033 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Tue, 16 Nov 2021 12:08:47 -0800 Subject: [PATCH 1376/1408] Create memory safe overload of BluetoothGattServer#notifyCharacteristicChanged Tag: #feature Bug: 195157393 Test: Manual CTS-Coverage-Bug: 205190062 Change-Id: I27a59a1dac73f10f5a25c0f40d399a265544e577 --- .../bluetooth/BluetoothGattServer.java | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 3e799defa5e..08e0178403f 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -26,6 +28,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -709,33 +713,85 @@ public final class BluetoothGattServer implements BluetoothProfile { * notification * @return true, if the notification has been triggered successfully * @throws IllegalArgumentException + * + * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice, + * BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { + return notifyCharacteristicChanged(device, characteristic, confirm, + characteristic.getValue()) == BluetoothStatusCodes.SUCCESS; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, + BluetoothStatusCodes.ERROR_UNKNOWN + }) + public @interface NotifyCharacteristicReturnValues{} + + /** + * Send a notification or indication that a local characteristic has been + * updated. + * + *

    A notification or indication is sent to the remote device to signal + * that the characteristic has been updated. This function should be invoked + * for every client that requests notifications/indications by writing + * to the "Client Configuration" descriptor for the given characteristic. + * + * @param device the remote device to receive the notification/indication + * @param characteristic the local characteristic that has been updated + * @param confirm {@code true} to request confirmation from the client (indication) or + * {@code false} to send a notification + * @param value the characteristic value + * @return whether the notification has been triggered successfully + * @throws IllegalArgumentException if the characteristic value or service is null + */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @NotifyCharacteristicReturnValues + public int notifyCharacteristicChanged(@NonNull BluetoothDevice device, + @NonNull BluetoothGattCharacteristic characteristic, boolean confirm, + @NonNull byte[] value) { if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); - if (mService == null || mServerIf == 0) return false; + if (mService == null || mServerIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + if (characteristic == null) { + throw new IllegalArgumentException("characteristic must not be null"); + } + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; - - if (characteristic.getValue() == null) { - throw new IllegalArgumentException("Chracteristic value is empty. Use " - + "BluetoothGattCharacteristic#setvalue to update"); + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } + if (value == null) { + throw new IllegalArgumentException("Characteristic value must not be null"); } try { - mService.sendNotification(mServerIf, device.getAddress(), + return mService.sendNotification(mServerIf, device.getAddress(), characteristic.getInstanceId(), confirm, - characteristic.getValue(), mAttributionSource); + value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); - return false; + throw e.rethrowFromSystemServer(); } - - return true; } /** -- GitLab From d0e4f63440c9055300544f3eb958af94b0f6d9bf Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 8 Oct 2020 20:53:55 +0200 Subject: [PATCH 1377/1408] Bluetooth: make it possible to advertise Transport Discovery Data Bug: 159363165 Tag: #feature Test: called the API in test app, verified advertisement content Change-Id: I92fe0671717bda2de1be3ff022713133ad681ab6 --- .../android/bluetooth/le/AdvertiseData.java | 58 +++++- .../bluetooth/le/BluetoothLeAdvertiser.java | 3 + .../android/bluetooth/le/TransportBlock.java | 155 ++++++++++++++++ .../bluetooth/le/TransportDiscoveryData.java | 168 ++++++++++++++++++ 4 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 framework/java/android/bluetooth/le/TransportBlock.java create mode 100644 framework/java/android/bluetooth/le/TransportDiscoveryData.java diff --git a/framework/java/android/bluetooth/le/AdvertiseData.java b/framework/java/android/bluetooth/le/AdvertiseData.java index cec658049ca..fdf62ec3a64 100644 --- a/framework/java/android/bluetooth/le/AdvertiseData.java +++ b/framework/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.util.ArrayMap; import android.util.SparseArray; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,6 +48,9 @@ public final class AdvertiseData implements Parcelable { @NonNull private final List mServiceSolicitationUuids; + @Nullable + private final List mTransportDiscoveryData; + private final SparseArray mManufacturerSpecificData; private final Map mServiceData; private final boolean mIncludeTxPowerLevel; @@ -54,12 +58,14 @@ public final class AdvertiseData implements Parcelable { private AdvertiseData(List serviceUuids, List serviceSolicitationUuids, + List transportDiscoveryData, SparseArray manufacturerData, Map serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; mServiceSolicitationUuids = serviceSolicitationUuids; + mTransportDiscoveryData = transportDiscoveryData; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; @@ -82,6 +88,17 @@ public final class AdvertiseData implements Parcelable { return mServiceSolicitationUuids; } + /** + * Returns a list of {@link TransportDiscoveryData} within the advertisement. + */ + @NonNull + public List getTransportDiscoveryData() { + if (mTransportDiscoveryData == null) { + return Collections.emptyList(); + } + return mTransportDiscoveryData; + } + /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. @@ -116,8 +133,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, - mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, + mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -134,6 +151,7 @@ public final class AdvertiseData implements Parcelable { AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) + && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -144,7 +162,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" - + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + + mServiceSolicitationUuids + ", mTransportDiscoveryData=" + + mTransportDiscoveryData + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -162,6 +181,8 @@ public final class AdvertiseData implements Parcelable { dest.writeTypedArray(mServiceSolicitationUuids.toArray( new ParcelUuid[mServiceSolicitationUuids.size()]), flags); + dest.writeTypedList(mTransportDiscoveryData); + // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { @@ -197,6 +218,12 @@ public final class AdvertiseData implements Parcelable { builder.addServiceSolicitationUuid(uuid); } + List transportDiscoveryData = + in.createTypedArrayList(TransportDiscoveryData.CREATOR); + for (TransportDiscoveryData tdd : transportDiscoveryData) { + builder.addTransportDiscoveryData(tdd); + } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); @@ -223,6 +250,9 @@ public final class AdvertiseData implements Parcelable { private List mServiceUuids = new ArrayList(); @NonNull private List mServiceSolicitationUuids = new ArrayList(); + @Nullable + private List mTransportDiscoveryData = + new ArrayList(); private SparseArray mManufacturerSpecificData = new SparseArray(); private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; @@ -256,6 +286,7 @@ public final class AdvertiseData implements Parcelable { mServiceSolicitationUuids.add(serviceSolicitationUuid); return this; } + /** * Add service data to advertise data. * @@ -273,6 +304,23 @@ public final class AdvertiseData implements Parcelable { return this; } + /** + * Add Transport Discovery Data to advertise data. + * + * @param transportDiscoveryData Transport Discovery Data, consisting of one or more + * Transport Blocks. Transport Discovery Data AD Type Code is already included. + * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty + */ + @NonNull + public Builder addTransportDiscoveryData( + @NonNull TransportDiscoveryData transportDiscoveryData) { + if (transportDiscoveryData == null) { + throw new IllegalArgumentException("transportDiscoveryData is null"); + } + mTransportDiscoveryData.add(transportDiscoveryData); + return this; + } + /** * Add manufacturer specific data. *

    @@ -319,8 +367,8 @@ public final class AdvertiseData implements Parcelable { */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, - mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, - mIncludeDeviceName); + mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, + mIncludeTxPowerLevel, mIncludeDeviceName); } } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 58029745ab9..b9f8a57a75e 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -567,6 +567,9 @@ public final class BluetoothLeAdvertiser { + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } + for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { + size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); + } for (ParcelUuid uuid : data.getServiceData().keySet()) { int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; size += OVERHEAD_BYTES_PER_FIELD + uuidLen diff --git a/framework/java/android/bluetooth/le/TransportBlock.java b/framework/java/android/bluetooth/le/TransportBlock.java new file mode 100644 index 00000000000..b388beda6b3 --- /dev/null +++ b/framework/java/android/bluetooth/le/TransportBlock.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * Wrapper for Transport Discovery Data Transport Blocks. + * This class represents a Transport Block from a Transport Discovery Data. + * + * @see TransportDiscoveryData + * @see AdvertiseData + */ +public final class TransportBlock implements Parcelable { + private static final String TAG = "TransportBlock"; + private final int mOrgId; + private final int mTdsFlags; + private final int mTransportDataLength; + private final byte[] mTransportData; + + /** + * Creates an instance of TransportBlock from raw data. + * + * @param orgId the Organization ID + * @param tdsFlags the TDS flags + * @param transportDataLength the total length of the Transport Data + * @param transportData the Transport Data + */ + public TransportBlock(int orgId, int tdsFlags, int transportDataLength, + @Nullable byte[] transportData) { + mOrgId = orgId; + mTdsFlags = tdsFlags; + mTransportDataLength = transportDataLength; + mTransportData = transportData; + } + + private TransportBlock(Parcel in) { + mOrgId = in.readInt(); + mTdsFlags = in.readInt(); + mTransportDataLength = in.readInt(); + mTransportData = new byte[mTransportDataLength]; + in.readByteArray(mTransportData); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mOrgId); + dest.writeInt(mTdsFlags); + dest.writeInt(mTransportDataLength); + dest.writeByteArray(mTransportData); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator CREATOR = new Creator() { + @Override + public TransportBlock createFromParcel(Parcel in) { + return new TransportBlock(in); + } + + @Override + public TransportBlock[] newArray(int size) { + return new TransportBlock[size]; + } + }; + + /** + * Gets the Organization ID of the Transport Block which corresponds to one of the + * the Bluetooth SIG Assigned Numbers. + */ + public int getOrgId() { + return mOrgId; + } + + /** + * Gets the TDS flags of the Transport Block which represents the role of the device and + * information about its state and supported features. + */ + public int getTdsFlags() { + return mTdsFlags; + } + + /** + * Gets the total number of octets in the Transport Data field in this Transport Block. + */ + public int getTransportDataLength() { + return mTransportDataLength; + } + + /** + * Gets the Transport Data of the Transport Block which contains organization-specific data. + */ + @Nullable + public byte[] getTransportData() { + return mTransportData; + } + + /** + * Converts this TransportBlock to byte array + * + * @return byte array representation of this Transport Block or null if the conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mOrgId); + buffer.put((byte) mTdsFlags); + buffer.put((byte) mTransportDataLength); + if (mTransportData != null) { + buffer.put(mTransportData); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportBlock + */ + public int totalBytes() { + // 3 uint8 + byte[] length + int size = 3 + mTransportDataLength; + return size; + } +} diff --git a/framework/java/android/bluetooth/le/TransportDiscoveryData.java b/framework/java/android/bluetooth/le/TransportDiscoveryData.java new file mode 100644 index 00000000000..c8e97f9a823 --- /dev/null +++ b/framework/java/android/bluetooth/le/TransportDiscoveryData.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 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.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper for Transport Discovery Data AD Type. + * This class contains the Transport Discovery Data AD Type Code as well as + * a list of potential Transport Blocks. + * + * @see AdvertiseData + */ +public final class TransportDiscoveryData implements Parcelable { + private static final String TAG = "TransportDiscoveryData"; + private final int mTransportDataType; + private final List mTransportBlocks; + + /** + * Creates a TransportDiscoveryData instance. + * + * @param transportDataType the Transport Discovery Data AD Type + * @param transportBlocks the list of Transport Blocks + */ + public TransportDiscoveryData(int transportDataType, + @NonNull List transportBlocks) { + mTransportDataType = transportDataType; + mTransportBlocks = transportBlocks; + } + + /** + * Creates a TransportDiscoveryData instance from byte arrays. + * + * Uses the transport discovery data bytes and parses them into an usable class. + * + * @param transportDiscoveryData the raw discovery data + */ + public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { + ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); + mTransportBlocks = new ArrayList(); + if (byteBuffer.remaining() > 0) { + mTransportDataType = byteBuffer.get(); + } else { + mTransportDataType = -1; + } + try { + while (byteBuffer.remaining() > 0) { + int orgId = byteBuffer.get(); + int tdsFlags = byteBuffer.get(); + int transportDataLength = byteBuffer.get(); + byte[] transportData = new byte[transportDataLength]; + byteBuffer.get(transportData, 0, transportDataLength); + mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, + transportDataLength, transportData)); + } + } catch (BufferUnderflowException e) { + Log.e(TAG, "Error while parsing data: " + e.toString()); + } + } + + private TransportDiscoveryData(Parcel in) { + mTransportDataType = in.readInt(); + mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mTransportDataType); + dest.writeTypedList(mTransportBlocks); + } + + public static final @NonNull Creator CREATOR = + new Creator() { + @Override + public TransportDiscoveryData createFromParcel(Parcel in) { + return new TransportDiscoveryData(in); + } + + @Override + public TransportDiscoveryData[] newArray(int size) { + return new TransportDiscoveryData[size]; + } + }; + + /** + * Gets the transport data type. + */ + public int getTransportDataType() { + return mTransportDataType; + } + + /** + * @return the list of {@link TransportBlock} in this TransportDiscoveryData + * or an empty list if there are no Transport Blocks + */ + @NonNull + public List getTransportBlocks() { + if (mTransportBlocks == null) { + return Collections.emptyList(); + } + return mTransportBlocks; + } + + /** + * Converts this TransportDiscoveryData to byte array + * + * @return byte array representation of this Transport Discovery Data or null if the + * conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mTransportDataType); + for (TransportBlock transportBlock : getTransportBlocks()) { + buffer.put(transportBlock.toByteArray()); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportDataDiscovery + */ + public int totalBytes() { + int size = 1; // Counting Transport Data Type here. + for (TransportBlock transportBlock : getTransportBlocks()) { + size += transportBlock.totalBytes(); + } + return size; + } +} -- GitLab From 647179ea4ea64a1401a9304b4afa3c07659a2daf Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 18 Nov 2021 15:56:58 +0100 Subject: [PATCH 1378/1408] BT_MAINLINE Delete getControllerActivityEnergyInfo This hidden api is deprecated since 2016. Removing it because it's not used and it's calling 3 hidden apis Bug: 200200870 Tag: #refactor Test: Build Change-Id: Ib84e069dc663def3d6a2d6f64717d76d39b59fb9 --- .../android/bluetooth/BluetoothAdapter.java | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cf00cbd584a..2c875fee101 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -51,7 +51,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; -import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.IBinder; @@ -59,7 +58,6 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.SynchronousResultReceiver; import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -82,7 +80,6 @@ import java.util.Set; import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -2423,38 +2420,6 @@ public final class BluetoothAdapter { return false; } - /** - * Return the record of {@link BluetoothActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * @param updateType Type of info, cached vs refreshed. - * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or - * unsupported - * @hide - * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)} - * instead. - */ - @Deprecated - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { - SynchronousResultReceiver receiver = new SynchronousResultReceiver(); - requestControllerActivityEnergyInfo(receiver); - try { - SynchronousResultReceiver.Result result = receiver.awaitResult(1000); - if (result.bundle != null) { - return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY); - } - } catch (TimeoutException e) { - Log.e(TAG, "getControllerActivityEnergyInfo timed out"); - } - return null; - } - /** * Request the record of {@link BluetoothActivityEnergyInfo} object that * has the activity and energy info. This can be used to ascertain what -- GitLab From 88eade5300ce39db2c157bb32dac30df961a9d1f Mon Sep 17 00:00:00 2001 From: William Escande Date: Fri, 19 Nov 2021 16:48:45 +0100 Subject: [PATCH 1379/1408] BT MAINLINE ParcelFileDescriptor to dup method In order to have Bluetooth a mainline module, we must remove all call to externa hidden API. Tag: #refactor Bug: 200200870 Test: Build Change-Id: I291d670b1dfc3760bedd5f6666ea04883568df69 --- framework/java/android/bluetooth/BluetoothSocket.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 1655b62bbfe..db5b75148e8 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; @@ -266,7 +265,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("bt socket acept failed"); } - as.mPfd = new ParcelFileDescriptor(fds[0]); + as.mPfd = ParcelFileDescriptor.dup(fds[0]); as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); -- GitLab From 5c9ab293ead54180c2f2b95bc37bebf01bd36b1e Mon Sep 17 00:00:00 2001 From: Alice Kuo Date: Tue, 23 Nov 2021 19:33:12 +0800 Subject: [PATCH 1380/1408] As airplane mode turn on, keep BT on if LE audio profile connected Bug: 207464971 Test: w/o LE audio device, and turn on/off airplane mode Change-Id: I1865c5ff7e8c04a4188dc7c379223e48c8b29ad9 --- .../BluetoothAirplaneModeListener.java | 3 ++- .../bluetooth/BluetoothManagerService.java | 8 ++++--- .../bluetooth/BluetoothModeChangeHelper.java | 21 +++++++++++++++++-- .../BluetoothAirplaneModeListenerTest.java | 6 +++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index 197321f1cb6..263ff189a28 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; * when Bluetooth is on and Bluetooth is in one of the following situations: * 1. Bluetooth A2DP is connected. * 2. Bluetooth Hearing Aid profile is connected. + * 3. Bluetooth LE Audio is connected */ class BluetoothAirplaneModeListener { private static final String TAG = "BluetoothAirplaneModeListener"; @@ -132,7 +133,7 @@ class BluetoothAirplaneModeListener { return false; } if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() - || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { + || !mAirplaneHelper.isMediaProfileConnected()) { return false; } return true; diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index f62935ab1b1..8860a816410 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -35,6 +35,7 @@ import android.app.BroadcastOptions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; @@ -456,12 +457,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) - || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { + || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action) + || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) { final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) && state == BluetoothProfile.STATE_DISCONNECTED - && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + && !mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Device disconnected, reactivating pending flag changes"); onInitFlagsChanged(); } @@ -2291,7 +2293,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + if (mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS + " ms due to existing connections"); diff --git a/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java index 3642e4dccf3..e5854c96820 100644 --- a/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java +++ b/service/java/com/android/server/bluetooth/BluetoothModeChangeHelper.java @@ -20,6 +20,7 @@ import android.annotation.RequiresPermission; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; @@ -37,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; public class BluetoothModeChangeHelper { private volatile BluetoothA2dp mA2dp; private volatile BluetoothHearingAid mHearingAid; + private volatile BluetoothLeAudio mLeAudio; private final BluetoothAdapter mAdapter; private final Context mContext; @@ -47,6 +49,7 @@ public class BluetoothModeChangeHelper { mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEARING_AID); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO); } private final ServiceListener mProfileServiceListener = new ServiceListener() { @@ -60,6 +63,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = (BluetoothHearingAid) proxy; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = (BluetoothLeAudio) proxy; + break; default: break; } @@ -75,6 +81,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = null; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = null; + break; default: break; } @@ -82,8 +91,8 @@ public class BluetoothModeChangeHelper { }; @VisibleForTesting - public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); + public boolean isMediaProfileConnected() { + return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected(); } @VisibleForTesting @@ -142,4 +151,12 @@ public class BluetoothModeChangeHelper { } return hearingAid.getConnectedDevices().size() > 0; } + + private boolean isLeAudioConnected() { + final BluetoothLeAudio leAudio = mLeAudio; + if (leAudio == null) { + return false; + } + return leAudio.getConnectedDevices().size() > 0; + } } diff --git a/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java index 3ace3f4c79d..a1d4c203de1 100644 --- a/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ b/service/tests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -66,7 +66,7 @@ public class BluetoothAirplaneModeListenerTest { when(mHelper.isBluetoothOn()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isAirplaneModeOn()).thenReturn(true); @@ -83,7 +83,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); @@ -97,7 +97,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); -- GitLab From 59ffe8a6a7e37a29a7020f743fac97ad85b026e2 Mon Sep 17 00:00:00 2001 From: Patty Date: Tue, 30 Nov 2021 19:56:07 +0800 Subject: [PATCH 1381/1408] Change isCISCentralSupported() to isLeAudioSupported() API Tag: #feature Bug: 200749925 Bug: 150670922 Test: atest BasicAdapterTest Change-Id: Ie606f45e148f6d9026495279d718a3cc1523c36f --- framework/java/android/bluetooth/BluetoothAdapter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index cf00cbd584a..6154b7149a1 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -2286,21 +2286,21 @@ public final class BluetoothAdapter { public @interface LeFeatureReturnValues {} /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Connected Isochronous Stream Central - * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if + * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is + * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if * the feature is not supported or an error code. * - * @return whether the chipset supports the LE Connected Isochronous Stream Central feature + * @return whether the LE audio is supported */ @RequiresNoPermission - public @LeFeatureReturnValues int isCisCentralSupported() { + public @LeFeatureReturnValues int isLeAudioSupported() { if (!getLeAccess()) { return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isCisCentralSupported(); + return mService.isLeAudioSupported(); } } catch (RemoteException e) { e.rethrowFromSystemServer(); -- GitLab From 697eeaf6d09232d3a06f7c5e67461d35c67a2e92 Mon Sep 17 00:00:00 2001 From: William Escande Date: Wed, 24 Nov 2021 20:13:18 +0100 Subject: [PATCH 1382/1408] replace CURRENT_OR_SELF with non hidden API This API is called by a mainline module (Bluetooth), and needs to be change to an available API. Bug: 200200870 Tag: #refactor Test: Build + start bt + switch user and restart bt Change-Id: I37bb0d16e61d666d0aa51d5efe48fa9b39f9c1c3 --- framework/java/android/bluetooth/BluetoothPbap.java | 3 +-- .../java/android/bluetooth/BluetoothProfileConnector.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index c000e56e7b0..8ee38d3e126 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -23,7 +23,6 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; @@ -170,7 +169,7 @@ public class BluetoothPbap implements BluetoothProfile { mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { + UserHandle.CURRENT)) { Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); return false; } diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index a254291f57d..ecd5e4077de 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -103,7 +103,7 @@ public abstract class BluetoothProfileConnector { mContext.getPackageManager(), 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { + UserHandle.CURRENT)) { logError("Could not bind to Bluetooth Service with " + intent); return false; } -- GitLab From 0ea2899f31af41b6cae5da76da92aad0f0f02dc0 Mon Sep 17 00:00:00 2001 From: Hyundo Moon Date: Tue, 23 Nov 2021 16:33:18 +0900 Subject: [PATCH 1383/1408] BluetoothGatt: Deprecate unsupported methods Bug: 207452114 Test: make update-api -j && make -j; Change-Id: Ic0534c1e25f9e35fae96886002e5ff1dd33f9fd4 --- framework/java/android/bluetooth/BluetoothGatt.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 4e7c01ad2db..fe8d1ba80e3 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1806,32 +1806,33 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument - * * @throws UnsupportedOperationException */ @Override @RequiresNoPermission + @Deprecated public int getConnectionState(BluetoothDevice device) { throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } /** - * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument * * @throws UnsupportedOperationException */ @Override @RequiresNoPermission + @Deprecated public List getConnectedDevices() { throw new UnsupportedOperationException( "Use BluetoothManager#getConnectedDevices instead."); } /** - * Not supported - please use + * @deprecated Not supported - please use * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} * with {@link BluetoothProfile#GATT} as first argument * @@ -1839,6 +1840,7 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override @RequiresNoPermission + @Deprecated public List getDevicesMatchingConnectionStates(int[] states) { throw new UnsupportedOperationException( "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); -- GitLab From 5c05c2f74ff7f5b14083b8000cb7f22438bdf823 Mon Sep 17 00:00:00 2001 From: Patty Date: Thu, 4 Nov 2021 21:03:32 +0800 Subject: [PATCH 1384/1408] Parse audio config codec capability for LE devices 1. Rename function getHwOffloadEncodingFormatsSupportedForA2DP to getHwOffloadFormatsSupportedForBluetoothMedia 2. Add audio format for LC3 3. Add public class BluetoothLeAudioCodecConfig to store the codec for LE audio 4. Add test case for BluetoothLeAudioCodecConfig Tag: #feature Bug: 203535499 Bug: 150670922 Test: atest BluetoothLeAudioCodecConfigTest BluetoothInstrumentationTests Change-Id: I5f64d9078ca2c07e13cffd83b879dd95468ed313 Merged-In: I5f64d9078ca2c07e13cffd83b879dd95468ed313 --- .../BluetoothLeAudioCodecConfig.java | 129 ++++++++++++++++++ .../BluetoothLeAudioCodecConfigTest.java | 59 ++++++++ 2 files changed, 188 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java create mode 100644 framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java diff --git a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java new file mode 100644 index 00000000000..dcaf4b682f4 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2021 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.annotation.IntDef; +import android.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents the codec configuration for a Bluetooth LE Audio source device. + *

    Contains the source codec type. + *

    The source codec type values are the same as those supported by the + * device hardware. + * + * {@see BluetoothLeAudioCodecConfig} + */ +public final class BluetoothLeAudioCodecConfig { + // Add an entry for each source codec here. + + /** @hide */ + @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { + SOURCE_CODEC_TYPE_LC3, + SOURCE_CODEC_TYPE_INVALID + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SourceCodecType {}; + + public static final int SOURCE_CODEC_TYPE_LC3 = 0; + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + + /** + * Represents the count of valid source codec types. Can be accessed via + * {@link #getMaxCodecType}. + */ + private static final int SOURCE_CODEC_TYPE_MAX = 1; + + private final @SourceCodecType int mCodecType; + + /** + * Creates a new BluetoothLeAudioCodecConfig. + * + * @param codecType the source codec type + */ + private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType) { + mCodecType = codecType; + } + + @Override + public String toString() { + return "{codecName:" + getCodecName() + "}"; + } + + /** + * Gets the codec type. + * + * @return the codec type + */ + public @SourceCodecType int getCodecType() { + return mCodecType; + } + + /** + * Returns the valid codec types count. + */ + public static int getMaxCodecType() { + return SOURCE_CODEC_TYPE_MAX; + } + + /** + * Gets the codec name. + * + * @return the codec name + */ + public @NonNull String getCodecName() { + switch (mCodecType) { + case SOURCE_CODEC_TYPE_LC3: + return "LC3"; + case SOURCE_CODEC_TYPE_INVALID: + return "INVALID CODEC"; + default: + break; + } + return "UNKNOWN CODEC(" + mCodecType + ")"; + } + + /** + * Builder for {@link BluetoothLeAudioCodecConfig}. + *

    By default, the codec type will be set to + * {@link BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID} + */ + public static final class Builder { + private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID; + + /** + * Set codec type for Bluetooth codec config. + * + * @param codecType of this codec + * @return the same Builder instance + */ + public @NonNull Builder setCodecType(@SourceCodecType int codecType) { + mCodecType = codecType; + return this; + } + + /** + * Build {@link BluetoothLeAudioCodecConfig}. + * @return new BluetoothLeAudioCodecConfig built + */ + public @NonNull BluetoothLeAudioCodecConfig build() { + return new BluetoothLeAudioCodecConfig(mCodecType); + } + } +} diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java new file mode 100644 index 00000000000..6471492c966 --- /dev/null +++ b/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 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.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothLeAudioCodecConfig}. + */ +public class BluetoothLeAudioCodecConfigTest extends TestCase { + private int[] mCodecTypeArray = new int[] { + BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3, + BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID, + }; + + @SmallTest + public void testBluetoothLeAudioCodecConfig_valid_get_methods() { + + for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) { + int codecType = mCodecTypeArray[codecIdx]; + + BluetoothLeAudioCodecConfig leAudioCodecConfig = + buildBluetoothLeAudioCodecConfig(codecType); + + if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) { + assertEquals("LC3", leAudioCodecConfig.getCodecName()); + } + if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) { + assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName()); + } + + assertEquals(1, leAudioCodecConfig.getMaxCodecType()); + assertEquals(codecType, leAudioCodecConfig.getCodecType()); + } + } + + private BluetoothLeAudioCodecConfig buildBluetoothLeAudioCodecConfig(int sourceCodecType) { + return new BluetoothLeAudioCodecConfig.Builder() + .setCodecType(sourceCodecType) + .build(); + + } +} -- GitLab From dbdda0a36c0dac61c479676c7e1524d83acfb1e8 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 2 Dec 2021 17:59:09 +0100 Subject: [PATCH 1385/1408] Bluetooth: Pretty print LE Audio profile name in logs Bug: 159786353 Change-Id: I1157a34fa91883a861ff0573431009823c2cd8b1 --- framework/java/android/bluetooth/BluetoothProfile.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 0cf9f9fd6f4..4f2dba75bae 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -436,6 +436,8 @@ public interface BluetoothProfile { return "OPP"; case HEARING_AID: return "HEARING_AID"; + case LE_AUDIO: + return "LE_AUDIO"; default: return "UNKNOWN_PROFILE"; } -- GitLab From 55a03869afdab02fd0737daf3f78151959e0d32c Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Tue, 5 Oct 2021 17:11:45 +0000 Subject: [PATCH 1386/1408] Make BluetoothCodecConfig and BluetoothCodecStatus public. Made BluetoothCodecConfig and BluetoothCodecStatus public, added Builder to BluetoothCodecConfig, added accessor for SOURCE_CODEC_TYPE_MAX and changed Arrays returns to List. Tag: #feature Bug: 200202780 Bug: 170678351 Test: BluetoothCodecConfigTest & BluetoothCodecStatusTest NoNonSdkCheck: Only user actually requested the changes Change-Id: Ic61b7087f53f45781f7e0eecca8b6d983093a22d --- .../bluetooth/BluetoothCodecConfig.java | 497 ++++++++++++------ .../bluetooth/BluetoothCodecStatus.java | 111 ++-- .../bluetooth/BluetoothCodecConfigTest.java | 56 +- .../bluetooth/BluetoothCodecStatusTest.java | 253 +++++---- 4 files changed, 537 insertions(+), 380 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java index 1d0bf97c34e..9a4151adffc 100644 --- a/framework/java/android/bluetooth/BluetoothCodecConfig.java +++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java @@ -29,16 +29,14 @@ import java.util.Objects; /** * Represents the codec configuration for a Bluetooth A2DP source device. + *

    Contains the source codec type, the codec priority, the codec sample + * rate, the codec bits per sample, and the codec channel mode. + *

    The source codec type values are the same as those supported by the + * device hardware. * * {@see BluetoothA2dp} - * - * {@hide} */ public final class BluetoothCodecConfig implements Parcelable { - // Add an entry for each source codec here. - // NOTE: The values should be same as those listed in the following file: - // hardware/libhardware/include/hardware/bt_av.h - /** @hide */ @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { SOURCE_CODEC_TYPE_SBC, @@ -46,33 +44,49 @@ public final class BluetoothCodecConfig implements Parcelable { SOURCE_CODEC_TYPE_APTX, SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, - SOURCE_CODEC_TYPE_MAX, SOURCE_CODEC_TYPE_INVALID }) @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} - @UnsupportedAppUsage + /** + * Source codec type SBC. This is the mandatory source codec + * type. + */ public static final int SOURCE_CODEC_TYPE_SBC = 0; - @UnsupportedAppUsage + /** + * Source codec type AAC. + */ public static final int SOURCE_CODEC_TYPE_AAC = 1; - @UnsupportedAppUsage + /** + * Source codec type APTX. + */ public static final int SOURCE_CODEC_TYPE_APTX = 2; - @UnsupportedAppUsage + /** + * Source codec type APTX HD. + */ public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - @UnsupportedAppUsage + /** + * Source codec type LDAC. + */ public static final int SOURCE_CODEC_TYPE_LDAC = 4; - @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_MAX = 5; - - @UnsupportedAppUsage + /** + * Source codec type invalid. This is the default value used for codec + * type. + */ public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + /** + * Represents the count of valid source codec types. Can be accessed via + * {@link #getMaxCodecType}. + */ + private static final int SOURCE_CODEC_TYPE_MAX = 5; + /** @hide */ @IntDef(prefix = "CODEC_PRIORITY_", value = { CODEC_PRIORITY_DISABLED, @@ -82,16 +96,24 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} - @UnsupportedAppUsage + /** + * Codec priority disabled. + * Used to indicate that this codec is disabled and should not be used. + */ public static final int CODEC_PRIORITY_DISABLED = -1; - @UnsupportedAppUsage + /** + * Codec priority default. + * Default value used for codec priority. + */ public static final int CODEC_PRIORITY_DEFAULT = 0; - @UnsupportedAppUsage + /** + * Codec priority highest. + * Used to indicate the highest priority a codec can have. + */ public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - /** @hide */ @IntDef(prefix = "SAMPLE_RATE_", value = { SAMPLE_RATE_NONE, @@ -105,28 +127,42 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} - @UnsupportedAppUsage + /** + * Codec sample rate 0 Hz. Default value used for + * codec sample rate. + */ public static final int SAMPLE_RATE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec sample rate 44100 Hz. + */ public static final int SAMPLE_RATE_44100 = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec sample rate 48000 Hz. + */ public static final int SAMPLE_RATE_48000 = 0x1 << 1; - @UnsupportedAppUsage + /** + * Codec sample rate 88200 Hz. + */ public static final int SAMPLE_RATE_88200 = 0x1 << 2; - @UnsupportedAppUsage + /** + * Codec sample rate 96000 Hz. + */ public static final int SAMPLE_RATE_96000 = 0x1 << 3; - @UnsupportedAppUsage + /** + * Codec sample rate 176400 Hz. + */ public static final int SAMPLE_RATE_176400 = 0x1 << 4; - @UnsupportedAppUsage + /** + * Codec sample rate 192000 Hz. + */ public static final int SAMPLE_RATE_192000 = 0x1 << 5; - /** @hide */ @IntDef(prefix = "BITS_PER_SAMPLE_", value = { BITS_PER_SAMPLE_NONE, @@ -137,19 +173,27 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} - @UnsupportedAppUsage + /** + * Codec bits per sample 0. Default value of the codec + * bits per sample. + */ public static final int BITS_PER_SAMPLE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec bits per sample 16. + */ public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec bits per sample 24. + */ public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - @UnsupportedAppUsage + /** + * Codec bits per sample 32. + */ public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - /** @hide */ @IntDef(prefix = "CHANNEL_MODE_", value = { CHANNEL_MODE_NONE, @@ -159,13 +203,20 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} - @UnsupportedAppUsage + /** + * Codec channel mode NONE. Default value of the + * codec channel mode. + */ public static final int CHANNEL_MODE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec channel mode MONO. + */ public static final int CHANNEL_MODE_MONO = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec channel mode STEREO. + */ public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -178,6 +229,21 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + /** + * Creates a new BluetoothCodecConfig. + * + * @param codecType the source codec type + * @param codecPriority the priority of this codec + * @param sampleRate the codec sample rate + * @param bitsPerSample the bits per sample of this codec + * @param channelMode the channel mode of this codec + * @param codecSpecific1 the specific value 1 + * @param codecSpecific2 the specific value 2 + * @param codecSpecific3 the specific value 3 + * @param codecSpecific4 the specific value 4 + * values to 0. + * @hide + */ @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @@ -195,17 +261,34 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } - @UnsupportedAppUsage + /** + * Creates a new BluetoothCodecConfig. + *

    By default, the codec priority will be set + * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to + * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to + * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to + * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific + * values to 0. + * + * @param codecType the source codec type + */ public BluetoothCodecConfig(@SourceCodecType int codecType) { - mCodecType = codecType; - mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; - mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; - mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; - mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; - mCodecSpecific1 = 0; - mCodecSpecific2 = 0; - mCodecSpecific3 = 0; - mCodecSpecific4 = 0; + this(codecType, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_NONE, + BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, + BluetoothCodecConfig.CHANNEL_MODE_NONE, 0, 0, 0, 0); + } + + private BluetoothCodecConfig(Parcel in) { + mCodecType = in.readInt(); + mCodecPriority = in.readInt(); + mSampleRate = in.readInt(); + mBitsPerSample = in.readInt(); + mChannelMode = in.readInt(); + mCodecSpecific1 = in.readLong(); + mCodecSpecific2 = in.readLong(); + mCodecSpecific3 = in.readLong(); + mCodecSpecific4 = in.readLong(); } @Override @@ -226,10 +309,8 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a hash based on the config values - * - * @return a hash based on the config values - * @hide + * Returns a hash representation of this BluetoothCodecConfig + * based on all the config values. */ @Override public int hashCode() { @@ -238,33 +319,25 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); } - /** - * Checks whether the object contains valid codec configuration. - * - * @return true if the object contains valid codec configuration, otherwise false. - * @hide - */ - public boolean isValid() { - return (mSampleRate != SAMPLE_RATE_NONE) - && (mBitsPerSample != BITS_PER_SAMPLE_NONE) - && (mChannelMode != CHANNEL_MODE_NONE); - } - /** * Adds capability string to an existing string. * - * @param prevStr the previous string with the capabilities. Can be a null pointer. - * @param capStr the capability string to append to prevStr argument. - * @return the result string in the form "prevStr|capStr". + * @param prevStr the previous string with the capabilities. Can be a {@code null} pointer + * @param capStr the capability string to append to prevStr argument + * @return the result string in the form "prevStr|capStr" */ - private static String appendCapabilityToString(String prevStr, - String capStr) { + private static String appendCapabilityToString(@Nullable String prevStr, + @NonNull String capStr) { if (prevStr == null) { return capStr; } return prevStr + "|" + capStr; } + /** + * Returns a {@link String} that describes each BluetoothCodecConfig parameter + * current value. + */ @Override public String toString() { String sampleRateStr = null; @@ -331,8 +404,6 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Always returns 0 - * * @return 0 * @hide */ @@ -344,20 +415,7 @@ public final class BluetoothCodecConfig implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothCodecConfig createFromParcel(Parcel in) { - final int codecType = in.readInt(); - final int codecPriority = in.readInt(); - final int sampleRate = in.readInt(); - final int bitsPerSample = in.readInt(); - final int channelMode = in.readInt(); - final long codecSpecific1 = in.readLong(); - final long codecSpecific2 = in.readLong(); - final long codecSpecific3 = in.readLong(); - final long codecSpecific4 = in.readLong(); - return new BluetoothCodecConfig(codecType, codecPriority, - sampleRate, bitsPerSample, - channelMode, codecSpecific1, - codecSpecific2, codecSpecific3, - codecSpecific4); + return new BluetoothCodecConfig(in); } public BluetoothCodecConfig[] newArray(int size) { @@ -368,8 +426,8 @@ public final class BluetoothCodecConfig implements Parcelable { /** * Flattens the object to a parcel * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. + * @param out The Parcel in which the object should be written + * @param flags Additional flags about how the object should be written * * @hide */ @@ -387,9 +445,8 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Gets the codec name. - * - * @return the codec name + * Returns the codec name converted to {@link String}. + * @hide */ public @NonNull String getCodecName() { switch (mCodecType) { @@ -412,137 +469,100 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Gets the codec type. - * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}. - * - * @return the codec type + * Returns the source codec type of this config. */ - @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } + /** + * Returns the valid codec types count. + */ + public static int getMaxCodecType() { + return SOURCE_CODEC_TYPE_MAX; + } + /** * Checks whether the codec is mandatory. + *

    The actual mandatory codec type for Android Bluetooth audio is SBC. + * See {@link #SOURCE_CODEC_TYPE_SBC}. * - * @return true if the codec is mandatory, otherwise false. + * @return {@code true} if the codec is mandatory, {@code false} otherwise + * @hide */ public boolean isMandatoryCodec() { return mCodecType == SOURCE_CODEC_TYPE_SBC; } /** - * Gets the codec selection priority. - * The codec selection priority is relative to other codecs: larger value - * means higher priority. If 0, reset to default. - * - * @return the codec priority + * Returns the codec selection priority. + *

    The codec selection priority is relative to other codecs: larger value + * means higher priority. */ - @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } /** * Sets the codec selection priority. - * The codec selection priority is relative to other codecs: larger value - * means higher priority. If 0, reset to default. + *

    The codec selection priority is relative to other codecs: larger value + * means higher priority. * - * @param codecPriority the codec priority + * @param codecPriority the priority this codec should have * @hide */ - @UnsupportedAppUsage public void setCodecPriority(@CodecPriority int codecPriority) { mCodecPriority = codecPriority; } /** - * Gets the codec sample rate. The value can be a bitmask with all - * supported sample rates: - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000} - * - * @return the codec sample rate + * Returns the codec sample rate. The value can be a bitmask with all + * supported sample rates. */ - @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } /** - * Gets the codec bits per sample. The value can be a bitmask with all - * bits per sample supported: - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32} - * - * @return the codec bits per sample + * Returns the codec bits per sample. The value can be a bitmask with all + * bits per sample supported. */ - @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } /** - * Gets the codec channel mode. The value can be a bitmask with all - * supported channel modes: - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO} - * - * @return the codec channel mode - * @hide + * Returns the codec channel mode. The value can be a bitmask with all + * supported channel modes. */ - @UnsupportedAppUsage public @ChannelMode int getChannelMode() { return mChannelMode; } /** - * Gets a codec specific value1. - * - * @return a codec specific value1. + * Returns the codec specific value1. */ - @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } /** - * Gets a codec specific value2. - * - * @return a codec specific value2 - * @hide + * Returns the codec specific value2. */ - @UnsupportedAppUsage public long getCodecSpecific2() { return mCodecSpecific2; } /** - * Gets a codec specific value3. - * - * @return a codec specific value3 - * @hide + * Returns the codec specific value3. */ - @UnsupportedAppUsage public long getCodecSpecific3() { return mCodecSpecific3; } /** - * Gets a codec specific value4. - * - * @return a codec specific value4 - * @hide + * Returns the codec specific value4. */ - @UnsupportedAppUsage public long getCodecSpecific4() { return mCodecSpecific4; } @@ -551,7 +571,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether a value set presented by a bitmask has zero or single bit * * @param valueSet the value set presented by a bitmask - * @return true if the valueSet contains zero or single bit, otherwise false. + * @return {@code true} if the valueSet contains zero or single bit, {@code false} otherwise * @hide */ private static boolean hasSingleBit(int valueSet) { @@ -559,9 +579,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single sample rate. - * - * @return true if the object contains none or single sample rate, otherwise false. + * Returns whether the object contains none or single sample rate. * @hide */ public boolean hasSingleSampleRate() { @@ -569,9 +587,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single bits per sample. - * - * @return true if the object contains none or single bits per sample, otherwise false. + * Returns whether the object contains none or single bits per sample. * @hide */ public boolean hasSingleBitsPerSample() { @@ -579,9 +595,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single channel mode. - * - * @return true if the object contains none or single channel mode, otherwise false. + * Returns whether the object contains none or single channel mode. * @hide */ public boolean hasSingleChannelMode() { @@ -589,10 +603,10 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the audio feeding parameters are same. + * Checks whether the audio feeding parameters are the same. * * @param other the codec config to compare against - * @return true if the audio feeding parameters are same, otherwise false + * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise * @hide */ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { @@ -606,7 +620,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Any parameters with NONE value will be considered to be a wildcard matching. * * @param other the codec config to compare against - * @return true if the audio feeding parameters are similar, otherwise false. + * @return {@code true} if the audio feeding parameters are similar, {@code false} otherwise * @hide */ public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { @@ -614,18 +628,18 @@ public final class BluetoothCodecConfig implements Parcelable { return false; } int sampleRate = other.mSampleRate; - if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE - || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + if (mSampleRate == SAMPLE_RATE_NONE + || sampleRate == SAMPLE_RATE_NONE) { sampleRate = mSampleRate; } int bitsPerSample = other.mBitsPerSample; - if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE - || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + if (mBitsPerSample == BITS_PER_SAMPLE_NONE + || bitsPerSample == BITS_PER_SAMPLE_NONE) { bitsPerSample = mBitsPerSample; } int channelMode = other.mChannelMode; - if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE - || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + if (mChannelMode == CHANNEL_MODE_NONE + || channelMode == CHANNEL_MODE_NONE) { channelMode = mChannelMode; } return sameAudioFeedingParameters(new BluetoothCodecConfig( @@ -636,25 +650,158 @@ public final class BluetoothCodecConfig implements Parcelable { /** * Checks whether the codec specific parameters are the same. + *

    Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1 + * are compared. * * @param other the codec config to compare against - * @return true if the codec specific parameters are the same, otherwise false. + * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise * @hide */ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { if (other == null && mCodecType != other.mCodecType) { return false; } - // Currently we only care about the AAC VBR and LDAC Playback Quality at CodecSpecific1 switch (mCodecType) { case SOURCE_CODEC_TYPE_AAC: case SOURCE_CODEC_TYPE_LDAC: if (mCodecSpecific1 != other.mCodecSpecific1) { return false; } - // fall through default: return true; } } + + /** + * Builder for {@link BluetoothCodecConfig}. + *

    By default, the codec type will be set to + * {@link BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority + * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to + * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to + * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to + * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific + * values to 0. + */ + public static final class Builder { + private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; + private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; + private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; + private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; + private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; + private long mCodecSpecific1 = 0; + private long mCodecSpecific2 = 0; + private long mCodecSpecific3 = 0; + private long mCodecSpecific4 = 0; + + /** + * Set codec type for Bluetooth codec config. + * + * @param codecType of this codec + * @return the same Builder instance + */ + public @NonNull Builder setCodecType(@SourceCodecType int codecType) { + mCodecType = codecType; + return this; + } + + /** + * Set codec priority for Bluetooth codec config. + * + * @param codecPriority of this codec + * @return the same Builder instance + */ + public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) { + mCodecPriority = codecPriority; + return this; + } + + /** + * Set sample rate for Bluetooth codec config. + * + * @param sampleRate of this codec + * @return the same Builder instance + */ + public @NonNull Builder setSampleRate(@SampleRate int sampleRate) { + mSampleRate = sampleRate; + return this; + } + + /** + * Set the bits per sample for Bluetooth codec config. + * + * @param bitsPerSample of this codec + * @return the same Builder instance + */ + public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) { + mBitsPerSample = bitsPerSample; + return this; + } + + /** + * Set the channel mode for Bluetooth codec config. + * + * @param channelMode of this codec + * @return the same Builder instance + */ + public @NonNull Builder setChannelMode(@ChannelMode int channelMode) { + mChannelMode = channelMode; + return this; + } + + /** + * Set the first codec specific values for Bluetooth codec config. + * + * @param codecSpecific1 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific1(long codecSpecific1) { + mCodecSpecific1 = codecSpecific1; + return this; + } + + /** + * Set the second codec specific values for Bluetooth codec config. + * + * @param codecSpecific2 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific2(long codecSpecific2) { + mCodecSpecific2 = codecSpecific2; + return this; + } + + /** + * Set the third codec specific values for Bluetooth codec config. + * + * @param codecSpecific3 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific3(long codecSpecific3) { + mCodecSpecific3 = codecSpecific3; + return this; + } + + /** + * Set the fourth codec specific values for Bluetooth codec config. + * + * @param codecSpecific4 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific4(long codecSpecific4) { + mCodecSpecific4 = codecSpecific4; + return this; + } + + /** + * Build {@link BluetoothCodecConfig}. + * @return new BluetoothCodecConfig built + */ + public @NonNull BluetoothCodecConfig build() { + return new BluetoothCodecConfig(mCodecType, mCodecPriority, + mSampleRate, mBitsPerSample, + mChannelMode, mCodecSpecific1, + mCodecSpecific2, mCodecSpecific3, + mCodecSpecific4); + } + } } diff --git a/framework/java/android/bluetooth/BluetoothCodecStatus.java b/framework/java/android/bluetooth/BluetoothCodecStatus.java index 7764ebeb2e3..02606feb3b3 100644 --- a/framework/java/android/bluetooth/BluetoothCodecStatus.java +++ b/framework/java/android/bluetooth/BluetoothCodecStatus.java @@ -16,12 +16,13 @@ package android.bluetooth; +import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -29,8 +30,6 @@ import java.util.Objects; * A2DP source device. * * {@see BluetoothA2dp} - * - * {@hide} */ public final class BluetoothCodecStatus implements Parcelable { /** @@ -39,22 +38,27 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; private final @Nullable BluetoothCodecConfig mCodecConfig; - private final BluetoothCodecConfig[] mCodecsLocalCapabilities; - private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; + private final @Nullable List mCodecsLocalCapabilities; + private final @Nullable List mCodecsSelectableCapabilities; public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig, - @Nullable BluetoothCodecConfig[] codecsLocalCapabilities, - @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities) { + @Nullable List codecsLocalCapabilities, + @Nullable List codecsSelectableCapabilities) { mCodecConfig = codecConfig; mCodecsLocalCapabilities = codecsLocalCapabilities; mCodecsSelectableCapabilities = codecsSelectableCapabilities; } + private BluetoothCodecStatus(Parcel in) { + mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); + mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); + mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); + } + @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecStatus) { @@ -68,26 +72,25 @@ public final class BluetoothCodecStatus implements Parcelable { } /** - * Checks whether two arrays of capabilities contain same capabilities. - * The order of the capabilities in each array is ignored. + * Checks whether two lists of capabilities contain same capabilities. + * The order of the capabilities in each list is ignored. * - * @param c1 the first array of capabilities to compare - * @param c2 the second array of capabilities to compare - * @return true if both arrays contain same capabilities - * @hide + * @param c1 the first list of capabilities to compare + * @param c2 the second list of capabilities to compare + * @return {@code true} if both lists contain same capabilities */ - public static boolean sameCapabilities(BluetoothCodecConfig[] c1, - BluetoothCodecConfig[] c2) { + private static boolean sameCapabilities(@Nullable List c1, + @Nullable List c2) { if (c1 == null) { return (c2 == null); } if (c2 == null) { return false; } - if (c1.length != c2.length) { + if (c1.size() != c2.size()) { return false; } - return Arrays.asList(c1).containsAll(Arrays.asList(c2)); + return c1.containsAll(c2); } /** @@ -95,10 +98,9 @@ public final class BluetoothCodecStatus implements Parcelable { * Any parameters of the codec config with NONE value will be considered a wildcard matching. * * @param codecConfig the codec config to compare against - * @return true if the codec config matches, otherwise false - * @hide + * @return {@code true} if the codec config matches, {@code false} otherwise */ - public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) { if (codecConfig == null || !codecConfig.hasSingleSampleRate() || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { return false; @@ -128,10 +130,7 @@ public final class BluetoothCodecStatus implements Parcelable { } /** - * Returns a hash based on the codec config and local capabilities - * - * @return a hash based on the config values - * @hide + * Returns a hash based on the codec config and local capabilities. */ @Override public int hashCode() { @@ -139,17 +138,19 @@ public final class BluetoothCodecStatus implements Parcelable { mCodecsLocalCapabilities); } + /** + * Returns a {@link String} that describes each BluetoothCodecStatus parameter + * current value. + */ @Override public String toString() { return "{mCodecConfig:" + mCodecConfig - + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) - + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities + + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities + "}"; } /** - * Always returns 0 - * * @return 0 * @hide */ @@ -161,16 +162,7 @@ public final class BluetoothCodecStatus implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothCodecStatus createFromParcel(Parcel in) { - final BluetoothCodecConfig codecConfig = in.readTypedObject( - BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray( - BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray( - BluetoothCodecConfig.CREATOR); - - return new BluetoothCodecStatus(codecConfig, - codecsLocalCapabilities, - codecsSelectableCapabilities); + return new BluetoothCodecStatus(in); } public BluetoothCodecStatus[] newArray(int size) { @@ -179,47 +171,38 @@ public final class BluetoothCodecStatus implements Parcelable { }; /** - * Flattens the object to a parcel + * Flattens the object to a parcel. * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. - * - * @hide + * @param out The Parcel in which the object should be written + * @param flags Additional flags about how the object should be written */ @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); - out.writeTypedArray(mCodecsLocalCapabilities, 0); - out.writeTypedArray(mCodecsSelectableCapabilities, 0); + out.writeTypedList(mCodecsLocalCapabilities); + out.writeTypedList(mCodecsSelectableCapabilities); } /** - * Gets the current codec configuration. - * - * @return the current codec configuration + * Returns the current codec configuration. */ - @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } /** - * Gets the codecs local capabilities. - * - * @return an array with the codecs local capabilities + * Returns the codecs local capabilities. */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { - return mCodecsLocalCapabilities; + public @NonNull List getCodecsLocalCapabilities() { + return (mCodecsLocalCapabilities == null) + ? Collections.emptyList() : mCodecsLocalCapabilities; } /** - * Gets the codecs selectable capabilities. - * - * @return an array with the codecs selectable capabilities + * Returns the codecs selectable capabilities. */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { - return mCodecsSelectableCapabilities; + public @NonNull List getCodecsSelectableCapabilities() { + return (mCodecsSelectableCapabilities == null) + ? Collections.emptyList() : mCodecsSelectableCapabilities; } } diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java index 59b46656dd5..bd55426601f 100644 --- a/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothCodecConfigTest.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import junit.framework.TestCase; @@ -34,7 +33,6 @@ public class BluetoothCodecConfigTest extends TestCase { BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX, BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID, }; private static final int[] kCodecPriorityArray = new int[] { @@ -168,20 +166,11 @@ public class BluetoothCodecConfigTest extends TestCase { long codec_specific3 = selectCodecSpecific3(config_id); long codec_specific4 = selectCodecSpecific4(config_id); - BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority, + BluetoothCodecConfig bcc = buildBluetoothCodecConfig(codec_type, codec_priority, sample_rate, bits_per_sample, channel_mode, codec_specific1, codec_specific2, codec_specific3, codec_specific4); - if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { - assertFalse(bcc.isValid()); - } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { - assertFalse(bcc.isValid()); - } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { - assertFalse(bcc.isValid()); - } else { - assertTrue(bcc.isValid()); - } if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { assertTrue(bcc.isMandatoryCodec()); @@ -204,10 +193,6 @@ public class BluetoothCodecConfigTest extends TestCase { if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) { assertEquals("LDAC", bcc.getCodecName()); } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) { - assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")", - bcc.getCodecName()); - } if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) { assertEquals("INVALID CODEC", bcc.getCodecName()); } @@ -227,7 +212,7 @@ public class BluetoothCodecConfigTest extends TestCase { @SmallTest public void testBluetoothCodecConfig_equals() { BluetoothCodecConfig bcc1 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -235,7 +220,7 @@ public class BluetoothCodecConfigTest extends TestCase { 1000, 2000, 3000, 4000); BluetoothCodecConfig bcc2_same = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -244,7 +229,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertTrue(bcc1.equals(bcc2_same)); BluetoothCodecConfig bcc3_codec_type = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -253,7 +238,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc3_codec_type)); BluetoothCodecConfig bcc4_codec_priority = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -262,7 +247,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc4_codec_priority)); BluetoothCodecConfig bcc5_sample_rate = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_48000, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -271,7 +256,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc5_sample_rate)); BluetoothCodecConfig bcc6_bits_per_sample = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -280,7 +265,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc6_bits_per_sample)); BluetoothCodecConfig bcc7_channel_mode = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -289,7 +274,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc7_channel_mode)); BluetoothCodecConfig bcc8_codec_specific1 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -298,7 +283,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc8_codec_specific1)); BluetoothCodecConfig bcc9_codec_specific2 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -307,7 +292,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc9_codec_specific2)); BluetoothCodecConfig bcc10_codec_specific3 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -316,7 +301,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc10_codec_specific3)); BluetoothCodecConfig bcc11_codec_specific4 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -324,4 +309,21 @@ public class BluetoothCodecConfigTest extends TestCase { 1000, 2000, 3000, 4004); assertFalse(bcc1.equals(bcc11_codec_specific4)); } + + private BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, + int codecPriority, int sampleRate, int bitsPerSample, int channelMode, + long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { + return new BluetoothCodecConfig.Builder() + .setCodecType(sourceCodecType) + .setCodecPriority(codecPriority) + .setSampleRate(sampleRate) + .setBitsPerSample(bitsPerSample) + .setChannelMode(channelMode) + .setCodecSpecific1(codecSpecific1) + .setCodecSpecific2(codecSpecific2) + .setCodecSpecific3(codecSpecific3) + .setCodecSpecific4(codecSpecific4) + .build(); + + } } diff --git a/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java index 83bf2ed1938..1cb2dcae865 100644 --- a/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothCodecStatusTest.java @@ -17,13 +17,13 @@ package android.bluetooth; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import java.util.Arrays; -import java.util.Objects; import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + /** * Unit test cases for {@link BluetoothCodecStatus}. *

    @@ -34,7 +34,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Codec configs: A and B are same; C is different private static final BluetoothCodecConfig config_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -42,7 +42,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig config_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -50,7 +50,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig config_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -59,7 +59,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Local capabilities: A and B are same; C is different private static final BluetoothCodecConfig local_capability1_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -69,7 +69,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability1_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -79,7 +79,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability1_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -89,7 +89,7 @@ public class BluetoothCodecStatusTest extends TestCase { private static final BluetoothCodecConfig local_capability2_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -99,7 +99,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability2_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -109,7 +109,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability2_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -118,7 +118,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -128,7 +128,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -138,7 +138,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -147,7 +147,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -157,7 +157,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -167,7 +167,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -176,7 +176,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -190,7 +190,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -204,7 +204,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -219,7 +219,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Selectable capabilities: A and B are same; C is different private static final BluetoothCodecConfig selectable_capability1_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -228,7 +228,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability1_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -237,7 +237,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability1_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -245,7 +245,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -254,7 +254,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -263,7 +263,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -271,7 +271,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -280,7 +280,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -289,7 +289,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -297,7 +297,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -306,7 +306,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -315,7 +315,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -323,7 +323,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -337,7 +337,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -351,7 +351,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -363,79 +363,87 @@ public class BluetoothCodecStatusTest extends TestCase { BluetoothCodecConfig.CHANNEL_MODE_STEREO, 1000, 2000, 3000, 4000); - private static final BluetoothCodecConfig[] local_capability_A = { - local_capability1_A, - local_capability2_A, - local_capability3_A, - local_capability4_A, - local_capability5_A, - }; - - private static final BluetoothCodecConfig[] local_capability_B = { - local_capability1_B, - local_capability2_B, - local_capability3_B, - local_capability4_B, - local_capability5_B, - }; - - private static final BluetoothCodecConfig[] local_capability_B_reordered = { - local_capability5_B, - local_capability4_B, - local_capability2_B, - local_capability3_B, - local_capability1_B, - }; - - private static final BluetoothCodecConfig[] local_capability_C = { - local_capability1_C, - local_capability2_C, - local_capability3_C, - local_capability4_C, - local_capability5_C, - }; - - private static final BluetoothCodecConfig[] selectable_capability_A = { - selectable_capability1_A, - selectable_capability2_A, - selectable_capability3_A, - selectable_capability4_A, - selectable_capability5_A, - }; - - private static final BluetoothCodecConfig[] selectable_capability_B = { - selectable_capability1_B, - selectable_capability2_B, - selectable_capability3_B, - selectable_capability4_B, - selectable_capability5_B, - }; - - private static final BluetoothCodecConfig[] selectable_capability_B_reordered = { - selectable_capability5_B, - selectable_capability4_B, - selectable_capability2_B, - selectable_capability3_B, - selectable_capability1_B, - }; - - private static final BluetoothCodecConfig[] selectable_capability_C = { - selectable_capability1_C, - selectable_capability2_C, - selectable_capability3_C, - selectable_capability4_C, - selectable_capability5_C, - }; + private static final List LOCAL_CAPABILITY_A = + new ArrayList() {{ + add(local_capability1_A); + add(local_capability2_A); + add(local_capability3_A); + add(local_capability4_A); + add(local_capability5_A); + }}; + + private static final List LOCAL_CAPABILITY_B = + new ArrayList() {{ + add(local_capability1_B); + add(local_capability2_B); + add(local_capability3_B); + add(local_capability4_B); + add(local_capability5_B); + }}; + + private static final List LOCAL_CAPABILITY_B_REORDERED = + new ArrayList() {{ + add(local_capability5_B); + add(local_capability4_B); + add(local_capability2_B); + add(local_capability3_B); + add(local_capability1_B); + }}; + + private static final List LOCAL_CAPABILITY_C = + new ArrayList() {{ + add(local_capability1_C); + add(local_capability2_C); + add(local_capability3_C); + add(local_capability4_C); + add(local_capability5_C); + }}; + + private static final List SELECTABLE_CAPABILITY_A = + new ArrayList() {{ + add(selectable_capability1_A); + add(selectable_capability2_A); + add(selectable_capability3_A); + add(selectable_capability4_A); + add(selectable_capability5_A); + }}; + + private static final List SELECTABLE_CAPABILITY_B = + new ArrayList() {{ + add(selectable_capability1_B); + add(selectable_capability2_B); + add(selectable_capability3_B); + add(selectable_capability4_B); + add(selectable_capability5_B); + }}; + + private static final List SELECTABLE_CAPABILITY_B_REORDERED = + new ArrayList() {{ + add(selectable_capability5_B); + add(selectable_capability4_B); + add(selectable_capability2_B); + add(selectable_capability3_B); + add(selectable_capability1_B); + }}; + + private static final List SELECTABLE_CAPABILITY_C = + new ArrayList() {{ + add(selectable_capability1_C); + add(selectable_capability2_C); + add(selectable_capability3_C); + add(selectable_capability4_C); + add(selectable_capability5_C); + }}; private static final BluetoothCodecStatus bcs_A = - new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A); + new BluetoothCodecStatus(config_A, LOCAL_CAPABILITY_A, SELECTABLE_CAPABILITY_A); private static final BluetoothCodecStatus bcs_B = - new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B); + new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B, SELECTABLE_CAPABILITY_B); private static final BluetoothCodecStatus bcs_B_reordered = - new BluetoothCodecStatus(config_B, local_capability_B_reordered, - selectable_capability_B_reordered); + new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B_REORDERED, + SELECTABLE_CAPABILITY_B_REORDERED); private static final BluetoothCodecStatus bcs_C = - new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C); + new BluetoothCodecStatus(config_C, LOCAL_CAPABILITY_C, SELECTABLE_CAPABILITY_C); @SmallTest public void testBluetoothCodecStatus_get_methods() { @@ -444,16 +452,16 @@ public class BluetoothCodecStatusTest extends TestCase { assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B)); assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C)); - assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A)); - assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B)); - assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C)); + assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_A)); + assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_B)); + assertFalse(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_C)); - assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_A)); - assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_B)); - assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_C)); + assertTrue(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_A)); + assertTrue(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_B)); + assertFalse(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_C)); } @SmallTest @@ -465,4 +473,21 @@ public class BluetoothCodecStatusTest extends TestCase { assertFalse(bcs_A.equals(bcs_C)); assertFalse(bcs_C.equals(bcs_A)); } + + private static BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, + int codecPriority, int sampleRate, int bitsPerSample, int channelMode, + long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { + return new BluetoothCodecConfig.Builder() + .setCodecType(sourceCodecType) + .setCodecPriority(codecPriority) + .setSampleRate(sampleRate) + .setBitsPerSample(bitsPerSample) + .setChannelMode(channelMode) + .setCodecSpecific1(codecSpecific1) + .setCodecSpecific2(codecSpecific2) + .setCodecSpecific3(codecSpecific3) + .setCodecSpecific4(codecSpecific4) + .build(); + + } } -- GitLab From 2eb043e7e5273b0f8e70c00c71361a17d97262ee Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Thu, 4 Nov 2021 12:57:33 -0700 Subject: [PATCH 1387/1408] Add BluetoothLeBroadcast profile API. Implementation for the APIs will be added later. Bug: 205174140 Bug: 208222281 Tag: #feature Test: gd/cert/run Change-Id: I8781be89cba45a6e7a76fec7df24790a9f02d6a6 --- .../bluetooth/BluetoothLeBroadcast.java | 287 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- .../bluetooth/BluetoothStatusCodes.java | 60 ++++ 3 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeBroadcast.java diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcast.java b/framework/java/android/bluetooth/BluetoothLeBroadcast.java new file mode 100644 index 00000000000..fed9f911d5b --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeBroadcast.java @@ -0,0 +1,287 @@ +/* + * Copyright 2021 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.annotation.IntDef; +import android.content.Context; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile. + * + *

    BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast + * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} + * to get the BluetoothLeBroadcast proxy object. + * + * @hide + */ +public final class BluetoothLeBroadcast implements BluetoothProfile { + private static final String TAG = "BluetoothLeBroadcast"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Constants used by the LE Audio Broadcast profile for the Broadcast state + * + * @hide + */ + @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = { + LE_AUDIO_BROADCAST_STATE_DISABLED, + LE_AUDIO_BROADCAST_STATE_ENABLING, + LE_AUDIO_BROADCAST_STATE_ENABLED, + LE_AUDIO_BROADCAST_STATE_DISABLING, + LE_AUDIO_BROADCAST_STATE_PLAYING, + LE_AUDIO_BROADCAST_STATE_NOT_PLAYING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastState {} + + /** + * Indicates that LE Audio Broadcast mode is currently disabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10; + + /** + * Indicates that LE Audio Broadcast mode is being enabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11; + + /** + * Indicates that LE Audio Broadcast mode is currently enabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12; + /** + * Indicates that LE Audio Broadcast mode is being disabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13; + + /** + * Indicates that an LE Audio Broadcast mode is currently playing + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14; + + /** + * Indicates that LE Audio Broadcast is currently not playing + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15; + + /** + * Constants used by the LE Audio Broadcast profile for encryption key length + * + * @hide + */ + @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = { + LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT, + LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioEncryptionKeyLength {} + + /** + * Indicates that the LE Audio Broadcast encryption key size is 32 bits. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16; + + /** + * Indicates that the LE Audio Broadcast encryption key size is 128 bits. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17; + + /** + * Interface for receiving events related to broadcasts + */ + public interface Callback { + /** + * Called when broadcast state has changed + * + * @param prevState broadcast state before the change + * @param newState broadcast state after the change + */ + @LeAudioBroadcastState + void onBroadcastStateChange(int prevState, int newState); + /** + * Called when encryption key has been updated + * + * @param success true if the key was updated successfully, false otherwise + */ + void onEncryptionKeySet(boolean success); + } + + /** + * Create a BluetoothLeBroadcast proxy object for interacting with the local + * LE Audio Broadcast Source service. + * + * @hide + */ + /*package*/ BluetoothLeBroadcast(Context context, + BluetoothProfile.ServiceListener listener) { + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public int getConnectionState(BluetoothDevice device) { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public List getConnectedDevices() { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Enable LE Audio Broadcast mode. + * + * Generates a new broadcast ID and enables sending of encrypted or unencrypted + * isochronous PDUs + * + * @hide + */ + public int enableBroadcastMode() { + if (DBG) log("enableBroadcastMode"); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; + } + + /** + * Disable LE Audio Broadcast mode. + * + * @hide + */ + public int disableBroadcastMode() { + if (DBG) log("disableBroadcastMode"); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; + } + + /** + * Get the current LE Audio broadcast state + * + * @hide + */ + @LeAudioBroadcastState + public int getBroadcastState() { + if (DBG) log("getBroadcastState"); + return LE_AUDIO_BROADCAST_STATE_DISABLED; + } + + /** + * Enable LE Audio broadcast encryption + * + * @param keyLength if useExisting is true, this specifies the length of the key that should + * be generated + * @param useExisting true, if an existing key should be used + * false, if a new key should be generated + * + * @hide + */ + @LeAudioEncryptionKeyLength + public int enableEncryption(boolean useExisting, int keyLength) { + if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED; + } + + /** + * Disable LE Audio broadcast encryption + * + * @param removeExisting true, if the existing key should be removed + * false, otherwise + * + * @hide + */ + public int disableEncryption(boolean removeExisting) { + if (DBG) log("disableEncryption removeExisting=" + removeExisting); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED; + } + + /** + * Enable or disable LE Audio broadcast encryption + * + * @param key use the provided key if non-null, generate a new key if null + * @param keyLength 0 if encryption is disabled, 4 bytes (low security), + * 16 bytes (high security) + * + * @hide + */ + @LeAudioEncryptionKeyLength + public int setEncryptionKey(byte[] key, int keyLength) { + if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED; + } + + + /** + * Get the encryption key that was set before + * + * @return encryption key as a byte array or null if no encryption key was set + * + * @hide + */ + public byte[] getEncryptionKey() { + if (DBG) log("getEncryptionKey"); + return null; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index 0cf9f9fd6f4..7f96958a618 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -232,13 +232,20 @@ public interface BluetoothProfile { */ int CSIP_SET_COORDINATOR = 25; + /** + * LE Audio Broadcast Source + * + * @hide + */ + int LE_AUDIO_BROADCAST = 26; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 25; + int MAX_PROFILE_ID = 26; /** * Default priority for devices that we try to auto-connect to and diff --git a/framework/java/android/bluetooth/BluetoothStatusCodes.java b/framework/java/android/bluetooth/BluetoothStatusCodes.java index ca01784efd8..9dafa073ab3 100644 --- a/framework/java/android/bluetooth/BluetoothStatusCodes.java +++ b/framework/java/android/bluetooth/BluetoothStatusCodes.java @@ -225,6 +225,66 @@ public final class BluetoothStatusCodes { */ public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109; + /** + * Indicates that setting the LE Audio Broadcast mode failed. + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110; + + /** + * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed. + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111; + + /** + * Indicates that connecting to a remote Broadcast Audio Scan Service failed. + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112; + + /** + * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed. + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113; + + /** + * Indicates that enabling LE Audio Broadcast encryption failed + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114; + + /** + * Indicates that disabling LE Audio Broadcast encryption failed + *

    + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115; + /** * Indicates that an unknown error has occurred has occurred. */ -- GitLab From 902e20dd83b9fc4f26c856c337101e55fab9cbe4 Mon Sep 17 00:00:00 2001 From: Patty Date: Mon, 6 Dec 2021 23:33:03 +0800 Subject: [PATCH 1388/1408] Remove unnecessary test case Tag: #refactor Bug: 203535499 Bug: 150670922 Test: atest BluetoothLeAudioCodecConfigTest BluetoothInstrumentationTests Change-Id: I5f3301c96990becca0f8baa0a5e61502c411e83c --- .../src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java index 6471492c966..c3d707cd759 100644 --- a/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java +++ b/framework/tests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java @@ -45,7 +45,6 @@ public class BluetoothLeAudioCodecConfigTest extends TestCase { assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName()); } - assertEquals(1, leAudioCodecConfig.getMaxCodecType()); assertEquals(codecType, leAudioCodecConfig.getCodecType()); } } -- GitLab From 44ba557a903cd39b7441163feafc4a251611f0a0 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Tue, 7 Dec 2021 14:58:35 +0000 Subject: [PATCH 1389/1408] Added missing equals override in new Transport Data classes Tag: #feature Bug: 208904630 Test: atest Change-Id: I92cbd152eb063c866f7101040cb228036ae2c07a --- .../android/bluetooth/le/TransportBlock.java | 28 +++++++++++++++++-- .../bluetooth/le/TransportDiscoveryData.java | 16 +++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/le/TransportBlock.java b/framework/java/android/bluetooth/le/TransportBlock.java index b388beda6b3..18bad9c3c25 100644 --- a/framework/java/android/bluetooth/le/TransportBlock.java +++ b/framework/java/android/bluetooth/le/TransportBlock.java @@ -24,6 +24,7 @@ import android.util.Log; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; +import java.util.Arrays; /** * Wrapper for Transport Discovery Data Transport Blocks. @@ -59,8 +60,12 @@ public final class TransportBlock implements Parcelable { mOrgId = in.readInt(); mTdsFlags = in.readInt(); mTransportDataLength = in.readInt(); - mTransportData = new byte[mTransportDataLength]; - in.readByteArray(mTransportData); + if (mTransportDataLength > 0) { + mTransportData = new byte[mTransportDataLength]; + in.readByteArray(mTransportData); + } else { + mTransportData = null; + } } @Override @@ -68,7 +73,9 @@ public final class TransportBlock implements Parcelable { dest.writeInt(mOrgId); dest.writeInt(mTdsFlags); dest.writeInt(mTransportDataLength); - dest.writeByteArray(mTransportData); + if (mTransportData != null) { + dest.writeByteArray(mTransportData); + } } /** @@ -79,6 +86,21 @@ public final class TransportBlock implements Parcelable { return 0; } + /** + * @hide + */ + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TransportBlock other = (TransportBlock) obj; + return Arrays.equals(toByteArray(), other.toByteArray()); + } + public static final @NonNull Creator CREATOR = new Creator() { @Override public TransportBlock createFromParcel(Parcel in) { diff --git a/framework/java/android/bluetooth/le/TransportDiscoveryData.java b/framework/java/android/bluetooth/le/TransportDiscoveryData.java index c8e97f9a823..2b52f19798a 100644 --- a/framework/java/android/bluetooth/le/TransportDiscoveryData.java +++ b/framework/java/android/bluetooth/le/TransportDiscoveryData.java @@ -26,6 +26,7 @@ import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -96,6 +97,21 @@ public final class TransportDiscoveryData implements Parcelable { return 0; } + /** + * @hide + */ + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TransportDiscoveryData other = (TransportDiscoveryData) obj; + return Arrays.equals(toByteArray(), other.toByteArray()); + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mTransportDataType); -- GitLab From f29b1fe9a4b51718de37e25d6d61977387a7ae05 Mon Sep 17 00:00:00 2001 From: Himanshu Rawat Date: Thu, 9 Dec 2021 07:15:44 +0000 Subject: [PATCH 1390/1408] Corrected comment on when BT is not disabled on entering airplane mode. Bug: 208945756 Test: Manual Change-Id: I790356dbe8664677c5aef7e784eb3c9b30e790cf --- .../server/bluetooth/BluetoothAirplaneModeListener.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java index 263ff189a28..380b1f37b98 100644 --- a/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java +++ b/service/java/com/android/server/bluetooth/BluetoothAirplaneModeListener.java @@ -112,9 +112,9 @@ class BluetoothAirplaneModeListener { void handleAirplaneModeChange() { if (shouldSkipAirplaneModeChange()) { Log.i(TAG, "Ignore airplane mode change"); - // We have to store Bluetooth state here, so if user turns off Bluetooth - // after airplane mode is turned on, we don't forget to turn on Bluetooth - // when airplane mode turns off. + // Airplane mode enabled when Bluetooth is being used for audio/headering aid. + // Bluetooth is not disabled in such case, only state is changed to + // BLUETOOTH_ON_AIRPLANE mode. mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); if (shouldPopToast()) { -- GitLab From e5db50fddc932560bfe78c82ed79062278768cc6 Mon Sep 17 00:00:00 2001 From: Himanshu Rawat Date: Mon, 13 Dec 2021 03:29:14 +0000 Subject: [PATCH 1391/1408] Global settings for Bluetooth (Settings.Global.BLUETOOTH_ON) must be set to BLUETOOTH_ON_BLUETOOTH whenever non-BLE MESSAGE_ENABLE is handled irrespective of the state. Bug: 208777033 Test: Manual Change-Id: I5b0460f7741cc80fae367bc1cbf4602087a10975 --- .../android/server/bluetooth/BluetoothManagerService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8860a816410..450e9881bef 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -1850,6 +1850,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; + if (isBle == 0) { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); + } + // Use service interface to get the exact state try { mBluetoothLock.readLock().lock(); @@ -1863,7 +1867,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else { Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); mBluetooth.onLeServiceUp(mContext.getAttributionSource()); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } break; case BluetoothAdapter.STATE_BLE_TURNING_ON: -- GitLab From 375c93f52c0230fba3594c2427874566b0c4c7ed Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Mon, 13 Dec 2021 14:32:46 -0800 Subject: [PATCH 1392/1408] Remove unused references to android.app.ActivityThread in BluetoothAdapter Tag: #feature Bug: 210468546 Test: Manual Change-Id: Ia473afe6bcf4d0824e2fd2c6ca2ce562d0ecbde3 --- framework/java/android/bluetooth/BluetoothAdapter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 14be9215c76..6a81c4043a3 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -29,7 +29,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; @@ -991,7 +990,6 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - String packageName = ActivityThread.currentPackageName(); try { return mManagerService.disableBle(mAttributionSource, mToken); } catch (RemoteException e) { @@ -1038,7 +1036,6 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) { return false; } - String packageName = ActivityThread.currentPackageName(); try { return mManagerService.enableBle(mAttributionSource, mToken); } catch (RemoteException e) { -- GitLab From d517127a57b499305f82ec6eea6da8e0bd485e87 Mon Sep 17 00:00:00 2001 From: William Escande Date: Tue, 14 Dec 2021 16:16:11 +0100 Subject: [PATCH 1393/1408] 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 --- .../java/android/bluetooth/Attributable.java | 55 +++++++++++++++++++ .../java/android/bluetooth/BluetoothA2dp.java | 2 - .../android/bluetooth/BluetoothA2dpSink.java | 3 +- .../android/bluetooth/BluetoothAdapter.java | 1 - .../bluetooth/BluetoothAvrcpController.java | 2 - .../android/bluetooth/BluetoothDevice.java | 1 - .../android/bluetooth/BluetoothHeadset.java | 1 - .../bluetooth/BluetoothHeadsetClient.java | 1 - .../bluetooth/BluetoothHeadsetClientCall.java | 1 - .../bluetooth/BluetoothHearingAid.java | 5 +- .../android/bluetooth/BluetoothHidDevice.java | 1 - .../android/bluetooth/BluetoothHidHost.java | 5 +- .../android/bluetooth/BluetoothLeAudio.java | 2 - .../android/bluetooth/BluetoothManager.java | 1 - .../java/android/bluetooth/BluetoothMap.java | 3 +- .../android/bluetooth/BluetoothMapClient.java | 1 - .../java/android/bluetooth/BluetoothPan.java | 2 - .../java/android/bluetooth/BluetoothPbap.java | 1 - .../bluetooth/BluetoothPbapClient.java | 2 - .../java/android/bluetooth/BluetoothSap.java | 1 - .../bluetooth/BluetoothVolumeControl.java | 2 - .../bluetooth/le/BluetoothLeScanner.java | 2 +- .../le/PeriodicAdvertisingManager.java | 2 +- .../java/android/bluetooth/le/ScanResult.java | 2 +- 24 files changed, 63 insertions(+), 36 deletions(-) create mode 100644 framework/java/android/bluetooth/Attributable.java diff --git a/framework/java/android/bluetooth/Attributable.java b/framework/java/android/bluetooth/Attributable.java new file mode 100644 index 00000000000..d9acbe3eefb --- /dev/null +++ b/framework/java/android/bluetooth/Attributable.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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.annotation.NonNull; +import android.annotation.Nullable; +import android.content.AttributionSource; + +import java.util.List; + +/** + * Marker interface for a class which can have an {@link AttributionSource} + * assigned to it; these are typically {@link android.os.Parcelable} classes + * which need to be updated after crossing Binder transaction boundaries. + * + * @hide + */ +public interface Attributable { + void setAttributionSource(@NonNull AttributionSource attributionSource); + + static @Nullable T setAttributionSource( + @Nullable T attributable, + @NonNull AttributionSource attributionSource) { + if (attributable != null) { + attributable.setAttributionSource(attributionSource); + } + return attributable; + } + + static @Nullable List setAttributionSource( + @Nullable List attributableList, + @NonNull AttributionSource attributionSource) { + if (attributableList != null) { + final int size = attributableList.size(); + for (int i = 0; i < size; i++) { + setAttributionSource(attributableList.get(i), attributionSource); + } + } + return attributableList; + } +} diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 1dd32fec251..d66dc637743 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,7 +28,6 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 2dd63a0263f..924dc55f31a 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -20,14 +20,13 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 14be9215c76..362a89d8692 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -48,7 +48,6 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index d27c2764800..536dfb0a66d 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -18,11 +18,9 @@ package android.bluetooth; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6e918bd6243..9ff4dc3bb12 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -32,7 +32,6 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.companion.AssociationRequest; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Build; diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index c0463243f41..3bdfd2859bc 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -27,7 +27,6 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index a5a24708720..2ef37101e23 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -23,7 +23,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java index 3f1ef846125..032b507f5d3 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.os.Build; import android.os.Parcel; diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index 183f4d55bde..a00b20d99f9 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -16,19 +16,16 @@ package android.bluetooth; -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.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index c2744b89aad..f5b444fd17c 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -24,7 +24,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index fb4cbb2eb13..121aa161152 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -21,12 +21,11 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index d7940eb9d3a..34398eba5d2 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -23,10 +23,8 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 20152f3d247..c93de41b5b3 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -26,7 +26,6 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManager; diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 86796519df4..474e41f4aa1 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -21,12 +21,11 @@ import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 042b58669a0..8a3f80164ae 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; diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index 577be3d2aea..ac7a52d8f63 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -16,7 +16,6 @@ package android.bluetooth; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -27,7 +26,6 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 8ee38d3e126..e1379291810 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -24,7 +24,6 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index c7dd6bd9af1..cc91ad258d0 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -20,10 +20,8 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index fda19ed6d0c..ab2b8eaaa36 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -24,7 +24,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java index 678c11a59f3..ba83eca423f 100644 --- a/framework/java/android/bluetooth/BluetoothVolumeControl.java +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -27,8 +27,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; import android.os.Binder; diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java index ee173dbc4ad..f913349e795 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PendingIntent; +import android.bluetooth.Attributable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; import android.bluetooth.IBluetoothGatt; @@ -30,7 +31,6 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java index dea686d18ea..bbd31170bb4 100644 --- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -18,6 +18,7 @@ package android.bluetooth.le; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; +import android.bluetooth.Attributable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.IBluetoothGatt; @@ -25,7 +26,6 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.Attributable; import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java index 52284562848..f437d867ea3 100644 --- a/framework/java/android/bluetooth/le/ScanResult.java +++ b/framework/java/android/bluetooth/le/ScanResult.java @@ -18,8 +18,8 @@ package android.bluetooth.le; import android.annotation.NonNull; import android.annotation.Nullable; +import android.bluetooth.Attributable; import android.bluetooth.BluetoothDevice; -import android.content.Attributable; import android.content.AttributionSource; import android.os.Parcel; import android.os.Parcelable; -- GitLab From 26454b57c77ef192b879d3b9ad24710e393885d2 Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 9 Dec 2021 15:14:14 +0100 Subject: [PATCH 1394/1408] Pbap use profileConnector, resolveSystemService api fix * All profiles use the BluetoothProfileConnector but not Pbap. I reproduced the changes that were made in aosp/932813 for all other profiles. This allow pbap to no longer call resolveSystemService. * `Intent.resolveSystemService` is an hidden API and can no longer be called from Bluetooth as we aim to become mainline. It's code is simple enough to be copied. Tag: #refactor Bug: 200200870 Test: atest BluetoothInstrumentationTests Merged-In: I838b910c633b3ca943fec01f3ccca466ff65f892 Change-Id: I838b910c633b3ca943fec01f3ccca466ff65f892 --- .../java/android/bluetooth/BluetoothPbap.java | 135 +++--------------- .../bluetooth/BluetoothProfileConnector.java | 32 ++++- 2 files changed, 50 insertions(+), 117 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothPbap.java b/framework/java/android/bluetooth/BluetoothPbap.java index 8ee38d3e126..e0a857006f4 100644 --- a/framework/java/android/bluetooth/BluetoothPbap.java +++ b/framework/java/android/bluetooth/BluetoothPbap.java @@ -26,15 +26,10 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -97,10 +92,6 @@ public class BluetoothPbap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothPbap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private final BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; /** @hide */ @@ -114,87 +105,25 @@ public class BluetoothPbap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - log("onBluetoothStateChange: up=" + up); - if (!up) { - doUnbind(); - } else { - doBind(); - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PBAP, "BluetoothPbap", + IBluetoothPbap.class.getName()) { + @Override + public IBluetoothPbap getServiceInterface(IBinder service) { + return IBluetoothPbap.Stub.asInterface(service); } - }; + }; /** * Create a BluetoothPbap proxy object. * * @hide */ - public BluetoothPbap(Context context, ServiceListener l, BluetoothAdapter adapter) { - mContext = context; - mServiceListener = l; + public BluetoothPbap(Context context, ServiceListener listener, BluetoothAdapter adapter) { mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); - - // Preserve legacy compatibility where apps were depending on - // registerStateChangeCallback() performing a permissions check which - // has been relaxed in modern platform versions - if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R - && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Need BLUETOOTH permission"); - } - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "", re); - } - } - doBind(); - } - - @SuppressLint("AndroidFrameworkRequiresPermission") - boolean doBind() { - synchronized (mConnection) { - try { - if (mService == null) { - log("Binding service..."); - Intent intent = new Intent(IBluetoothPbap.class.getName()); - ComponentName comp = intent.resolveSystemService( - mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT)) { - Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); - return false; - } - } - } catch (SecurityException se) { - Log.e(TAG, "", se); - return false; - } - } - return true; - } - - private void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - log("Unbinding service..."); - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException ie) { - Log.e(TAG, "", ie); - } finally { - mService = null; - } - } - } + mProfileConnector.connect(context, listener); } /** @hide */ @@ -215,16 +144,11 @@ public class BluetoothPbap implements BluetoothProfile { * @hide */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "", re); - } - } - doUnbind(); - mServiceListener = null; + mProfileConnector.disconnect(); + } + + private IBluetoothPbap getService() { + return (IBluetoothPbap) mProfileConnector.getService(); } /** @@ -237,7 +161,7 @@ public class BluetoothPbap implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { log("getConnectedDevices()"); - final IBluetoothPbap service = mService; + final IBluetoothPbap service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -266,7 +190,7 @@ public class BluetoothPbap implements BluetoothProfile { public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { log("getConnectionState: device=" + device); try { - final IBluetoothPbap service = mService; + final IBluetoothPbap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { return service.getConnectionState(device, mAttributionSource); } @@ -290,7 +214,7 @@ public class BluetoothPbap implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); - final IBluetoothPbap service = mService; + final IBluetoothPbap service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); return new ArrayList(); @@ -331,7 +255,7 @@ public class BluetoothPbap implements BluetoothProfile { @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); try { - final IBluetoothPbap service = mService; + final IBluetoothPbap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN @@ -360,7 +284,7 @@ public class BluetoothPbap implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { log("disconnect()"); - final IBluetoothPbap service = mService; + final IBluetoothPbap service = getService(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); return false; @@ -374,25 +298,6 @@ public class BluetoothPbap implements BluetoothProfile { return false; } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - log("Proxy object connected"); - mService = IBluetoothPbap.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP, BluetoothPbap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - log("Proxy object disconnected"); - doUnbind(); - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/framework/java/android/bluetooth/BluetoothProfileConnector.java b/framework/java/android/bluetooth/BluetoothProfileConnector.java index ecd5e4077de..79373f1a32e 100644 --- a/framework/java/android/bluetooth/BluetoothProfileConnector.java +++ b/framework/java/android/bluetooth/BluetoothProfileConnector.java @@ -16,12 +16,16 @@ package android.bluetooth; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; @@ -29,6 +33,7 @@ import android.os.UserHandle; import android.util.CloseGuard; import android.util.Log; +import java.util.List; /** * Connector for Bluetooth profile proxies to bind manager service and * profile services @@ -57,6 +62,29 @@ public abstract class BluetoothProfileConnector { } }; + private @Nullable ComponentName resolveSystemService(@NonNull Intent intent, + @NonNull PackageManager pm, @PackageManager.ComponentInfoFlags int flags) { + List results = pm.queryIntentServices(intent, flags); + if (results == null) { + return null; + } + ComponentName comp = null; + for (int i = 0; i < results.size(); i++) { + ResolveInfo ri = results.get(i); + if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + continue; + } + ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName, + ri.serviceInfo.name); + if (comp != null) { + throw new IllegalStateException("Multiple system services handle " + intent + + ": " + comp + ", " + foundComp); + } + comp = foundComp; + } + return comp; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { logDebug("Proxy object connected"); @@ -99,8 +127,8 @@ public abstract class BluetoothProfileConnector { mCloseGuard.open("doUnbind"); try { Intent intent = new Intent(mServiceName); - ComponentName comp = intent.resolveSystemService( - mContext.getPackageManager(), 0); + ComponentName comp = resolveSystemService(intent, mContext.getPackageManager(), + 0); intent.setComponent(comp); if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, UserHandle.CURRENT)) { -- GitLab From cc2d01f2367b0cf54fc2d9686b065156797ce8d9 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Wed, 15 Dec 2021 13:04:11 +0000 Subject: [PATCH 1395/1408] Replaced Bluetooth SystemProperties.set by sysprop Tag: #feature Bug: 197210455 Test: set/get sysprop with SystemProperties Merged-In: I613808dbc930c90e391df9857a173a86aedc4acf Change-Id: I613808dbc930c90e391df9857a173a86aedc4acf --- framework/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 14be9215c76..cc3f747fa82 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -58,7 +58,7 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.SystemProperties; +import android.sysprop.BluetoothProperties; import android.util.Log; import android.util.Pair; @@ -1341,7 +1341,7 @@ public final class BluetoothAdapter { return true; } Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); - SystemProperties.set("persist.bluetooth.factoryreset", "true"); + BluetoothProperties.factory_reset(true); } catch (RemoteException e) { Log.e(TAG, "", e); } finally { -- GitLab From 9cc5b0f8b0f0acd4a55b48378fa77c0f1fea5798 Mon Sep 17 00:00:00 2001 From: Etienne Ruffieux Date: Tue, 14 Dec 2021 18:39:55 +0000 Subject: [PATCH 1396/1408] Moved AttributionSource related APIs in AttributionSource Modified myAttributionSource() to check for global AS for process in ActivityThread and fallback to building new AS with PackageManager#getPackageForUid(myUid()) if null. Tag: #feature Bug: 210467846 Bug: 210468546 Test: build Change-Id: I7aa75395469bf0bb806100420faaf98c52057355 CTS-Coverage-Bug: 210906055 --- .../android/bluetooth/BluetoothAdapter.java | 2 +- .../android/bluetooth/BluetoothDevice.java | 2 +- .../android/bluetooth/BluetoothManager.java | 34 ++----------------- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 14be9215c76..9160edea873 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -788,7 +788,7 @@ public final class BluetoothAdapter { @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null)); + sAdapter = createAdapter(AttributionSource.myAttributionSource()); } return sAdapter; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 6e918bd6243..c889d100a49 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1178,7 +1178,7 @@ public final class BluetoothDevice implements Parcelable, Attributable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; - mAttributionSource = BluetoothManager.resolveAttributionSource(null); + mAttributionSource = AttributionSource.myAttributionSource(); } /** {@hide} */ diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 20152f3d247..2d8625098cf 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.app.ActivityThread; -import android.app.AppGlobals; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Attributable; @@ -69,37 +65,11 @@ public final class BluetoothManager { * @hide */ public BluetoothManager(Context context) { - mAttributionSource = resolveAttributionSource(context); + mAttributionSource = (context != null) ? context.getAttributionSource() : + AttributionSource.myAttributionSource(); mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); } - /** {@hide} */ - public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) { - AttributionSource res = null; - if (context != null) { - res = context.getAttributionSource(); - } - if (res == null) { - res = ActivityThread.currentAttributionSource(); - } - if (res == null) { - int uid = android.os.Process.myUid(); - if (uid == android.os.Process.ROOT_UID) { - uid = android.os.Process.SYSTEM_UID; - } - try { - res = new AttributionSource.Builder(uid) - .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0]) - .build(); - } catch (RemoteException ignored) { - } - } - if (res == null) { - throw new IllegalStateException("Failed to resolve AttributionSource"); - } - return res; - } - /** * Get the BLUETOOTH Adapter for this device. * -- GitLab From b86023dde0fbbe539e2415942ffe1c97108654f8 Mon Sep 17 00:00:00 2001 From: William Escande Date: Thu, 16 Dec 2021 16:07:55 +0100 Subject: [PATCH 1397/1408] 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/BluetoothA2dp.java | 511 ++++++++------- .../android/bluetooth/BluetoothA2dpSink.java | 183 +++--- .../bluetooth/BluetoothAvrcpController.java | 135 ++-- .../BluetoothCsipSetCoordinator.java | 260 ++++---- .../android/bluetooth/BluetoothHeadset.java | 540 +++++++++------- .../bluetooth/BluetoothHeadsetClient.java | 581 ++++++++++-------- .../bluetooth/BluetoothHearingAid.java | 302 +++++---- .../android/bluetooth/BluetoothHidDevice.java | 264 ++++---- .../android/bluetooth/BluetoothHidHost.java | 291 +++++---- .../android/bluetooth/BluetoothLeAudio.java | 307 +++++---- .../java/android/bluetooth/BluetoothMap.java | 185 +++--- .../android/bluetooth/BluetoothMapClient.java | 251 +++++--- .../java/android/bluetooth/BluetoothPan.java | 159 +++-- .../bluetooth/BluetoothPbapClient.java | 157 ++--- .../java/android/bluetooth/BluetoothSap.java | 184 +++--- .../android/bluetooth/BluetoothUtils.java | 41 ++ .../bluetooth/BluetoothVolumeControl.java | 128 ++-- 17 files changed, 2606 insertions(+), 1873 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothUtils.java diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index d66dc637743..8b9cec17a19 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,17 +32,19 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** @@ -271,7 +275,7 @@ public final class BluetoothA2dp implements BluetoothProfile { IBluetoothA2dp.class.getName()) { @Override public IBluetoothA2dp getServiceInterface(IBinder service) { - return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothA2dp.Stub.asInterface(service); } }; @@ -322,17 +326,21 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - return service.connect(device); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.connectWithAttribution(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -364,17 +372,21 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - return service.disconnect(device); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnectWithAttribution(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -385,19 +397,24 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevicesWithAttribution(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getConnectedDevicesWithAttribution(mAttributionSource), + 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -408,20 +425,25 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getDevicesMatchingConnectionStatesWithAttribution(states, + mAttributionSource, recv); return Attributable.setAttributionSource( - service.getDevicesMatchingConnectionStatesWithAttribution(states, - mAttributionSource), + 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -432,18 +454,21 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getConnectionState(device); + final IBluetoothA2dp service = getService(); + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getConnectionStateWithAttribution(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; } + return defaultValue; } /** @@ -471,18 +496,21 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && ((device == null) || isValidDevice(device))) { - return service.setActiveDevice(device, mAttributionSource); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && ((device == null) || isValidDevice(device))) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setActiveDevice(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -499,18 +527,24 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + final BluetoothDevice defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getActiveDevice(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getActiveDevice(mAttributionSource), mAttributionSource); + 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 null; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return null; } + return defaultValue; } /** @@ -555,22 +589,23 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -589,19 +624,7 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - return BluetoothAdapter.connectionPolicyToPriority( - service.getPriority(device, mAttributionSource)); - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; - } + return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); } /** @@ -623,18 +646,21 @@ public final class BluetoothA2dp implements BluetoothProfile { }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getConnectionPolicy(device, mAttributionSource); + final IBluetoothA2dp service = getService(); + final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } + return defaultValue; } /** @@ -646,17 +672,21 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresNoPermission public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { - return service.isAvrcpAbsoluteVolumeSupported(); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isAvrcpAbsoluteVolumeSupported(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; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); - return false; } + return defaultValue; } /** @@ -669,14 +699,16 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { service.setAvrcpAbsoluteVolume(volume, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); } } @@ -689,18 +721,22 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isA2dpPlaying(BluetoothDevice device) { - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.isA2dpPlaying(device, mAttributionSource); + if (DBG) log("isA2dpPlaying(" + device + ")"); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isA2dpPlaying(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -729,8 +765,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Gets the current codec status (configuration and capability). * - * @param device the remote Bluetooth device. If null, use the current - * active A2DP Bluetooth device. + * @param device the remote Bluetooth device. * @return the current codec status * @hide */ @@ -742,26 +777,28 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); verifyDeviceNotNull(device, "getCodecStatus"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { - return service.getCodecStatus(device, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + final IBluetoothA2dp service = getService(); + final BluetoothCodecStatus defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getCodecStatus(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - return null; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); - return null; } + return defaultValue; } /** * Sets the codec configuration preference. * - * @param device the remote Bluetooth device. If null, use the current - * active A2DP Bluetooth device. + * @param device the remote Bluetooth device. * @param codecConfig the codec configuration preference * @hide */ @@ -777,24 +814,23 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); throw new IllegalArgumentException("codecConfig cannot be null"); } - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { service.setCodecConfigPreference(device, codecConfig, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); - return; } } /** * Enables the optional codecs. * - * @param device the remote Bluetooth device. If null, use the currect - * active A2DP Bluetooth device. + * @param device the remote Bluetooth device. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -810,8 +846,7 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Disables the optional codecs. * - * @param device the remote Bluetooth device. If null, use the currect - * active A2DP Bluetooth device. + * @param device the remote Bluetooth device. * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -827,26 +862,25 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Enables or disables the optional codecs. * - * @param device the remote Bluetooth device. If null, use the currect - * active A2DP Bluetooth device. + * @param device the remote Bluetooth device. * @param enable if true, enable the optional codecs, other disable them */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { if (enable) { service.enableOptionalCodecs(device, mAttributionSource); } else { service.disableOptionalCodecs(device, mAttributionSource); } + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); - return; } } @@ -864,18 +898,23 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @OptionalCodecsSupportStatus public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { + if (DBG) log("isOptionalCodecsSupported(" + device + ")"); verifyDeviceNotNull(device, "isOptionalCodecsSupported"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - return service.supportsOptionalCodecs(device, mAttributionSource); + final IBluetoothA2dp service = getService(); + final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.supportsOptionalCodecs(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 OPTIONAL_CODECS_SUPPORT_UNKNOWN; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e); - return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } + return defaultValue; } /** @@ -892,18 +931,23 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @OptionalCodecsPreferenceStatus public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { + if (DBG) log("isOptionalCodecsEnabled(" + device + ")"); verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - return service.getOptionalCodecsEnabled(device, mAttributionSource); + final IBluetoothA2dp service = getService(); + final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getOptionalCodecsEnabled(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 OPTIONAL_CODECS_PREF_UNKNOWN; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e); - return OPTIONAL_CODECS_PREF_UNKNOWN; } + return defaultValue; } /** @@ -921,24 +965,24 @@ public final class BluetoothA2dp implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, @OptionalCodecsPreferenceStatus int value) { + if (DBG) log("setOptionalCodecsEnabled(" + device + ")"); verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); - try { - if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN - && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED - && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { - Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); - return; - } - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { + if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN + && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED + && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { + Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); + return; + } + final IBluetoothA2dp service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { service.setOptionalCodecsEnabled(device, value, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return; } } @@ -961,17 +1005,21 @@ public final class BluetoothA2dp implements BluetoothProfile { }) public @Type int getDynamicBufferSupport() { if (VDBG) log("getDynamicBufferSupport()"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { - return service.getDynamicBufferSupport(mAttributionSource); + final IBluetoothA2dp service = getService(); + final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getDynamicBufferSupport(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 DYNAMIC_BUFFER_SUPPORT_NONE; - } catch (RemoteException e) { - Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e); - return DYNAMIC_BUFFER_SUPPORT_NONE; } + return defaultValue; } /** @@ -992,17 +1040,22 @@ public final class BluetoothA2dp implements BluetoothProfile { }) public @Nullable BufferConstraints getBufferConstraints() { if (VDBG) log("getBufferConstraints()"); - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { - return service.getBufferConstraints(mAttributionSource); + final IBluetoothA2dp service = getService(); + final BufferConstraints defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getBufferConstraints(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 null; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return null; } + return defaultValue; } /** @@ -1027,17 +1080,21 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value); return false; } - try { - final IBluetoothA2dp service = getService(); - if (service != null && isEnabled()) { - return service.setBufferLengthMillis(codec, value, mAttributionSource); + final IBluetoothA2dp service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setBufferLengthMillis(codec, value, 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; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; } + return defaultValue; } /** diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java index 924dc55f31a..59416818ceb 100755 --- a/framework/java/android/bluetooth/BluetoothA2dpSink.java +++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -29,14 +31,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -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.List; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the Bluetooth A2DP Sink @@ -86,7 +90,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { @Override public IBluetoothA2dpSink getServiceInterface(IBinder service) { - return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothA2dpSink.Stub.asInterface(service); } }; @@ -140,16 +144,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -181,16 +189,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothA2dpSink 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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; } /** @@ -204,17 +216,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothA2dpSink 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(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, "Stack:" + 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; } /** @@ -228,18 +246,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothA2dpSink 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(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, "Stack:" + 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; } /** @@ -251,18 +274,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); + if (VDBG) log("getConnectionState(" + device + ")"); final IBluetoothA2dpSink 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } /** @@ -282,16 +309,21 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); final IBluetoothA2dpSink service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final BluetoothAudioConfig defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getAudioConfig(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return null; + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getAudioConfig(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 null; + return defaultValue; } /** @@ -337,20 +369,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink 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(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, "Stack:" + 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; } /** @@ -393,16 +427,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } /** @@ -420,17 +458,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile { android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public boolean isAudioPlaying(@NonNull BluetoothDevice device) { + if (VDBG) log("isAudioPlaying(" + device + ")"); final IBluetoothA2dpSink 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.isA2dpPlaying(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isA2dpPlaying(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; } /** diff --git a/framework/java/android/bluetooth/BluetoothAvrcpController.java b/framework/java/android/bluetooth/BluetoothAvrcpController.java index 536dfb0a66d..81fc3e11e9e 100644 --- a/framework/java/android/bluetooth/BluetoothAvrcpController.java +++ b/framework/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -23,13 +25,15 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; 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.List; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently @@ -93,8 +97,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { @Override public IBluetoothAvrcpController getServiceInterface(IBinder service) { - return IBluetoothAvrcpController.Stub.asInterface( - Binder.allowBlocking(service)); + return IBluetoothAvrcpController.Stub.asInterface(service); } }; @@ -130,19 +133,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothAvrcpController service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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, "Stack:" + 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; } /** @@ -153,20 +161,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothAvrcpController service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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, "Stack:" + 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; } /** @@ -177,18 +189,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothAvrcpController service = getService(); + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } /** @@ -201,17 +216,22 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothAvrcpController service = getService(); + final BluetoothAvrcpPlayerSettings defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - settings = service.getPlayerSettings(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getMetadata() " + e); - return null; + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getPlayerSettings(device, mAttributionSource, recv); + settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } - return settings; + return defaultValue; } /** @@ -222,18 +242,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothAvrcpController service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setPlayerApplicationSetting(plAppSetting, 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; } /** @@ -245,18 +268,20 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - final IBluetoothAvrcpController service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothAvrcpController service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); return; + } 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"); } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java index f0a8df0fa72..ba57ec472a6 100644 --- a/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java +++ b/framework/java/android/bluetooth/BluetoothCsipSetCoordinator.java @@ -17,6 +17,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; @@ -27,13 +29,14 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -41,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the Bluetooth CSIP set coordinator. @@ -229,8 +233,7 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto IBluetoothCsipSetCoordinator.class.getName()) { @Override public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) { - return IBluetoothCsipSetCoordinator.Stub.asInterface( - Binder.allowBlocking(service)); + return IBluetoothCsipSetCoordinator.Stub.asInterface(service); } }; @@ -283,26 +286,27 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto public @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor, @Nullable ClientLockCallback cb) { - if (VDBG) { - log("groupLockSet()"); - } + if (VDBG) log("groupLockSet()"); final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled()) { - IBluetoothCsipSetCoordinatorLockCallback delegate = null; - if ((executor != null) && (cb != null)) { - delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); - } - return service.groupLock(groupId, delegate, mAttributionSource).getUuid(); + final UUID defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + IBluetoothCsipSetCoordinatorLockCallback delegate = null; + if ((executor != null) && (cb != null)) { + delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.groupLock(groupId, delegate, mAttributionSource, recv); + final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + return ret == null ? defaultValue : ret.getUuid(); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - return null; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return null; } + return defaultValue; } /** @@ -315,27 +319,26 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean groupUnlock(@NonNull UUID lockUuid) { - if (VDBG) { - log("groupLockSet()"); - } + if (VDBG) log("groupLockSet()"); if (lockUuid == null) { return false; } - final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled()) { - service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); return true; + } 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -348,22 +351,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) { - if (VDBG) { - log("getGroupUuidMapByDevice()"); - } + if (VDBG) log("getGroupUuidMapByDevice()"); final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled()) { - return service.getGroupUuidMapByDevice(device, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + final Map defaultValue = new HashMap<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getGroupUuidMapByDevice(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - return new HashMap<>(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new HashMap<>(); } + return defaultValue; } /** @@ -376,22 +379,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @NonNull List getAllGroupIds(@Nullable ParcelUuid uuid) { - if (VDBG) { - log("getAllGroupIds()"); - } + if (VDBG) log("getAllGroupIds()"); final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled()) { - return service.getAllGroupIds(uuid, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getAllGroupIds(uuid, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - return new ArrayList(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -399,22 +403,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto */ @Override public @NonNull List getConnectedDevices() { - if (VDBG) { - log("getConnectedDevices()"); - } + if (VDBG) log("getConnectedDevices()"); final IBluetoothCsipSetCoordinator service = getService(); - if (service != null && isEnabled()) { - try { - return service.getConnectedDevices(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } + final List defaultValue = new ArrayList<>(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevices(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return new ArrayList(); + return defaultValue; } /** @@ -422,24 +427,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto */ @Override public - @NonNull List getDevicesMatchingConnectionStates( - @NonNull int[] states) { - if (VDBG) { - log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); - } + @NonNull List getDevicesMatchingConnectionStates(@NonNull int[] states) { + if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); final IBluetoothCsipSetCoordinator service = getService(); - if (service != null && isEnabled()) { - try { - return service.getDevicesMatchingConnectionStates(states, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); - } - } + final List defaultValue = new ArrayList<>(); if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return new ArrayList(); + return defaultValue; } /** @@ -447,24 +452,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto */ @Override public - @BluetoothProfile.BtProfileState int getConnectionState( - @Nullable BluetoothDevice device) { - if (VDBG) { - log("getState(" + device + ")"); - } + @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); final IBluetoothCsipSetCoordinator service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; - } - } + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + 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())); + } } - return BluetoothProfile.STATE_DISCONNECTED; + return defaultValue; } /** @@ -484,26 +488,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy( @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { - if (DBG) { - log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - } + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled() && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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())); } - return false; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -521,22 +523,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { - if (VDBG) { - log("getConnectionPolicy(" + device + ")"); - } + if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothCsipSetCoordinator service = getService(); - try { - if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); + final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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())); } - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } + return defaultValue; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java index 3bdfd2859bc..1b141c9afac 100644 --- a/framework/java/android/bluetooth/BluetoothHeadset.java +++ b/framework/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -31,7 +33,6 @@ import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; -import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -41,8 +42,11 @@ import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** * Public API for controlling the Bluetooth Headset Service. This includes both @@ -479,16 +483,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.connect(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.connectWithAttribution(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; } /** @@ -520,16 +528,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnectWithAttribution(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; } /** @@ -541,18 +553,23 @@ public final class BluetoothHeadset implements BluetoothProfile { public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevicesWithAttribution(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getConnectedDevicesWithAttribution(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; } /** @@ -564,18 +581,23 @@ public final class BluetoothHeadset implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -587,16 +609,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); final IBluetoothHeadset service = mService; - 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getConnectionStateWithAttribution(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; } /** @@ -623,23 +649,7 @@ public final class BluetoothHeadset implements BluetoothProfile { }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - if (priority != BluetoothProfile.PRIORITY_OFF - && priority != BluetoothProfile.PRIORITY_ON) { - return false; - } - try { - return service.setPriority( - device, BluetoothAdapter.priorityToConnectionPolicy(priority), - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); } /** @@ -665,20 +675,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadset service = mService; - 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(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; } /** @@ -698,18 +710,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return BluetoothAdapter.connectionPolicyToPriority( - service.getPriority(device, mAttributionSource)); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.PRIORITY_OFF; - } - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return BluetoothProfile.PRIORITY_OFF; + return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); } /** @@ -732,16 +733,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; - 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(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; } /** @@ -756,15 +761,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) { if (DBG) log("isNoiseReductionSupported()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.isNoiseReductionSupported(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isNoiseReductionSupported(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; } /** @@ -779,15 +789,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) { if (DBG) log("isVoiceRecognitionSupported()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.isVoiceRecognitionSupported(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isVoiceRecognitionSupported(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; } /** @@ -818,15 +833,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.startVoiceRecognition(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; } /** @@ -847,15 +867,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.stopVoiceRecognition(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; } /** @@ -870,15 +895,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean isAudioConnected(BluetoothDevice device) { if (VDBG) log("isAudioConnected()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.isAudioConnected(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isAudioConnected(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; } /** @@ -904,17 +934,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); final IBluetoothHeadset service = mService; - if (service != null && !isDisabled()) { + final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (!isDisabled()) { try { - return service.getAudioState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getAudioState(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + return defaultValue; } /** @@ -932,15 +965,17 @@ public final class BluetoothHeadset implements BluetoothProfile { public void setAudioRouteAllowed(boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - service.setAudioRouteAllowed(allowed, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setAudioRouteAllowed(allowed, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } } @@ -955,17 +990,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean getAudioRouteAllowed() { if (VDBG) log("getAudioRouteAllowed"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getAudioRouteAllowed(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getAudioRouteAllowed(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -980,15 +1018,17 @@ public final class BluetoothHeadset implements BluetoothProfile { public void setForceScoAudio(boolean forced) { if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - service.setForceScoAudio(forced, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setForceScoAudio(forced, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } } @@ -1005,16 +1045,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean isAudioOn() { if (VDBG) log("isAudioOn()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.isAudioOn(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isAudioOn(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; } /** @@ -1039,18 +1083,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio() { + if (VDBG) log("connectAudio()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.connectAudio(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.connectAudio(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1068,18 +1116,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio() { + if (VDBG) log("disconnectAudio()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.disconnectAudio(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnectAudio(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1113,17 +1165,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean startScoUsingVirtualVoiceCall() { if (DBG) log("startScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.startScoUsingVirtualVoiceCall(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.startScoUsingVirtualVoiceCall(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1148,17 +1203,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public boolean stopScoUsingVirtualVoiceCall() { if (DBG) log("stopScoUsingVirtualVoiceCall()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.stopScoUsingVirtualVoiceCall(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1178,16 +1236,16 @@ public final class BluetoothHeadset implements BluetoothProfile { public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type, String name) { final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { service.phoneStateChanged(numActive, numHeld, callState, number, type, name, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } } @@ -1204,16 +1262,18 @@ public final class BluetoothHeadset implements BluetoothProfile { public void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type) { final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); service.clccResponse(index, direction, status, mode, mpty, number, type, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } } @@ -1245,18 +1305,21 @@ public final class BluetoothHeadset implements BluetoothProfile { throw new IllegalArgumentException("command is null"); } final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.sendVendorSpecificResultCode(device, command, arg, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } + final boolean defaultValue = false; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendVendorSpecificResultCode(device, command, arg, + 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; } /** @@ -1290,17 +1353,20 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.d(TAG, "setActiveDevice: " + device); } final IBluetoothHeadset service = mService; - if (service != null && isEnabled() && (device == null || isValidDevice(device))) { - try { - return service.setActiveDevice(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } + final boolean defaultValue = false; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && (device == null || isValidDevice(device))) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setActiveDevice(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; } /** @@ -1316,22 +1382,25 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothDevice getActiveDevice() { - if (VDBG) { - Log.d(TAG, "getActiveDevice"); - } + if (VDBG) Log.d(TAG, "getActiveDevice"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { + final BluetoothDevice defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getActiveDevice(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getActiveDevice(mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + 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 null; + return defaultValue; } /** @@ -1346,21 +1415,22 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled() { - if (DBG) { - log("isInbandRingingEnabled()"); - } + if (DBG) log("isInbandRingingEnabled()"); final IBluetoothHeadset service = mService; - if (service != null && isEnabled()) { - try { - return service.isInbandRingingEnabled(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - } - } + final boolean defaultValue = false; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isInbandRingingEnabled(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; } /** @@ -1380,7 +1450,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service)); + mService = IBluetoothHeadset.Stub.asInterface(service); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_CONNECTED)); } diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index 2ef37101e23..7d7a7f798bb 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -25,15 +27,17 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.Build; import android.os.Bundle; 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.List; +import java.util.concurrent.TimeoutException; /** * Public API to control Hands Free Profile (HFP role only). @@ -432,7 +436,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override public IBluetoothHeadsetClient getServiceInterface(IBinder service) { - return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothHeadsetClient.Stub.asInterface(service); } }; @@ -479,18 +483,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + 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())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -507,18 +514,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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())); - return false; + 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; } /** @@ -531,19 +541,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothHeadsetClient service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -558,20 +573,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothHeadsetClient service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -585,18 +604,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -634,22 +656,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -686,18 +709,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -715,17 +741,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.startVoiceRecognition(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; } /** @@ -739,20 +769,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, - String atCommand) { + public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { if (DBG) log("sendVendorSpecificCommand()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendVendorAtCommand(device, vendorId, atCommand, 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; } /** @@ -770,17 +803,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.stopVoiceRecognition(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; } /** @@ -793,18 +830,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final List defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getCurrentCalls(device, mAttributionSource, recv); return Attributable.setAttributionSource( - service.getCurrentCalls(device, mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + 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 null; + return defaultValue; } /** @@ -817,17 +860,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentAgEvents()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final Bundle defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getCurrentAgEvents(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getCurrentAgEvents(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 null; + return defaultValue; } /** @@ -844,17 +891,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.acceptCall(device, flag, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.acceptCall(device, flag, 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; } /** @@ -868,17 +919,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.holdCall(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.holdCall(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; } /** @@ -897,17 +952,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.rejectCall(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.rejectCall(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; } /** @@ -930,17 +989,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.terminateCall(device, call, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.terminateCall(device, call, 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; } /** @@ -961,17 +1024,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.enterPrivateMode(device, index, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.enterPrivateMode(device, index, 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; } /** @@ -991,17 +1058,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.explicitCallTransfer(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.explicitCallTransfer(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; } /** @@ -1017,18 +1088,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final BluetoothHeadsetClientCall defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.dial(device, number, mAttributionSource, recv); return Attributable.setAttributionSource( - service.dial(device, number, mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + 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 null; + return defaultValue; } /** @@ -1045,17 +1122,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.sendDTMF(device, code, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendDTMF(device, code, 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; } /** @@ -1074,17 +1155,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getLastVoiceTagNumber(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getLastVoiceTagNumber(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; } /** @@ -1097,17 +1182,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothHeadsetClient service = getService(); + final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getAudioState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getAudioState(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + return defaultValue; } return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; } @@ -1123,17 +1212,18 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothHeadsetClient service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - service.setAudioRouteAllowed(device, allowed, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } } @@ -1148,19 +1238,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getAudioRouteAllowed(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getAudioRouteAllowed(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1175,19 +1267,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + if (VDBG) log("connectAudio"); + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.connectAudio(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.connectAudio(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1202,19 +1297,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + if (VDBG) log("disconnectAudio"); + final IBluetoothHeadsetClient service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.disconnectAudio(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnectAudio(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return false; + return defaultValue; } /** @@ -1226,19 +1324,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = - getService(); - if (service != null && isEnabled()) { + if (VDBG) log("getCurrentAgFeatures"); + final IBluetoothHeadsetClient service = getService(); + final Bundle defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getCurrentAgFeatures(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getCurrentAgFeatures(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); } - return null; + return defaultValue; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothHearingAid.java b/framework/java/android/bluetooth/BluetoothHearingAid.java index a00b20d99f9..339a75fe0fb 100644 --- a/framework/java/android/bluetooth/BluetoothHearingAid.java +++ b/framework/java/android/bluetooth/BluetoothHearingAid.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -28,14 +30,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -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.List; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the Hearing Aid profile. @@ -136,7 +140,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { @Override public IBluetoothHearingAid getServiceInterface(IBinder service) { - return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothHearingAid.Stub.asInterface(service); } }; @@ -181,16 +185,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() && isValidDevice(device)) { - return service.connect(device, mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -223,16 +231,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() && isValidDevice(device)) { - return service.disconnect(device, mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -244,17 +256,23 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevices(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getConnectedDevices(mAttributionSource), mAttributionSource); + 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -267,18 +285,23 @@ public final class BluetoothHearingAid implements BluetoothProfile { @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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 | 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -291,17 +314,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getConnectionState(device, mAttributionSource); + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; } + return defaultValue; } /** @@ -330,18 +356,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device, mAttributionSource); - return true; + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && ((device == null) || isValidDevice(device))) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setActiveDevice(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -359,17 +387,23 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List getActiveDevices() { if (VDBG) log("getActiveDevices()"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled()) { + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getActiveDevices(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getActiveDevices(mAttributionSource), mAttributionSource); + 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<>(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList<>(); } + return defaultValue; } /** @@ -416,21 +450,22 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); verifyDeviceNotNull(device, "setConnectionPolicy"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -474,17 +509,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) log("getConnectionPolicy(" + device + ")"); verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getConnectionPolicy(device, mAttributionSource); + final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } + return defaultValue; } /** @@ -519,19 +557,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); - final IBluetoothHearingAid service = getService(); - try { - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setVolume(volume, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - - if (!isEnabled()) return; - - service.setVolume(volume, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } } @@ -552,24 +589,23 @@ public final class BluetoothHearingAid implements BluetoothProfile { android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public long getHiSyncId(@NonNull BluetoothDevice device) { - if (VDBG) { - log("getHiSyncId(" + device + ")"); - } + if (VDBG) log("getHiSyncId(" + device + ")"); verifyDeviceNotNull(device, "getConnectionPolicy"); final IBluetoothHearingAid service = getService(); - try { - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return HI_SYNC_ID_INVALID; + final long defaultValue = HI_SYNC_ID_INVALID; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getHiSyncId(device, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - - if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - - return service.getHiSyncId(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return HI_SYNC_ID_INVALID; } + return defaultValue; } /** @@ -583,21 +619,22 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getDeviceSide(BluetoothDevice device) { - if (VDBG) { - log("getDeviceSide(" + device + ")"); - } + if (VDBG) log("getDeviceSide(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getDeviceSide(device, mAttributionSource); + final int defaultValue = SIDE_LEFT; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getDeviceSide(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 SIDE_LEFT; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return SIDE_LEFT; } + return defaultValue; } /** @@ -611,21 +648,22 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getDeviceMode(BluetoothDevice device) { - if (VDBG) { - log("getDeviceMode(" + device + ")"); - } + if (VDBG) log("getDeviceMode(" + device + ")"); final IBluetoothHearingAid service = getService(); - try { - if (service != null && isEnabled() - && isValidDevice(device)) { - return service.getDeviceMode(device, mAttributionSource); + final int defaultValue = MODE_MONAURAL; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getDeviceMode(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 MODE_MONAURAL; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return MODE_MONAURAL; } + return defaultValue; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothHidDevice.java b/framework/java/android/bluetooth/BluetoothHidDevice.java index f5b444fd17c..44a355b5f75 100644 --- a/framework/java/android/bluetooth/BluetoothHidDevice.java +++ b/framework/java/android/bluetooth/BluetoothHidDevice.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -26,14 +28,16 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; 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.List; import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; /** * Provides the public APIs to control the Bluetooth HID Device profile. @@ -431,7 +435,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { @Override public IBluetoothHidDevice getServiceInterface(IBinder service) { - return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothHidDevice.Stub.asInterface(service); } }; @@ -455,18 +459,23 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getConnectedDevices() { final IBluetoothHidDevice service = getService(); - if (service != null) { + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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, e.toString()); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), + mAttributionSource); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return new ArrayList<>(); + return defaultValue; } /** {@inheritDoc} */ @@ -475,19 +484,23 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List getDevicesMatchingConnectionStates(int[] states) { final IBluetoothHidDevice service = getService(); - if (service != null) { + final List defaultValue = new ArrayList<>(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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, e.toString()); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return new ArrayList<>(); + return defaultValue; } /** {@inheritDoc} */ @@ -496,17 +509,20 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { final IBluetoothHidDevice service = getService(); - if (service != null) { + final int defaultValue = STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + 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())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return STATE_DISCONNECTED; + return defaultValue; } /** @@ -555,18 +571,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { } final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = result; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource); - result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv); + result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -582,20 +601,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean unregisterApp() { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - result = service.unregisterApp(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.unregisterApp(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -609,20 +629,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendReport(BluetoothDevice device, int id, byte[] data) { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - result = service.sendReport(device, id, data, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendReport(device, id, data, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -637,20 +658,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - result = service.replyReport(device, type, id, data, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.replyReport(device, type, id, data, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -663,20 +685,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean reportError(BluetoothDevice device, byte error) { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - result = service.reportError(device, error, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.reportError(device, error, mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -689,18 +712,20 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getUserAppName() { final IBluetoothHidDevice service = getService(); - - if (service != null) { + final String defaultValue = ""; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.getUserAppName(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getUserAppName(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return ""; + return defaultValue; } /** @@ -714,20 +739,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(BluetoothDevice device) { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - result = service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + 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())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -740,20 +766,21 @@ public final class BluetoothHidDevice implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(BluetoothDevice device) { - boolean result = false; - final IBluetoothHidDevice service = getService(); - if (service != null) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - result = service.disconnect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + 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())); } - } else { - Log.w(TAG, "Proxy not attached to service"); } - - return result; + return defaultValue; } /** @@ -781,23 +808,24 @@ public final class BluetoothHidDevice implements BluetoothProfile { }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { - log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - try { - final IBluetoothHidDevice service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + final IBluetoothHidDevice service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } private boolean isEnabled() { diff --git a/framework/java/android/bluetooth/BluetoothHidHost.java b/framework/java/android/bluetooth/BluetoothHidHost.java index 121aa161152..ecbeddf2b85 100644 --- a/framework/java/android/bluetooth/BluetoothHidHost.java +++ b/framework/java/android/bluetooth/BluetoothHidHost.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -28,13 +30,15 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; 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.List; +import java.util.concurrent.TimeoutException; /** @@ -244,7 +248,7 @@ public final class BluetoothHidHost implements BluetoothProfile { "BluetoothHidHost", IBluetoothHidHost.class.getName()) { @Override public IBluetoothHidHost getServiceInterface(IBinder service) { - return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothHidHost.Stub.asInterface(service); } }; @@ -292,16 +296,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -334,16 +342,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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; } /** @@ -358,17 +370,23 @@ public final class BluetoothHidHost implements BluetoothProfile { public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost 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(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, "Stack:" + 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; } /** @@ -382,18 +400,23 @@ public final class BluetoothHidHost implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothHidHost 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(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, "Stack:" + 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; } /** @@ -411,16 +434,20 @@ public final class BluetoothHidHost implements BluetoothProfile { throw new IllegalArgumentException("device must not be null"); } final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } /** @@ -469,20 +496,22 @@ public final class BluetoothHidHost implements BluetoothProfile { throw new IllegalArgumentException("device must not be null"); } final IBluetoothHidHost 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(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, "Stack:" + 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; } /** @@ -528,16 +557,20 @@ public final class BluetoothHidHost implements BluetoothProfile { throw new IllegalArgumentException("device must not be null"); } final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } private boolean isEnabled() { @@ -561,18 +594,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.virtualUnplug(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.virtualUnplug(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; } /** @@ -588,16 +623,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getProtocolMode(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getProtocolMode(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; } /** @@ -613,16 +652,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.setProtocolMode(device, protocolMode, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setProtocolMode(device, protocolMode, 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; } /** @@ -645,17 +688,21 @@ public final class BluetoothHidHost implements BluetoothProfile { + "bufferSize=" + bufferSize); } final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getReport(device, reportType, reportId, bufferSize, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getReport(device, reportType, reportId, bufferSize, 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; } /** @@ -673,16 +720,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.setReport(device, reportType, report, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setReport(device, reportType, report, 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; } /** @@ -699,16 +750,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.sendData(device, report, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.sendData(device, report, 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; } /** @@ -724,16 +779,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getIdleTime(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getIdleTime(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; } /** @@ -750,16 +809,20 @@ public final class BluetoothHidHost implements BluetoothProfile { public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); final IBluetoothHidHost 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.setIdleTime(device, idleTime, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setIdleTime(device, idleTime, 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; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java index 34398eba5d2..15db686b3be 100644 --- a/framework/java/android/bluetooth/BluetoothLeAudio.java +++ b/framework/java/android/bluetooth/BluetoothLeAudio.java @@ -17,6 +17,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,14 +29,16 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the LeAudio profile. @@ -331,7 +335,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { IBluetoothLeAudio.class.getName()) { @Override public IBluetoothLeAudio getServiceInterface(IBinder service) { - return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothLeAudio.Stub.asInterface(service); } }; @@ -385,17 +389,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(@Nullable BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.connect(device, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.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())); } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -425,17 +433,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(@Nullable BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.disconnect(device, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -446,18 +458,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled()) { + final IBluetoothLeAudio service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getConnectedDevices(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getConnectedDevices(mAttributionSource), mAttributionSource); + 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -469,19 +487,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public @NonNull List getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled()) { + final IBluetoothLeAudio service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.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 | 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(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList(); } + return defaultValue; } /** @@ -493,18 +516,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() - && isValidDevice(device)) { - return service.getConnectionState(device, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; } + return defaultValue; } /** @@ -531,19 +557,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() - && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device, mAttributionSource); - return true; + final IBluetoothLeAudio service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setActiveDevice(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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -557,19 +585,25 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getActiveDevices() { - if (VDBG) log("getActiveDevices()"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled()) { + if (VDBG) log("getActiveDevice()"); + final IBluetoothLeAudio service = getService(); + final List defaultValue = new ArrayList(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver> recv = + new SynchronousResultReceiver(); + service.getActiveDevices(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getActiveDevices(mAttributionSource), mAttributionSource); + 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<>(); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return new ArrayList<>(); } + return defaultValue; } /** @@ -583,17 +617,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull BluetoothDevice device) { if (VDBG) log("getGroupId()"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled()) { - return service.getGroupId(device, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final int defaultValue = GROUP_ID_INVALID; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getGroupId(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 GROUP_ID_INVALID; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return GROUP_ID_INVALID; } + return defaultValue; } /** @@ -606,17 +644,18 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(int volume) { if (VDBG) log("setVolume(vol: " + volume + " )"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled()) { - service.setVolume(volume, mAttributionSource); - return; + final IBluetoothLeAudio service = getService(); + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setVolume(volume, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return; } } @@ -635,16 +674,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) { if (VDBG) log("groupAddNode()"); final IBluetoothLeAudio service = getService(); - try { - if (service != null && mAdapter.isEnabled()) { - return service.groupAddNode(group_id, device, mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.groupAddNode(group_id, 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -663,16 +706,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) { if (VDBG) log("groupRemoveNode()"); final IBluetoothLeAudio service = getService(); - try { - if (service != null && mAdapter.isEnabled()) { - return service.groupRemoveNode(group_id, device, mAttributionSource); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.groupRemoveNode(group_id, 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -695,22 +742,23 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -728,18 +776,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); - try { - final IBluetoothLeAudio service = getService(); - if (service != null && mAdapter.isEnabled() - && isValidDevice(device)) { - return service.getConnectionPolicy(device, mAttributionSource); + final IBluetoothLeAudio service = getService(); + final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (mAdapter.isEnabled() && isValidDevice(device)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; } + return defaultValue; } diff --git a/framework/java/android/bluetooth/BluetoothMap.java b/framework/java/android/bluetooth/BluetoothMap.java index 474e41f4aa1..56e49726242 100644 --- a/framework/java/android/bluetooth/BluetoothMap.java +++ b/framework/java/android/bluetooth/BluetoothMap.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresNoPermission; @@ -28,15 +30,17 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** * This class provides the APIs to control the Bluetooth MAP @@ -87,7 +91,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { "BluetoothMap", IBluetoothMap.class.getName()) { @Override public IBluetoothMap getServiceInterface(IBinder service) { - return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothMap.Stub.asInterface(service); } }; @@ -142,17 +146,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public int getState() { if (VDBG) log("getState()"); final IBluetoothMap service = getService(); - if (service != null) { - try { - return service.getState(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + final int defaultValue = BluetoothMap.STATE_ERROR; + if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getState(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return BluetoothMap.STATE_ERROR; + return defaultValue; } /** @@ -168,18 +175,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public BluetoothDevice getClient() { if (VDBG) log("getClient()"); final IBluetoothMap service = getService(); - if (service != null) { + final BluetoothDevice defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getClient(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getClient(mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), + mAttributionSource); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); } - return null; + return defaultValue; } /** @@ -194,17 +206,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); final IBluetoothMap 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + 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; } /** @@ -233,16 +248,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothMap 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(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())); - return false; + 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; } /** @@ -284,17 +303,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap 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(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; } /** @@ -309,18 +334,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); final IBluetoothMap 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(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; } /** @@ -335,16 +365,21 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); final IBluetoothMap 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(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; } /** @@ -390,20 +425,22 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap 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(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; } /** @@ -446,16 +483,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap 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(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; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothMapClient.java b/framework/java/android/bluetooth/BluetoothMapClient.java index 8a3f80164ae..03536f9aad3 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() { diff --git a/framework/java/android/bluetooth/BluetoothPan.java b/framework/java/android/bluetooth/BluetoothPan.java index ac7a52d8f63..d4ad4ef47ac 100644 --- a/framework/java/android/bluetooth/BluetoothPan.java +++ b/framework/java/android/bluetooth/BluetoothPan.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -28,16 +30,18 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -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.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** * This class provides the APIs to control the Bluetooth Pan @@ -188,7 +192,7 @@ public final class BluetoothPan implements BluetoothProfile { "BluetoothPan", IBluetoothPan.class.getName()) { @Override public IBluetoothPan getServiceInterface(IBinder service) { - return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothPan.Stub.asInterface(service); } }; @@ -249,16 +253,20 @@ public final class BluetoothPan implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothPan 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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())); } } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; + return defaultValue; } /** @@ -289,16 +297,20 @@ public final class BluetoothPan implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothPan 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; + 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; } /** @@ -322,22 +334,23 @@ public final class BluetoothPan implements BluetoothProfile { public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - try { - final IBluetoothPan service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + final IBluetoothPan service = getService(); + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; } + return defaultValue; } /** @@ -354,17 +367,23 @@ public final class BluetoothPan implements BluetoothProfile { public @NonNull List getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothPan 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(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, "Stack:" + 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; } /** @@ -381,18 +400,23 @@ public final class BluetoothPan implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothPan 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(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, "Stack:" + 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; } /** @@ -409,16 +433,20 @@ public final class BluetoothPan implements BluetoothProfile { public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan 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(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + 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; } /** @@ -438,11 +466,16 @@ public final class BluetoothPan implements BluetoothProfile { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); final IBluetoothPan service = getService(); - if (service != null && isEnabled()) { + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - service.setBluetoothTethering(value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setBluetoothTethering(value, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } } @@ -459,14 +492,20 @@ public final class BluetoothPan implements BluetoothProfile { public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); - if (service != null && isEnabled()) { + final boolean defaultValue = false; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { - return service.isTetheringOn(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.isTetheringOn(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; } @UnsupportedAppUsage diff --git a/framework/java/android/bluetooth/BluetoothPbapClient.java b/framework/java/android/bluetooth/BluetoothPbapClient.java index cc91ad258d0..e096de8cb82 100644 --- a/framework/java/android/bluetooth/BluetoothPbapClient.java +++ b/framework/java/android/bluetooth/BluetoothPbapClient.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -24,13 +26,15 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; 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.List; +import java.util.concurrent.TimeoutException; /** * This class provides the APIs to control the Bluetooth PBAP Client Profile. @@ -64,7 +68,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { @Override public IBluetoothPbapClient getServiceInterface(IBinder service) { - return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothPbapClient.Stub.asInterface(service); } }; @@ -123,18 +127,20 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("connect(" + device + ") for PBAP Client."); } final IBluetoothPbapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.connect(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; - } - } + final boolean defaultValue = false; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(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; } /** @@ -155,19 +161,21 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("disconnect(" + device + ")" + new Exception()); } final IBluetoothPbapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { + final boolean defaultValue = true; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { try { - service.disconnect(device, mAttributionSource); + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.disconnect(device, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); return true; - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; + } 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; } /** @@ -184,19 +192,23 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("getConnectedDevices()"); } final IBluetoothPbapClient 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(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; } /** @@ -212,20 +224,23 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("getDevicesMatchingStates()"); } final IBluetoothPbapClient 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(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; } /** @@ -241,18 +256,20 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("getConnectionState(" + device + ")"); } final IBluetoothPbapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - try { - return service.getConnectionState(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return BluetoothProfile.STATE_DISCONNECTED; - } - } + final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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())); + } } - return BluetoothProfile.STATE_DISCONNECTED; + return defaultValue; } private static void log(String msg) { @@ -311,22 +328,22 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); } final IBluetoothPbapClient service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - try { - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return false; - } - } + final boolean defaultValue = false; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device) + && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { + try { + 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())); + } } - return false; + return defaultValue; } /** @@ -370,17 +387,19 @@ public final class BluetoothPbapClient implements BluetoothProfile { log("getConnectionPolicy(" + device + ")"); } final IBluetoothPbapClient service = getService(); - if (service != null && 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 int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; if (service == null) { Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled() && isValidDevice(device)) { + try { + 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())); + } } - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return defaultValue; } } diff --git a/framework/java/android/bluetooth/BluetoothSap.java b/framework/java/android/bluetooth/BluetoothSap.java index ab2b8eaaa36..808fa391331 100644 --- a/framework/java/android/bluetooth/BluetoothSap.java +++ b/framework/java/android/bluetooth/BluetoothSap.java @@ -16,6 +16,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; @@ -26,14 +28,16 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.AttributionSource; import android.content.Context; -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.List; +import java.util.concurrent.TimeoutException; /** * This class provides the APIs to control the Bluetooth SIM @@ -104,7 +108,7 @@ public final class BluetoothSap implements BluetoothProfile { "BluetoothSap", IBluetoothSap.class.getName()) { @Override public IBluetoothSap getServiceInterface(IBinder service) { - return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothSap.Stub.asInterface(service); } }; @@ -155,17 +159,20 @@ public final class BluetoothSap implements BluetoothProfile { public int getState() { if (VDBG) log("getState()"); final IBluetoothSap service = getService(); - if (service != null) { - try { - return service.getState(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { + final int defaultValue = BluetoothSap.STATE_ERROR; + if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.getState(mAttributionSource, recv); + return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } } - return BluetoothSap.STATE_ERROR; + return defaultValue; } /** @@ -180,18 +187,23 @@ public final class BluetoothSap implements BluetoothProfile { public BluetoothDevice getClient() { if (VDBG) log("getClient()"); final IBluetoothSap service = getService(); - if (service != null) { + final BluetoothDevice defaultValue = null; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { try { + final SynchronousResultReceiver recv = + new SynchronousResultReceiver(); + service.getClient(mAttributionSource, recv); return Attributable.setAttributionSource( - service.getClient(mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), + mAttributionSource); + } catch (RemoteException | TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } - } else { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); } - return null; + return defaultValue; } /** @@ -206,17 +218,20 @@ public final class BluetoothSap implements BluetoothProfile { public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); final IBluetoothSap 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(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; } /** @@ -244,16 +259,20 @@ public final class BluetoothSap implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothSap 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(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())); - return false; + 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; } /** @@ -267,17 +286,23 @@ public final class BluetoothSap implements BluetoothProfile { public List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothSap 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(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; } /** @@ -291,18 +316,23 @@ public final class BluetoothSap implements BluetoothProfile { public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); final IBluetoothSap 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(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; } /** @@ -316,16 +346,20 @@ public final class BluetoothSap implements BluetoothProfile { public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); final IBluetoothSap 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(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; } /** @@ -370,20 +404,22 @@ public final class BluetoothSap implements BluetoothProfile { @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothSap 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(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; } /** @@ -425,16 +461,20 @@ public final class BluetoothSap implements BluetoothProfile { public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap 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(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; } private static void log(String msg) { diff --git a/framework/java/android/bluetooth/BluetoothUtils.java b/framework/java/android/bluetooth/BluetoothUtils.java new file mode 100644 index 00000000000..867469241f8 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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 java.time.Duration; + +/** + * {@hide} + */ +public final class BluetoothUtils { + /** + * This utility class cannot be instantiated + */ + private BluetoothUtils() {} + + /** + * Timeout value for synchronous binder call + */ + private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5); + + /** + * @return timeout value for synchronous binder call + */ + static Duration getSyncTimeout() { + return SYNC_CALLS_TIMEOUT; + } +} diff --git a/framework/java/android/bluetooth/BluetoothVolumeControl.java b/framework/java/android/bluetooth/BluetoothVolumeControl.java index ba83eca423f..27532aabc3f 100644 --- a/framework/java/android/bluetooth/BluetoothVolumeControl.java +++ b/framework/java/android/bluetooth/BluetoothVolumeControl.java @@ -17,6 +17,8 @@ package android.bluetooth; +import static android.bluetooth.BluetoothUtils.getSyncTimeout; + import android.Manifest; import android.annotation.IntRange; import android.annotation.NonNull; @@ -29,14 +31,16 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.content.AttributionSource; import android.content.Context; -import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; +import com.android.modules.utils.SynchronousResultReceiver; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeoutException; /** * This class provides the public APIs to control the Bluetooth Volume Control service. @@ -86,7 +90,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose IBluetoothVolumeControl.class.getName()) { @Override public IBluetoothVolumeControl getServiceInterface(IBinder service) { - return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service)); + return IBluetoothVolumeControl.Stub.asInterface(service); } }; @@ -134,17 +138,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose public @NonNull List getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothVolumeControl 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(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; } /** @@ -159,18 +169,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose public List getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); final IBluetoothVolumeControl 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(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; } /** @@ -185,16 +200,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); final IBluetoothVolumeControl 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(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; } /** @@ -212,18 +231,19 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose }) public void setVolume(@Nullable BluetoothDevice device, @IntRange(from = 0, to = 255) int volume) { - if (DBG) - log("setVolume(" + volume + ")"); + if (DBG) log("setVolume(" + volume + ")"); final IBluetoothVolumeControl service = getService(); - try { - if (service != null && isEnabled()) { - service.setVolume(device, volume, mAttributionSource); - return; + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) log(Log.getStackTraceString(new Throwable())); + } else if (isEnabled()) { + try { + final SynchronousResultReceiver recv = new SynchronousResultReceiver(); + service.setVolume(device, volume, mAttributionSource, recv); + recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); + } 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"); - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } } @@ -249,20 +269,22 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothVolumeControl 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(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; } /** @@ -285,16 +307,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothVolumeControl 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(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; } private boolean isEnabled() { -- GitLab From fbbb005a03fa7fb0fda87afd7d6015d44cae7a3c Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Tue, 17 Nov 2020 09:11:17 +0000 Subject: [PATCH 1398/1408] gtbs: Add Generic Telephone Bearer Service support Tag: #feature Bug: 159786353 Sponsor: jpawlowski@ Test: build Change-Id: I8264ade4b07df17fc0207437dfdcae7028cc20ff --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothLeCall.java | 285 ++++++ .../bluetooth/BluetoothLeCallControl.java | 911 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- .../bluetooth/BluetoothManagerService.java | 9 +- 5 files changed, 1218 insertions(+), 3 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeCall.java create mode 100644 framework/java/android/bluetooth/BluetoothLeCallControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index b23c3eb0cea..991da1da1d4 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3086,6 +3086,9 @@ public final class BluetoothAdapter { BluetoothCsipSetCoordinator csipSetCoordinator = new BluetoothCsipSetCoordinator(context, listener, this); return true; + } else if (profile == BluetoothProfile.LE_CALL_CONTROL) { + BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener); + return true; } else { return false; } @@ -3188,6 +3191,10 @@ public final class BluetoothAdapter { (BluetoothCsipSetCoordinator) proxy; csipSetCoordinator.close(); break; + case BluetoothProfile.LE_CALL_CONTROL: + BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy; + tbs.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothLeCall.java b/framework/java/android/bluetooth/BluetoothLeCall.java new file mode 100644 index 00000000000..fb7789db25c --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeCall.java @@ -0,0 +1,285 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.UUID; + +/** + * Representation of Call + * + * @hide + */ +public final class BluetoothLeCall implements Parcelable { + + /** @hide */ + @IntDef(prefix = "STATE_", value = { + STATE_INCOMING, + STATE_DIALING, + STATE_ALERTING, + STATE_ACTIVE, + STATE_LOCALLY_HELD, + STATE_REMOTELY_HELD, + STATE_LOCALLY_AND_REMOTELY_HELD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + /** + * A remote party is calling (incoming call). + * + * @hide + */ + public static final int STATE_INCOMING = 0x00; + + /** + * The process to call the remote party has started but the remote party is not + * being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_DIALING = 0x01; + + /** + * A remote party is being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_ALERTING = 0x02; + + /** + * The call is in an active conversation. + * + * @hide + */ + public static final int STATE_ACTIVE = 0x03; + + /** + * The call is connected but held locally. “Locally Held” implies that either + * the server or the client can affect the state. + * + * @hide + */ + public static final int STATE_LOCALLY_HELD = 0x04; + + /** + * The call is connected but held remotely. “Remotely Held” means that the state + * is controlled by the remote party of a call. + * + * @hide + */ + public static final int STATE_REMOTELY_HELD = 0x05; + + /** + * The call is connected but held both locally and remotely. + * + * @hide + */ + public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06; + + /** + * Whether the call direction is outgoing. + * + * @hide + */ + public static final int FLAG_OUTGOING_CALL = 0x00000001; + + /** + * Whether the call URI and Friendly Name are withheld by server. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002; + + /** + * Whether the call URI and Friendly Name are withheld by network. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004; + + /** Unique UUID that identifies this call */ + private UUID mUuid; + + /** Remote Caller URI */ + private String mUri; + + /** Caller friendly name */ + private String mFriendlyName; + + /** Call state */ + private @State int mState; + + /** Call flags */ + private int mCallFlags; + + /** @hide */ + public BluetoothLeCall(@NonNull BluetoothLeCall that) { + mUuid = new UUID(that.getUuid().getMostSignificantBits(), + that.getUuid().getLeastSignificantBits()); + mUri = that.mUri; + mFriendlyName = that.mFriendlyName; + mState = that.mState; + mCallFlags = that.mCallFlags; + } + + /** @hide */ + public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName, + @State int state, int callFlags) { + mUuid = uuid; + mUri = uri; + mFriendlyName = friendlyName; + mState = state; + mCallFlags = callFlags; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + BluetoothLeCall that = (BluetoothLeCall) o; + return mUuid.equals(that.mUuid) && mUri.equals(that.mUri) + && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState + && mCallFlags == that.mCallFlags; + } + + @Override + public int hashCode() { + return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags); + } + + /** + * Returns a string representation of this BluetoothLeCall. + * + *

    + * Currently this is the UUID. + * + * @return string representation of this BluetoothLeCall + */ + @Override + public String toString() { + return mUuid.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeString(mUri); + out.writeString(mFriendlyName); + out.writeInt(mState); + out.writeInt(mCallFlags); + } + + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothLeCall createFromParcel(Parcel in) { + return new BluetoothLeCall(in); + } + + public BluetoothLeCall[] newArray(int size) { + return new BluetoothLeCall[size]; + } + }; + + private BluetoothLeCall(Parcel in) { + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); + mUri = in.readString(); + mFriendlyName = in.readString(); + mState = in.readInt(); + mCallFlags = in.readInt(); + } + + /** + * Returns an UUID of this BluetoothLeCall. + * + *

    + * An UUID is unique identifier of a BluetoothLeCall. + * + * @return UUID of this BluetoothLeCall + * @hide + */ + public @NonNull UUID getUuid() { + return mUuid; + } + + /** + * Returns a URI of the remote party of this BluetoothLeCall. + * + * @return string representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getUri() { + return mUri; + } + + /** + * Returns a friendly name of the call. + * + * @return friendly name representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getFriendlyName() { + return mFriendlyName; + } + + /** + * Returns the call state. + * + * @return the state of this BluetoothLeCall + * @hide + */ + public @State int getState() { + return mState; + } + + /** + * Returns the call flags. + * + * @return call flags + * @hide + */ + public int getCallFlags() { + return mCallFlags; + } + + /** + * Whether the call direction is incoming. + * + * @return true if incoming call, false otherwise + * @hide + */ + public boolean isIncomingCall() { + return (mCallFlags & FLAG_OUTGOING_CALL) == 0; + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeCallControl.java b/framework/java/android/bluetooth/BluetoothLeCallControl.java new file mode 100644 index 00000000000..5283e080425 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeCallControl.java @@ -0,0 +1,911 @@ +/* + * Copyright 2019 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; +import android.annotation.SuppressLint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the APIs to control the Call Control profile. + * + *

    + * This class provides Bluetooth Telephone Bearer Service functionality, + * allowing applications to expose a GATT Service based interface to control the + * state of the calls by remote devices such as LE audio devices. + * + *

    + * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothLeCallControl proxy object. + * + * @hide + */ +public final class BluetoothLeCallControl implements BluetoothProfile { + private static final String TAG = "BluetoothLeCallControl"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** @hide */ + @IntDef(prefix = "RESULT_", value = { + RESULT_SUCCESS, + RESULT_ERROR_UNKNOWN_CALL_ID, + RESULT_ERROR_INVALID_URI, + RESULT_ERROR_APPLICATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Result { + } + + /** + * Opcode write was successful. + * + * @hide + */ + public static final int RESULT_SUCCESS = 0; + + /** + * Unknown call Id has been used in the operation. + * + * @hide + */ + public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1; + + /** + * The URI provided in {@link Callback#onPlaceCallRequest} is invalid. + * + * @hide + */ + public static final int RESULT_ERROR_INVALID_URI = 2; + + /** + * Application internal error. + * + * @hide + */ + public static final int RESULT_ERROR_APPLICATION = 3; + + /** @hide */ + @IntDef(prefix = "TERMINATION_REASON_", value = { + TERMINATION_REASON_INVALID_URI, + TERMINATION_REASON_FAIL, + TERMINATION_REASON_REMOTE_HANGUP, + TERMINATION_REASON_SERVER_HANGUP, + TERMINATION_REASON_LINE_BUSY, + TERMINATION_REASON_NETWORK_CONGESTION, + TERMINATION_REASON_CLIENT_HANGUP, + TERMINATION_REASON_NO_SERVICE, + TERMINATION_REASON_NO_ANSWER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TerminationReason { + } + + /** + * Remote Caller ID value used to place a call was formed improperly. + * + * @hide + */ + public static final int TERMINATION_REASON_INVALID_URI = 0x00; + + /** + * Call fail. + * + * @hide + */ + public static final int TERMINATION_REASON_FAIL = 0x01; + + /** + * Remote party ended call. + * + * @hide + */ + public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02; + + /** + * Call ended from the server. + * + * @hide + */ + public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03; + + /** + * Line busy. + * + * @hide + */ + public static final int TERMINATION_REASON_LINE_BUSY = 0x04; + + /** + * Network congestion. + * + * @hide + */ + public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05; + + /** + * Client terminated. + * + * @hide + */ + public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06; + + /** + * No service. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_SERVICE = 0x07; + + /** + * No answer. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_ANSWER = 0x08; + + /* + * Flag indicating support for hold/unhold call feature. + * + * @hide + */ + public static final int CAPABILITY_HOLD_CALL = 0x00000001; + + /** + * Flag indicating support for joining calls feature. + * + * @hide + */ + public static final int CAPABILITY_JOIN_CALLS = 0x00000002; + + private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102; + private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103; + + private static final int REG_TIMEOUT = 10000; + + /** + * The template class is used to call callback functions on events from the TBS + * server. Callback functions are wrapped in this class and registered to the + * Android system during app registration. + * + * @hide + */ + public abstract static class Callback { + + private static final String TAG = "BluetoothLeCallControl.Callback"; + + /** + * Called when a remote client requested to accept the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be accepted + * @hide + */ + public abstract void onAcceptCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to terminate the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to terminate + * @hide + */ + public abstract void onTerminateCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to hold the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be put on hold + * @hide + */ + public void onHoldCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to unhold the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to unhold + * @hide + */ + public void onUnholdCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to place a call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The Id to be assigned for the new call + * @param uri The caller URI requested + * @hide + */ + public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri); + + /** + * A remote client has requested to join the calls. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callIds The call Id list requested to join + * @hide + */ + public void onJoinCalls(int requestId, @NonNull List callIds) { + Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!"); + } + } + + private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub { + + private final Executor mExecutor; + private final Callback mCallback; + + CallbackWrapper(Executor executor, Callback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onBearerRegistered(int ccid) { + synchronized (mServerIfLock) { + if (mCallback != null) { + mCcid = ccid; + mServerIfLock.notifyAll(); + } else { + // registration timeout + Log.e(TAG, "onBearerRegistered: mCallback is null"); + } + } + } + + @Override + public void onAcceptCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onTerminateCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onHoldCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onUnholdCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onJoinCalls(int requestId, List parcelUuids) { + List uuids = new ArrayList<>(); + for (ParcelUuid parcelUuid : parcelUuids) { + uuids.add(parcelUuid.getUuid()); + } + + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + private Context mContext; + private ServiceListener mServiceListener; + private volatile IBluetoothLeCallControl mService; + private BluetoothAdapter mAdapter; + private int mCcid = 0; + private String mToken; + private Callback mCallback = null; + private Object mServerIfLock = new Object(); + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) + Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + doUnbind(); + } else { + doBind(); + } + } + }; + + /** + * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth + * telephone bearer service. + */ + /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) { + mContext = context; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServiceListener = listener; + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + doBind(); + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) + Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager(). + bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind TelephoneBearerService", e); + } + } + } + return false; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + if (VDBG) + Log.d(TAG, "Unbinding service..."); + try { + mAdapter.getBluetoothManager(). + unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to unbind TelephoneBearerService", e); + } finally { + mService = null; + } + } + } + } + + /* package */ void close() { + if (VDBG) + log("close()"); + unregisterBearer(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "", re); + } + } + mServiceListener = null; + doUnbind(); + } + + private IBluetoothLeCallControl getService() { + return mService; + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public int getConnectionState(@Nullable BluetoothDevice device) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List getConnectedDevices() { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Register Telephone Bearer exposing the interface that allows remote devices + * to track and control the call states. + * + *

    + * This is an asynchronous call. The callback is used to notify success or + * failure if the function returns true. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * + * + * + * + * + * + * @param uci Bearer Unique Client Identifier + * @param uriSchemes URI Schemes supported list + * @param capabilities bearer capabilities + * @param provider Network provider name + * @param technology Network technology + * @param executor {@link Executor} object on which callback will be + * executed. The Executor object is required. + * @param callback {@link Callback} object to which callback messages will + * be sent. The Callback object is required. + * @return true on success, false otherwise + * @hide + */ + @SuppressLint("ExecutorRegistration") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean registerBearer(@Nullable String uci, + @NonNull List uriSchemes, int capabilities, + @NonNull String provider, int technology, + @NonNull Executor executor, @NonNull Callback callback) { + if (DBG) { + Log.d(TAG, "registerBearer"); + } + if (callback == null) { + throw new IllegalArgumentException("null parameter: " + callback); + } + if (mCcid != 0) { + return false; + } + + mToken = uci; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + synchronized (mServerIfLock) { + if (mCallback != null) { + Log.e(TAG, "Bearer can be opened only once"); + return false; + } + + mCallback = callback; + try { + CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback); + service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities, + provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + mCallback = null; + return false; + } + + try { + mServerIfLock.wait(REG_TIMEOUT); + } catch (InterruptedException e) { + Log.e(TAG, "" + e); + mCallback = null; + } + + if (mCcid == 0) { + mCallback = null; + return false; + } + + return true; + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + + return false; + } + + /** + * Unregister Telephone Bearer Service and destroy all the associated data. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void unregisterBearer() { + if (DBG) { + Log.d(TAG, "unregisterBearer"); + } + if (mCcid == 0) { + return; + } + + int ccid = mCcid; + mCcid = 0; + mCallback = null; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.unregisterBearer(mToken); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Get the Content Control ID (CCID) value. + * + * @return ccid Content Control ID value + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getContentControlId() { + return mCcid; + } + + /** + * Notify about the newly added call. + * + *

    + * This shall be called as early as possible after the call has been added. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param call Newly added call + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallAdded(@NonNull BluetoothLeCall call) { + if (DBG) { + Log.d(TAG, "onCallAdded: call=" + call); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callAdded(mCcid, call); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify about the removed call. + * + *

    + * This shall be called as early as possible after the call has been removed. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The Id of a call that has been removed + * @param reason Call termination reason + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) { + if (DBG) { + Log.d(TAG, "callRemoved: callId=" + callId); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callRemoved(mCcid, new ParcelUuid(callId), reason); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify the call state change + * + *

    + * This shall be called as early as possible after the state of the call has + * changed. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The call Id that state has been changed + * @param state Call state + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) { + if (DBG) { + Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callStateChanged(mCcid, new ParcelUuid(callId), state); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Provide the current calls list + * + *

    + * This function must be invoked after registration if application has any + * calls. + * + * @param calls current calls list + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void currentCallsList(@NonNull List calls) { + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.currentCallsList(mCcid, calls); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + /** + * Provide the network current status + * + *

    + * This function must be invoked on change of network state. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * + * + * @param provider Network provider name + * @param technology Network technology + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void networkStateChanged(@NonNull String provider, int technology) { + if (DBG) { + Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.networkStateChanged(mCcid, provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Send a response to a call control request to a remote device. + * + *

    + * This function must be invoked in when a request is received by one of these + * callback methods: + * + *

      + *
    • {@link Callback#onAcceptCall} + *
    • {@link Callback#onTerminateCall} + *
    • {@link Callback#onHoldCall} + *
    • {@link Callback#onUnholdCall} + *
    • {@link Callback#onPlaceCall} + *
    • {@link Callback#onJoinCalls} + *
    + * + * @param requestId The ID of the request that was received with the callback + * @param result The result of the request to be sent to the remote devices + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void requestResult(int requestId, @Result int result) { + if (DBG) { + Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.requestResult(mCcid, requestId, result); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private final IBluetoothProfileServiceConnection mConnection = + new IBluetoothProfileServiceConnection.Stub() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) { + Log.d(TAG, "Proxy object connected"); + } + mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service)); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED)); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + if (DBG) { + Log.d(TAG, "Proxy object disconnected"); + } + doUnbind(); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED)); + } + }; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_TBS_SERVICE_CONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL, + BluetoothLeCallControl.this); + } + break; + } + case MESSAGE_TBS_SERVICE_DISCONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL); + } + break; + } + } + } + }; +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index e047e5d81a9..d0f74e98572 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -239,13 +239,20 @@ public interface BluetoothProfile { */ int LE_AUDIO_BROADCAST = 26; + /** + * @hide + * Telephone Bearer Service from Call Control Profile + * + */ + int LE_CALL_CONTROL = 27; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 26; + int MAX_PROFILE_ID = 27; /** * Default priority for devices that we try to auto-connect to and diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 8860a816410..2c6bf2bd379 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -46,6 +46,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.bluetooth.IBluetoothLeCallControl; import android.content.ActivityNotFoundException; import android.content.AttributionSource; import android.content.BroadcastReceiver; @@ -1328,11 +1329,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + bluetoothProfile); } - if (bluetoothProfile != BluetoothProfile.HEADSET) { + Intent intent; + if (bluetoothProfile == BluetoothProfile.HEADSET) { + intent = new Intent(IBluetoothHeadset.class.getName()); + } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) { + intent = new Intent(IBluetoothLeCallControl.class.getName()); + } else { return false; } - Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); if (!psc.bindService()) { return false; -- GitLab From 9220457153e07d1b2232490006027e7584c6263f Mon Sep 17 00:00:00 2001 From: Johanna Ye Date: Thu, 23 Dec 2021 20:13:36 +0100 Subject: [PATCH 1399/1408] Reduce blocking time when waiting for CharacteristicWrite queue to clear up. Bug: 200073240 Test: manual build Change-Id: I62070a5789809c363dba3cb4f0d007d17e3a9ef8 --- framework/java/android/bluetooth/BluetoothGatt.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index fe8d1ba80e3..b531829d294 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -87,7 +87,7 @@ public final class BluetoothGatt implements BluetoothProfile { private static final int CONN_STATE_CLOSED = 4; private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5; - private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds + private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds private List mServices; -- GitLab From 4059e9b77614c0ebf6b2bc1392d7ae931b890760 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 29 Dec 2021 16:56:49 +0100 Subject: [PATCH 1400/1408] Use Common Audio Service UUID from latest assigned numbers Bug: 150670922 Test: compilation Merged-In: I35684ecec50c3db757331b6384c2579f5a28a70a Change-Id: I35684ecec50c3db757331b6384c2579f5a28a70a --- framework/java/android/bluetooth/BluetoothUuid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java index 858819e15ab..2a8ff518508 100644 --- a/framework/java/android/bluetooth/BluetoothUuid.java +++ b/framework/java/android/bluetooth/BluetoothUuid.java @@ -189,7 +189,7 @@ public final class BluetoothUuid { @NonNull @SystemApi public static final ParcelUuid CAP = - ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE"); + ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB"); /** @hide */ @NonNull @SystemApi -- GitLab From 4f30e149116596a155ae7090a98181b4899bf9d5 Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Wed, 8 Dec 2021 11:56:45 -0800 Subject: [PATCH 1401/1408] Add BluetoothLeBroadcastSourceInfo. Bug: 205174140 Bug: 208222452 Tag: #feature Test: gd/cert/run Change-Id: I5d129d8b230ab564387212ebe83eb3c4398c7b21 --- .../BluetoothLeBroadcastSourceInfo.java | 788 ++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java b/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java new file mode 100644 index 00000000000..cb47280acc7 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java @@ -0,0 +1,788 @@ +/* + * Copyright 2021 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.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * This class represents an LE Audio Broadcast Source and the associated information that is needed + * by Broadcast Audio Scan Service (BASS) residing on a Scan Delegator. + * + *

    For example, the Scan Delegator on an LE Audio Broadcast Sink can use the information + * contained within an instance of this class to synchronize with an LE Audio Broadcast Source in + * order to listen to a Broadcast Audio Stream. + * + *

    BroadcastAssistant has a BASS client which facilitates scanning and discovery of Broadcast + * Sources on behalf of say a Broadcast Sink. Upon successful discovery of one or more Broadcast + * sources, this information needs to be communicated to the BASS Server residing within the Scan + * Delegator on a Broadcast Sink. This is achieved using the Periodic Advertising Synchronization + * Transfer (PAST) procedure. This procedure uses information contained within an instance of this + * class. + * + * @hide + */ +public final class BluetoothLeBroadcastSourceInfo implements Parcelable { + private static final String TAG = "BluetoothLeBroadcastSourceInfo"; + private static final boolean DBG = true; + + /** + * Constants representing Broadcast Source address types + * + * @hide + */ + @IntDef( + prefix = "LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_", + value = { + LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC, + LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM, + LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastSourceAddressType {} + + /** + * Represents a public address used by an LE Audio Broadcast Source + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC = 0; + + /** + * Represents a random address used by an LE Audio Broadcast Source + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM = 1; + + /** + * Represents an invalid address used by an LE Audio Broadcast Seurce + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID = 0xFFFF; + + /** + * Periodic Advertising Synchronization state + * + *

    Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast + * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast + * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the + * Periodic Advertising Synchronizaton Transfer (PAST) procedure. + * + * @hide + */ + @IntDef( + prefix = "LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_", + value = { + LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE, + LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ, + LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC, + LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL, + LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastSinkPaSyncState {} + + /** + * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA) + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE = 0; + + /** + * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the + * Periodic Advertisements (PA). + * + *

    This is also known as scan delegation or scan offloading. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ = 1; + + /** + * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA). + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC = 2; + + /** + * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements + * (PA). + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL = 3; + + /** + * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements + * (PA) using the Periodic Advertisements Synchronization Transfert (PAST) procedure. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST = 4; + + /** + * Indicates that the Broadcast Sink synchornization state is invalid. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID = 0xFFFF; + + /** @hide */ + @IntDef( + prefix = "LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_", + value = { + LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED, + LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastSinkAudioSyncState {} + + /** + * Indicates that the Broadcast Sink is not synchronized with a Broadcast Audio Stream. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0; + + /** + * Indicates that the Broadcast Sink is synchronized with a Broadcast Audio Stream. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED = 1; + + /** + * Indicates that the Broadcast Sink audio synchronization state is invalid. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID = 0xFFFF; + + /** @hide */ + @IntDef( + prefix = "LE_AUDIO_BROADCAST_SINK_ENC_STATE_", + value = { + LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED, + LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED, + LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING, + LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastSinkEncryptionState {} + + /** + * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED = 0; + + /** + * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with the audio + * stream. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED = 1; + + /** + * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING = 2; + + /** + * Indicates that the Broadcast Sink is unable to decrypt an audio stream due to an incorrect + * Broadcast Code + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE = 3; + + /** + * Indicates that the Broadcast Sink encryption state is invalid. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID = 0xFF; + + /** + * Represents an invalid LE Audio Broadcast Source ID + * + * @hide + */ + public static final byte LE_AUDIO_BROADCAST_SINK_INVALID_SOURCE_ID = (byte) 0x00; + + /** + * Represents an invalid Broadcast ID of a Broadcast Source + * + * @hide + */ + public static final int INVALID_BROADCAST_ID = 0xFFFFFF; + + private byte mSourceId; + private @LeAudioBroadcastSourceAddressType int mSourceAddressType; + private BluetoothDevice mSourceDevice; + private byte mSourceAdvSid; + private int mBroadcastId; + private @LeAudioBroadcastSinkPaSyncState int mPaSyncState; + private @LeAudioBroadcastSinkEncryptionState int mEncryptionStatus; + private @LeAudioBroadcastSinkAudioSyncState int mAudioSyncState; + private byte[] mBadBroadcastCode; + private byte mNumSubGroups; + private Map mSubgroupBisSyncState = new HashMap(); + private Map mSubgroupMetadata = new HashMap(); + + private String mBroadcastCode; + private static final int BIS_NO_PREF = 0xFFFFFFFF; + private static final int BROADCAST_CODE_SIZE = 16; + + /** + * Constructor to create an Empty object of {@link BluetoothLeBroadcastSourceInfo } with the + * given Source Id. + * + *

    This is mainly used to represent the Empty Broadcast Source entries + * + * @param sourceId Source Id for this Broadcast Source info object + * @hide + */ + public BluetoothLeBroadcastSourceInfo(byte sourceId) { + mSourceId = sourceId; + mSourceAddressType = LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID; + mSourceDevice = null; + mSourceAdvSid = (byte) 0x00; + mBroadcastId = INVALID_BROADCAST_ID; + mPaSyncState = LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID; + mAudioSyncState = LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID; + mEncryptionStatus = LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID; + mBadBroadcastCode = null; + mNumSubGroups = 0; + mBroadcastCode = null; + } + + /*package*/ BluetoothLeBroadcastSourceInfo( + byte sourceId, + @LeAudioBroadcastSourceAddressType int addressType, + @NonNull BluetoothDevice device, + byte advSid, + int broadcastId, + @LeAudioBroadcastSinkPaSyncState int paSyncstate, + @LeAudioBroadcastSinkEncryptionState int encryptionStatus, + @LeAudioBroadcastSinkAudioSyncState int audioSyncstate, + @Nullable byte[] badCode, + byte numSubGroups, + @NonNull Map bisSyncState, + @Nullable Map subgroupMetadata, + @NonNull String broadcastCode) { + mSourceId = sourceId; + mSourceAddressType = addressType; + mSourceDevice = device; + mSourceAdvSid = advSid; + mBroadcastId = broadcastId; + mPaSyncState = paSyncstate; + mEncryptionStatus = encryptionStatus; + mAudioSyncState = audioSyncstate; + + if (badCode != null && badCode.length != 0) { + mBadBroadcastCode = new byte[badCode.length]; + System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length); + } + mNumSubGroups = numSubGroups; + mSubgroupBisSyncState = new HashMap(bisSyncState); + mSubgroupMetadata = new HashMap(subgroupMetadata); + mBroadcastCode = broadcastCode; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothLeBroadcastSourceInfo) { + BluetoothLeBroadcastSourceInfo other = (BluetoothLeBroadcastSourceInfo) o; + return (other.mSourceId == mSourceId + && other.mSourceAddressType == mSourceAddressType + && other.mSourceDevice == mSourceDevice + && other.mSourceAdvSid == mSourceAdvSid + && other.mBroadcastId == mBroadcastId + && other.mPaSyncState == mPaSyncState + && other.mEncryptionStatus == mEncryptionStatus + && other.mAudioSyncState == mAudioSyncState + && Arrays.equals(other.mBadBroadcastCode, mBadBroadcastCode) + && other.mNumSubGroups == mNumSubGroups + && mSubgroupBisSyncState.equals(other.mSubgroupBisSyncState) + && mSubgroupMetadata.equals(other.mSubgroupMetadata) + && other.mBroadcastCode == mBroadcastCode); + } + return false; + } + + /** + * Checks if an instance of {@link BluetoothLeBroadcastSourceInfo} is empty. + * + * @hide + */ + public boolean isEmpty() { + boolean ret = false; + if (mSourceAddressType == LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID + && mSourceDevice == null + && mSourceAdvSid == (byte) 0 + && mPaSyncState == LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID + && mEncryptionStatus == LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID + && mAudioSyncState == LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID + && mBadBroadcastCode == null + && mNumSubGroups == 0 + && mSubgroupBisSyncState.size() == 0 + && mSubgroupMetadata.size() == 0 + && mBroadcastCode == null) { + ret = true; + } + return ret; + } + + /** + * Compares an instance of {@link BluetoothLeBroadcastSourceInfo} with the provided instance. + * + * @hide + */ + public boolean matches(BluetoothLeBroadcastSourceInfo srcInfo) { + boolean ret = false; + if (srcInfo == null) { + ret = false; + } else { + if (mSourceDevice == null) { + if (mSourceAdvSid == srcInfo.getAdvertisingSid() + && mSourceAddressType == srcInfo.getAdvAddressType()) { + ret = true; + } + } else { + if (mSourceDevice.equals(srcInfo.getSourceDevice()) + && mSourceAdvSid == srcInfo.getAdvertisingSid() + && mSourceAddressType == srcInfo.getAdvAddressType() + && mBroadcastId == srcInfo.getBroadcastId()) { + ret = true; + } + } + } + return ret; + } + + @Override + public int hashCode() { + return Objects.hash( + mSourceId, + mSourceAddressType, + mSourceDevice, + mSourceAdvSid, + mBroadcastId, + mPaSyncState, + mEncryptionStatus, + mAudioSyncState, + mBadBroadcastCode, + mNumSubGroups, + mSubgroupBisSyncState, + mSubgroupMetadata, + mBroadcastCode); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "{BluetoothLeBroadcastSourceInfo : mSourceId" + + mSourceId + + " addressType: " + + mSourceAddressType + + " sourceDevice: " + + mSourceDevice + + " mSourceAdvSid:" + + mSourceAdvSid + + " mBroadcastId:" + + mBroadcastId + + " mPaSyncState:" + + mPaSyncState + + " mEncryptionStatus:" + + mEncryptionStatus + + " mAudioSyncState:" + + mAudioSyncState + + " mBadBroadcastCode:" + + mBadBroadcastCode + + " mNumSubGroups:" + + mNumSubGroups + + " mSubgroupBisSyncState:" + + mSubgroupBisSyncState + + " mSubgroupMetadata:" + + mSubgroupMetadata + + " mBroadcastCode:" + + mBroadcastCode + + "}"; + } + + /** + * Get the Source Id + * + * @return byte representing the Source Id, {@link + * #LE_AUDIO_BROADCAST_ASSISTANT_INVALID_SOURCE_ID} if invalid + * @hide + */ + public byte getSourceId() { + return mSourceId; + } + + /** + * Set the Source Id + * + * @param sourceId source Id + * @hide + */ + public void setSourceId(byte sourceId) { + mSourceId = sourceId; + } + + /** + * Set the Broadcast Source device + * + * @param sourceDevice the Broadcast Source BluetoothDevice + * @hide + */ + public void setSourceDevice(@NonNull BluetoothDevice sourceDevice) { + mSourceDevice = sourceDevice; + } + + /** + * Get the Broadcast Source BluetoothDevice + * + * @return Broadcast Source BluetoothDevice + * @hide + */ + public @NonNull BluetoothDevice getSourceDevice() { + return mSourceDevice; + } + + /** + * Set the address type of the Broadcast Source advertisements + * + * @hide + */ + public void setAdvAddressType(@LeAudioBroadcastSourceAddressType int addressType) { + mSourceAddressType = addressType; + } + + /** + * Get the address type used by advertisements from the Broadcast Source. + * BluetoothLeBroadcastSourceInfo Object + * + * @hide + */ + @LeAudioBroadcastSourceAddressType + public int getAdvAddressType() { + return mSourceAddressType; + } + + /** + * Set the advertising SID of the Broadcast Source advertisement. + * + * @param advSid advertising SID of the Broadcast Source + * @hide + */ + public void setAdvertisingSid(byte advSid) { + mSourceAdvSid = advSid; + } + + /** + * Get the advertising SID of the Broadcast Source advertisement. + * + * @return advertising SID of the Broadcast Source + * @hide + */ + public byte getAdvertisingSid() { + return mSourceAdvSid; + } + + /** + * Get the Broadcast ID of the Broadcast Source. + * + * @return broadcast ID + * @hide + */ + public int getBroadcastId() { + return mBroadcastId; + } + + /** + * Set the Periodic Advertising (PA) Sync State. + * + * @hide + */ + /*package*/ void setPaSyncState(@LeAudioBroadcastSinkPaSyncState int paSyncState) { + mPaSyncState = paSyncState; + } + + /** + * Get the Periodic Advertising (PA) Sync State + * + * @hide + */ + public @LeAudioBroadcastSinkPaSyncState int getMetadataSyncState() { + return mPaSyncState; + } + + /** + * Set the audio sync state + * + * @hide + */ + /*package*/ void setAudioSyncState(@LeAudioBroadcastSinkAudioSyncState int audioSyncState) { + mAudioSyncState = audioSyncState; + } + + /** + * Get the audio sync state + * + * @hide + */ + public @LeAudioBroadcastSinkAudioSyncState int getAudioSyncState() { + return mAudioSyncState; + } + + /** + * Set the encryption status + * + * @hide + */ + /*package*/ void setEncryptionStatus( + @LeAudioBroadcastSinkEncryptionState int encryptionStatus) { + mEncryptionStatus = encryptionStatus; + } + + /** + * Get the encryption status + * + * @hide + */ + public @LeAudioBroadcastSinkEncryptionState int getEncryptionStatus() { + return mEncryptionStatus; + } + + /** + * Get the incorrect broadcast code that the Scan delegator used to decrypt the Broadcast Audio + * Stream and failed. + * + *

    This code is valid only if {@link #getEncryptionStatus} returns {@link + * #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE} + * + * @return byte array containing bad broadcast value, null if the current encryption status is + * not {@link #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE} + * @hide + */ + public @Nullable byte[] getBadBroadcastCode() { + return mBadBroadcastCode; + } + + /** + * Get the number of subgroups. + * + * @return number of subgroups + * @hide + */ + public byte getNumberOfSubGroups() { + return mNumSubGroups; + } + + public @NonNull Map getSubgroupBisSyncState() { + return mSubgroupBisSyncState; + } + + public void setSubgroupBisSyncState(@NonNull Map bisSyncState) { + mSubgroupBisSyncState = new HashMap(bisSyncState); + } + + /*package*/ void setBroadcastCode(@NonNull String broadcastCode) { + mBroadcastCode = broadcastCode; + } + + /** + * Get the broadcast code + * + * @return + * @hide + */ + public @NonNull String getBroadcastCode() { + return mBroadcastCode; + } + + /** + * Set the broadcast ID + * + * @param broadcastId broadcast ID of the Broadcast Source + * @hide + */ + public void setBroadcastId(int broadcastId) { + mBroadcastId = broadcastId; + } + + private void writeSubgroupBisSyncStateToParcel( + @NonNull Parcel dest, @NonNull Map subgroupBisSyncState) { + dest.writeInt(subgroupBisSyncState.size()); + for (Map.Entry entry : subgroupBisSyncState.entrySet()) { + dest.writeInt(entry.getKey()); + dest.writeInt(entry.getValue()); + } + } + + private static void readSubgroupBisSyncStateFromParcel( + @NonNull Parcel in, @NonNull Map subgroupBisSyncState) { + int size = in.readInt(); + + for (int i = 0; i < size; i++) { + Integer key = in.readInt(); + Integer value = in.readInt(); + subgroupBisSyncState.put(key, value); + } + } + + private void writeSubgroupMetadataToParcel( + @NonNull Parcel dest, @Nullable Map subgroupMetadata) { + if (subgroupMetadata == null) { + dest.writeInt(0); + return; + } + + dest.writeInt(subgroupMetadata.size()); + for (Map.Entry entry : subgroupMetadata.entrySet()) { + dest.writeInt(entry.getKey()); + byte[] metadata = entry.getValue(); + if (metadata != null) { + dest.writeInt(metadata.length); + dest.writeByteArray(metadata); + } + } + } + + private static void readSubgroupMetadataFromParcel( + @NonNull Parcel in, @NonNull Map subgroupMetadata) { + int size = in.readInt(); + + for (int i = 0; i < size; i++) { + Integer key = in.readInt(); + Integer metaDataLen = in.readInt(); + byte[] metadata = null; + if (metaDataLen != 0) { + metadata = new byte[metaDataLen]; + in.readByteArray(metadata); + } + subgroupMetadata.put(key, metadata); + } + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public @NonNull BluetoothLeBroadcastSourceInfo createFromParcel( + @NonNull Parcel in) { + final byte sourceId = in.readByte(); + final int sourceAddressType = in.readInt(); + final BluetoothDevice sourceDevice = + in.readTypedObject(BluetoothDevice.CREATOR); + final byte sourceAdvSid = in.readByte(); + final int broadcastId = in.readInt(); + final int paSyncState = in.readInt(); + final int audioSyncState = in.readInt(); + final int encryptionStatus = in.readInt(); + final int badBroadcastLen = in.readInt(); + byte[] badBroadcastCode = null; + + if (badBroadcastLen > 0) { + badBroadcastCode = new byte[badBroadcastLen]; + in.readByteArray(badBroadcastCode); + } + final byte numSubGroups = in.readByte(); + final String broadcastCode = in.readString(); + Map subgroupBisSyncState = new HashMap(); + readSubgroupBisSyncStateFromParcel(in, subgroupBisSyncState); + Map subgroupMetadata = new HashMap(); + readSubgroupMetadataFromParcel(in, subgroupMetadata); + + BluetoothLeBroadcastSourceInfo srcInfo = + new BluetoothLeBroadcastSourceInfo( + sourceId, + sourceAddressType, + sourceDevice, + sourceAdvSid, + broadcastId, + paSyncState, + encryptionStatus, + audioSyncState, + badBroadcastCode, + numSubGroups, + subgroupBisSyncState, + subgroupMetadata, + broadcastCode); + return srcInfo; + } + + public @NonNull BluetoothLeBroadcastSourceInfo[] newArray(int size) { + return new BluetoothLeBroadcastSourceInfo[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeByte(mSourceId); + out.writeInt(mSourceAddressType); + out.writeTypedObject(mSourceDevice, 0); + out.writeByte(mSourceAdvSid); + out.writeInt(mBroadcastId); + out.writeInt(mPaSyncState); + out.writeInt(mAudioSyncState); + out.writeInt(mEncryptionStatus); + + if (mBadBroadcastCode != null) { + out.writeInt(mBadBroadcastCode.length); + out.writeByteArray(mBadBroadcastCode); + } else { + // zero indicates that there is no "bad broadcast code" + out.writeInt(0); + } + out.writeByte(mNumSubGroups); + out.writeString(mBroadcastCode); + writeSubgroupBisSyncStateToParcel(out, mSubgroupBisSyncState); + writeSubgroupMetadataToParcel(out, mSubgroupMetadata); + } + + private static void log(@NonNull String msg) { + if (DBG) { + Log.d(TAG, msg); + } + } +} +; -- GitLab From 19001515735eef64d46bf9ff7a10404dfc7c379a Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Wed, 5 Jan 2022 23:04:04 +0100 Subject: [PATCH 1402/1408] Bluetooth: add LE Audio COD constant definition Tag: #feature Test: compilation Bug: 150670922 Change-Id: Ic68cd1e9ce4f10d7c2388eeeabe67acad5e555ea --- framework/java/android/bluetooth/BluetoothClass.java | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java index 7fe18a0704b..8535b4fd283 100755 --- a/framework/java/android/bluetooth/BluetoothClass.java +++ b/framework/java/android/bluetooth/BluetoothClass.java @@ -119,6 +119,7 @@ public final class BluetoothClass implements Parcelable { private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; + public static final int LE_AUDIO = 0x004000; public static final int POSITIONING = 0x010000; public static final int NETWORKING = 0x020000; public static final int RENDER = 0x040000; -- GitLab From 6b15035995968cd7b1f28f7abaeba68b493ebac4 Mon Sep 17 00:00:00 2001 From: Andriy Naborskyy Date: Tue, 21 Jul 2020 23:19:40 -0700 Subject: [PATCH 1403/1408] le advertisement address type settings add api to request public or random address type for advertisements. bug: 155993389 Bug: 199827901 Tag: #feature Test: atest BluetoothInstrumentationTests Test: ACTS, SL4A, IOP and BCST, CTS, GTS Change-Id: I4a2b9918e0f0cc31649e3c1952d9ba34f78d337f --- .../bluetooth/le/AdvertiseSettings.java | 48 ++++++++++- .../le/AdvertisingSetParameters.java | 82 ++++++++++++++++++- .../bluetooth/le/BluetoothLeAdvertiser.java | 1 + 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/framework/java/android/bluetooth/le/AdvertiseSettings.java b/framework/java/android/bluetooth/le/AdvertiseSettings.java index 7129d762cd9..c52a6ee3398 100644 --- a/framework/java/android/bluetooth/le/AdvertiseSettings.java +++ b/framework/java/android/bluetooth/le/AdvertiseSettings.java @@ -16,6 +16,9 @@ package android.bluetooth.le; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus; import android.os.Parcel; import android.os.Parcelable; @@ -70,17 +73,21 @@ public final class AdvertiseSettings implements Parcelable { */ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + private final int mAdvertiseMode; private final int mAdvertiseTxPowerLevel; private final int mAdvertiseTimeoutMillis; private final boolean mAdvertiseConnectable; + private final int mOwnAddressType; private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel, - boolean advertiseConnectable, int advertiseTimeout) { + boolean advertiseConnectable, int advertiseTimeout, + @AddressTypeStatus int ownAddressType) { mAdvertiseMode = advertiseMode; mAdvertiseTxPowerLevel = advertiseTxPowerLevel; mAdvertiseConnectable = advertiseConnectable; mAdvertiseTimeoutMillis = advertiseTimeout; + mOwnAddressType = ownAddressType; } private AdvertiseSettings(Parcel in) { @@ -88,6 +95,7 @@ public final class AdvertiseSettings implements Parcelable { mAdvertiseTxPowerLevel = in.readInt(); mAdvertiseConnectable = in.readInt() != 0; mAdvertiseTimeoutMillis = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -118,12 +126,23 @@ public final class AdvertiseSettings implements Parcelable { return mAdvertiseTimeoutMillis; } + /** + * @return the own address type for advertising + * + * @hide + */ + @SystemApi + public @AddressTypeStatus int getOwnAddressType() { + return mOwnAddressType; + } + @Override public String toString() { return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel + ", mAdvertiseConnectable=" + mAdvertiseConnectable - + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]"; + + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + + ", mOwnAddressType=" + mOwnAddressType + "]"; } @Override @@ -137,6 +156,7 @@ public final class AdvertiseSettings implements Parcelable { dest.writeInt(mAdvertiseTxPowerLevel); dest.writeInt(mAdvertiseConnectable ? 1 : 0); dest.writeInt(mAdvertiseTimeoutMillis); + dest.writeInt(mOwnAddressType); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -160,6 +180,7 @@ public final class AdvertiseSettings implements Parcelable { private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; private int mTimeoutMillis = 0; private boolean mConnectable = true; + private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT; /** * Set advertise mode to control the advertising power and latency. @@ -225,11 +246,32 @@ public final class AdvertiseSettings implements Parcelable { return this; } + /** + * Set own address type for advertising to control public or privacy mode. If used to set + * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT}, + * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the + * time of starting advertising. + * + * @throws IllegalArgumentException If the {@code ownAddressType} is invalid + * + * @hide + */ + @SystemApi + public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) { + if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT + || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) { + throw new IllegalArgumentException("unknown address type " + ownAddressType); + } + mOwnAddressType = ownAddressType; + return this; + } + /** * Build the {@link AdvertiseSettings} object. */ public AdvertiseSettings build() { - return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis); + return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis, + mOwnAddressType); } } } diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java index e39b198ae38..5c8fae65193 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -16,11 +16,17 @@ package android.bluetooth.le; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The {@link AdvertisingSetParameters} provide a way to adjust advertising * preferences for each @@ -97,6 +103,39 @@ public final class AdvertisingSetParameters implements Parcelable { */ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + /** @hide */ + @IntDef(prefix = "ADDRESS_TYPE_", value = { + ADDRESS_TYPE_DEFAULT, + ADDRESS_TYPE_PUBLIC, + ADDRESS_TYPE_RANDOM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AddressTypeStatus {} + + /** + * Advertise own address type that corresponds privacy settings of the device. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_DEFAULT = -1; + + /** + * Advertise own public address type. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_PUBLIC = 0; + + /** + * Generate and adverise own resolvable private address. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_RANDOM = 1; + private final boolean mIsLegacy; private final boolean mIsAnonymous; private final boolean mIncludeTxPower; @@ -106,11 +145,12 @@ public final class AdvertisingSetParameters implements Parcelable { private final boolean mScannable; private final int mInterval; private final int mTxPowerLevel; + private final int mOwnAddressType; private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, - int interval, int txPowerLevel) { + int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) { mConnectable = connectable; mScannable = scannable; mIsLegacy = isLegacy; @@ -120,6 +160,7 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = secondaryPhy; mInterval = interval; mTxPowerLevel = txPowerLevel; + mOwnAddressType = ownAddressType; } private AdvertisingSetParameters(Parcel in) { @@ -132,6 +173,7 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = in.readInt(); mInterval = in.readInt(); mTxPowerLevel = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -197,6 +239,16 @@ public final class AdvertisingSetParameters implements Parcelable { return mTxPowerLevel; } + /** + * @return the own address type for advertising + * + * @hide + */ + @SystemApi + public @AddressTypeStatus int getOwnAddressType() { + return mOwnAddressType; + } + @Override public String toString() { return "AdvertisingSetParameters [connectable=" + mConnectable @@ -206,7 +258,8 @@ public final class AdvertisingSetParameters implements Parcelable { + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + ", interval=" + mInterval - + ", txPowerLevel=" + mTxPowerLevel + "]"; + + ", txPowerLevel=" + mTxPowerLevel + + ", ownAddressType=" + mOwnAddressType + "]"; } @Override @@ -225,6 +278,7 @@ public final class AdvertisingSetParameters implements Parcelable { dest.writeInt(mSecondaryPhy); dest.writeInt(mInterval); dest.writeInt(mTxPowerLevel); + dest.writeInt(mOwnAddressType); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -253,6 +307,7 @@ public final class AdvertisingSetParameters implements Parcelable { private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M; private int mInterval = INTERVAL_LOW; private int mTxPowerLevel = TX_POWER_MEDIUM; + private int mOwnAddressType = ADDRESS_TYPE_DEFAULT; /** * Set whether the advertisement type should be connectable or @@ -398,6 +453,26 @@ public final class AdvertisingSetParameters implements Parcelable { return this; } + /** + * Set own address type for advertising to control public or privacy mode. If used to set + * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT}, + * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the + * time of starting advertising. + * + * @throws IllegalArgumentException If the {@code ownAddressType} is invalid + * + * @hide + */ + @SystemApi + public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) { + if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT + || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) { + throw new IllegalArgumentException("unknown address type " + ownAddressType); + } + mOwnAddressType = ownAddressType; + return this; + } + /** * Build the {@link AdvertisingSetParameters} object. * @@ -431,7 +506,8 @@ public final class AdvertisingSetParameters implements Parcelable { } return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous, - mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel); + mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel, + mOwnAddressType); } } } diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index b9f8a57a75e..879dceedaae 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -138,6 +138,7 @@ public final class BluetoothLeAdvertiser { parameters.setLegacyMode(true); parameters.setConnectable(isConnectable); parameters.setScannable(true); // legacy advertisements we support are always scannable + parameters.setOwnAddressType(settings.getOwnAddressType()); if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) { parameters.setInterval(1600); // 1s } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) { -- GitLab From 5268cc98a9b340d0231729ced5b8bf91d554862a Mon Sep 17 00:00:00 2001 From: Qasim Javed Date: Mon, 13 Dec 2021 11:51:27 -0800 Subject: [PATCH 1404/1408] Add BluetoothLeBroadcastAssistantCallback. Bug: 205174140 Bug: 208222452 Tag: #feature Test: gd/cert/run Change-Id: I302f2d22e6f295db27c4309b7a30dd867a264439 --- ...BluetoothLeBroadcastAssistantCallback.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java new file mode 100644 index 00000000000..b866cce2247 --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java @@ -0,0 +1,140 @@ +/* + * Copyright 2021 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.annotation.IntDef; +import android.annotation.NonNull; +import android.bluetooth.le.ScanResult; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is + * offloaded to a Broadcast Assistant. + * + *

    An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast + * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This + * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is + * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast + * Assistant. + * + *

    Once a GATT connection is established between the BASS client and the BASS server, the + * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast + * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the + * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the + * Assistant about changes such as addition and removal of Broadcast Sources. + * + * @hide + */ +public abstract class BluetoothLeBroadcastAssistantCallback { + + /** + * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server + * + * @hide + */ + @IntDef( + prefix = "BASS_STATUS_", + value = { + BASS_STATUS_SUCCESS, + BASS_STATUS_FAILURE, + BASS_STATUS_INVALID_GATT_HANDLE, + BASS_STATUS_TXN_TIMEOUT, + BASS_STATUS_INVALID_SOURCE_ID, + BASS_STATUS_COLOCATED_SRC_UNAVAILABLE, + BASS_STATUS_INVALID_SOURCE_SELECTED, + BASS_STATUS_SOURCE_UNAVAILABLE, + BASS_STATUS_DUPLICATE_ADDITION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BassStatus {} + + public static final int BASS_STATUS_SUCCESS = 0x00; + public static final int BASS_STATUS_FAILURE = 0x01; + public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02; + public static final int BASS_STATUS_TXN_TIMEOUT = 0x03; + + public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04; + public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05; + public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06; + public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07; + public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08; + public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09; + public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10; + + /** + * Callback invoked when a new LE Audio Broadcast Source is found. + * + * @param result {@link ScanResult} scan result representing a Broadcast Source + */ + public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {} + + /** + * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs) + * of an LE Audio Broadcast Source. + * + * @param source the selected Broadcast Source + */ + public void onBluetoothLeBroadcastSourceSelected( + @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {} + + /** + * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio + * Broadcast Source. + * + * @param source the Broadcast Source with which synchronization was lost + */ + public void onBluetoothLeBroadcastSourceLost( + @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {} + + /** + * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan + * Delegator (within a Broadcast Sink, for example). + * + * @param sink Scan Delegator device on which a new Broadcast Source has been added + * @param source the added Broadcast Source + */ + public void onBluetoothLeBroadcastSourceAdded( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastSourceInfo source, + @BassStatus int status) {} + + /** + * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator + * has been updated. + * + * @param sink Scan Delegator device on which a Broadcast Source has been updated + * @param source the updated Broadcast Source + */ + public void onBluetoothLeBroadcastSourceUpdated( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastSourceInfo source, + @BassStatus int status) {} + + /** + * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the + * Scan Delegator (within a Broadcast Sink, for example). + * + * @param sink Scan Delegator device from which a Broadcast Source has been removed + * @param source the removed Broadcast Source + */ + public void onBluetoothLeBroadcastSourceRemoved( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastSourceInfo source, + @BassStatus int status) {} +} -- GitLab From 6bf87965a2b4220de686a0526f83ff010f1e252f Mon Sep 17 00:00:00 2001 From: Jack He Date: Fri, 7 Jan 2022 00:09:07 +0000 Subject: [PATCH 1405/1408] Revert "gtbs: Add Generic Telephone Bearer Service support" Revert "gtbs: Add Generic Telephone Bearer Service implementation" Revert submission 1873900-gtbs Reason for revert: broke cellular call over BT, see b/213412267 Reverted Changes: I284ddacfc:gtbs: Add Generic Telephone Bearer Service impleme... I8264ade4b:gtbs: Add Generic Telephone Bearer Service support... I996cd5ff9:Add support for Generic Telephone Bearer service (... Bug: 213412267 Tag: #feature Test: answer cellular call by pressing button on HFP headset Change-Id: I9c6b12b794b7378fd852238ad06890f46922c095 --- .../android/bluetooth/BluetoothAdapter.java | 7 - .../android/bluetooth/BluetoothLeCall.java | 285 ------ .../bluetooth/BluetoothLeCallControl.java | 911 ------------------ .../android/bluetooth/BluetoothProfile.java | 9 +- .../bluetooth/BluetoothManagerService.java | 9 +- 5 files changed, 3 insertions(+), 1218 deletions(-) delete mode 100644 framework/java/android/bluetooth/BluetoothLeCall.java delete mode 100644 framework/java/android/bluetooth/BluetoothLeCallControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 991da1da1d4..b23c3eb0cea 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3086,9 +3086,6 @@ public final class BluetoothAdapter { BluetoothCsipSetCoordinator csipSetCoordinator = new BluetoothCsipSetCoordinator(context, listener, this); return true; - } else if (profile == BluetoothProfile.LE_CALL_CONTROL) { - BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener); - return true; } else { return false; } @@ -3191,10 +3188,6 @@ public final class BluetoothAdapter { (BluetoothCsipSetCoordinator) proxy; csipSetCoordinator.close(); break; - case BluetoothProfile.LE_CALL_CONTROL: - BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy; - tbs.close(); - break; } } diff --git a/framework/java/android/bluetooth/BluetoothLeCall.java b/framework/java/android/bluetooth/BluetoothLeCall.java deleted file mode 100644 index fb7789db25c..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeCall.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2021 HIMSA II K/S - www.himsa.com. - * Represented by EHIMA - www.ehima.com - * - * 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.annotation.IntDef; -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.ParcelUuid; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; -import java.util.UUID; - -/** - * Representation of Call - * - * @hide - */ -public final class BluetoothLeCall implements Parcelable { - - /** @hide */ - @IntDef(prefix = "STATE_", value = { - STATE_INCOMING, - STATE_DIALING, - STATE_ALERTING, - STATE_ACTIVE, - STATE_LOCALLY_HELD, - STATE_REMOTELY_HELD, - STATE_LOCALLY_AND_REMOTELY_HELD - }) - @Retention(RetentionPolicy.SOURCE) - public @interface State { - } - - /** - * A remote party is calling (incoming call). - * - * @hide - */ - public static final int STATE_INCOMING = 0x00; - - /** - * The process to call the remote party has started but the remote party is not - * being alerted (outgoing call). - * - * @hide - */ - public static final int STATE_DIALING = 0x01; - - /** - * A remote party is being alerted (outgoing call). - * - * @hide - */ - public static final int STATE_ALERTING = 0x02; - - /** - * The call is in an active conversation. - * - * @hide - */ - public static final int STATE_ACTIVE = 0x03; - - /** - * The call is connected but held locally. “Locally Held” implies that either - * the server or the client can affect the state. - * - * @hide - */ - public static final int STATE_LOCALLY_HELD = 0x04; - - /** - * The call is connected but held remotely. “Remotely Held” means that the state - * is controlled by the remote party of a call. - * - * @hide - */ - public static final int STATE_REMOTELY_HELD = 0x05; - - /** - * The call is connected but held both locally and remotely. - * - * @hide - */ - public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06; - - /** - * Whether the call direction is outgoing. - * - * @hide - */ - public static final int FLAG_OUTGOING_CALL = 0x00000001; - - /** - * Whether the call URI and Friendly Name are withheld by server. - * - * @hide - */ - public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002; - - /** - * Whether the call URI and Friendly Name are withheld by network. - * - * @hide - */ - public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004; - - /** Unique UUID that identifies this call */ - private UUID mUuid; - - /** Remote Caller URI */ - private String mUri; - - /** Caller friendly name */ - private String mFriendlyName; - - /** Call state */ - private @State int mState; - - /** Call flags */ - private int mCallFlags; - - /** @hide */ - public BluetoothLeCall(@NonNull BluetoothLeCall that) { - mUuid = new UUID(that.getUuid().getMostSignificantBits(), - that.getUuid().getLeastSignificantBits()); - mUri = that.mUri; - mFriendlyName = that.mFriendlyName; - mState = that.mState; - mCallFlags = that.mCallFlags; - } - - /** @hide */ - public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName, - @State int state, int callFlags) { - mUuid = uuid; - mUri = uri; - mFriendlyName = friendlyName; - mState = state; - mCallFlags = callFlags; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - BluetoothLeCall that = (BluetoothLeCall) o; - return mUuid.equals(that.mUuid) && mUri.equals(that.mUri) - && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState - && mCallFlags == that.mCallFlags; - } - - @Override - public int hashCode() { - return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags); - } - - /** - * Returns a string representation of this BluetoothLeCall. - * - *

    - * Currently this is the UUID. - * - * @return string representation of this BluetoothLeCall - */ - @Override - public String toString() { - return mUuid.toString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(new ParcelUuid(mUuid), 0); - out.writeString(mUri); - out.writeString(mFriendlyName); - out.writeInt(mState); - out.writeInt(mCallFlags); - } - - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public BluetoothLeCall createFromParcel(Parcel in) { - return new BluetoothLeCall(in); - } - - public BluetoothLeCall[] newArray(int size) { - return new BluetoothLeCall[size]; - } - }; - - private BluetoothLeCall(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); - mUri = in.readString(); - mFriendlyName = in.readString(); - mState = in.readInt(); - mCallFlags = in.readInt(); - } - - /** - * Returns an UUID of this BluetoothLeCall. - * - *

    - * An UUID is unique identifier of a BluetoothLeCall. - * - * @return UUID of this BluetoothLeCall - * @hide - */ - public @NonNull UUID getUuid() { - return mUuid; - } - - /** - * Returns a URI of the remote party of this BluetoothLeCall. - * - * @return string representation of this BluetoothLeCall - * @hide - */ - public @NonNull String getUri() { - return mUri; - } - - /** - * Returns a friendly name of the call. - * - * @return friendly name representation of this BluetoothLeCall - * @hide - */ - public @NonNull String getFriendlyName() { - return mFriendlyName; - } - - /** - * Returns the call state. - * - * @return the state of this BluetoothLeCall - * @hide - */ - public @State int getState() { - return mState; - } - - /** - * Returns the call flags. - * - * @return call flags - * @hide - */ - public int getCallFlags() { - return mCallFlags; - } - - /** - * Whether the call direction is incoming. - * - * @return true if incoming call, false otherwise - * @hide - */ - public boolean isIncomingCall() { - return (mCallFlags & FLAG_OUTGOING_CALL) == 0; - } -} diff --git a/framework/java/android/bluetooth/BluetoothLeCallControl.java b/framework/java/android/bluetooth/BluetoothLeCallControl.java deleted file mode 100644 index 5283e080425..00000000000 --- a/framework/java/android/bluetooth/BluetoothLeCallControl.java +++ /dev/null @@ -1,911 +0,0 @@ -/* - * Copyright 2019 HIMSA II K/S - www.himsa.com. - * Represented by EHIMA - www.ehima.com - * - * 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.Manifest; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.content.ComponentName; -import android.content.Context; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; -import android.annotation.SuppressLint; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Executor; - -/** - * This class provides the APIs to control the Call Control profile. - * - *

    - * This class provides Bluetooth Telephone Bearer Service functionality, - * allowing applications to expose a GATT Service based interface to control the - * state of the calls by remote devices such as LE audio devices. - * - *

    - * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the - * BluetoothLeCallControl proxy object. - * - * @hide - */ -public final class BluetoothLeCallControl implements BluetoothProfile { - private static final String TAG = "BluetoothLeCallControl"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** @hide */ - @IntDef(prefix = "RESULT_", value = { - RESULT_SUCCESS, - RESULT_ERROR_UNKNOWN_CALL_ID, - RESULT_ERROR_INVALID_URI, - RESULT_ERROR_APPLICATION - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Result { - } - - /** - * Opcode write was successful. - * - * @hide - */ - public static final int RESULT_SUCCESS = 0; - - /** - * Unknown call Id has been used in the operation. - * - * @hide - */ - public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1; - - /** - * The URI provided in {@link Callback#onPlaceCallRequest} is invalid. - * - * @hide - */ - public static final int RESULT_ERROR_INVALID_URI = 2; - - /** - * Application internal error. - * - * @hide - */ - public static final int RESULT_ERROR_APPLICATION = 3; - - /** @hide */ - @IntDef(prefix = "TERMINATION_REASON_", value = { - TERMINATION_REASON_INVALID_URI, - TERMINATION_REASON_FAIL, - TERMINATION_REASON_REMOTE_HANGUP, - TERMINATION_REASON_SERVER_HANGUP, - TERMINATION_REASON_LINE_BUSY, - TERMINATION_REASON_NETWORK_CONGESTION, - TERMINATION_REASON_CLIENT_HANGUP, - TERMINATION_REASON_NO_SERVICE, - TERMINATION_REASON_NO_ANSWER - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TerminationReason { - } - - /** - * Remote Caller ID value used to place a call was formed improperly. - * - * @hide - */ - public static final int TERMINATION_REASON_INVALID_URI = 0x00; - - /** - * Call fail. - * - * @hide - */ - public static final int TERMINATION_REASON_FAIL = 0x01; - - /** - * Remote party ended call. - * - * @hide - */ - public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02; - - /** - * Call ended from the server. - * - * @hide - */ - public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03; - - /** - * Line busy. - * - * @hide - */ - public static final int TERMINATION_REASON_LINE_BUSY = 0x04; - - /** - * Network congestion. - * - * @hide - */ - public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05; - - /** - * Client terminated. - * - * @hide - */ - public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06; - - /** - * No service. - * - * @hide - */ - public static final int TERMINATION_REASON_NO_SERVICE = 0x07; - - /** - * No answer. - * - * @hide - */ - public static final int TERMINATION_REASON_NO_ANSWER = 0x08; - - /* - * Flag indicating support for hold/unhold call feature. - * - * @hide - */ - public static final int CAPABILITY_HOLD_CALL = 0x00000001; - - /** - * Flag indicating support for joining calls feature. - * - * @hide - */ - public static final int CAPABILITY_JOIN_CALLS = 0x00000002; - - private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102; - private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103; - - private static final int REG_TIMEOUT = 10000; - - /** - * The template class is used to call callback functions on events from the TBS - * server. Callback functions are wrapped in this class and registered to the - * Android system during app registration. - * - * @hide - */ - public abstract static class Callback { - - private static final String TAG = "BluetoothLeCallControl.Callback"; - - /** - * Called when a remote client requested to accept the call. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callId The call Id requested to be accepted - * @hide - */ - public abstract void onAcceptCall(int requestId, @NonNull UUID callId); - - /** - * A remote client has requested to terminate the call. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callId The call Id requested to terminate - * @hide - */ - public abstract void onTerminateCall(int requestId, @NonNull UUID callId); - - /** - * A remote client has requested to hold the call. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callId The call Id requested to be put on hold - * @hide - */ - public void onHoldCall(int requestId, @NonNull UUID callId) { - Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); - } - - /** - * A remote client has requested to unhold the call. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callId The call Id requested to unhold - * @hide - */ - public void onUnholdCall(int requestId, @NonNull UUID callId) { - Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); - } - - /** - * A remote client has requested to place a call. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callId The Id to be assigned for the new call - * @param uri The caller URI requested - * @hide - */ - public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri); - - /** - * A remote client has requested to join the calls. - * - *

    - * An application must call {@link BluetoothLeCallControl#requestResult} to complete the - * request. - * - * @param requestId The Id of the request - * @param callIds The call Id list requested to join - * @hide - */ - public void onJoinCalls(int requestId, @NonNull List callIds) { - Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!"); - } - } - - private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub { - - private final Executor mExecutor; - private final Callback mCallback; - - CallbackWrapper(Executor executor, Callback callback) { - mExecutor = executor; - mCallback = callback; - } - - @Override - public void onBearerRegistered(int ccid) { - synchronized (mServerIfLock) { - if (mCallback != null) { - mCcid = ccid; - mServerIfLock.notifyAll(); - } else { - // registration timeout - Log.e(TAG, "onBearerRegistered: mCallback is null"); - } - } - } - - @Override - public void onAcceptCall(int requestId, ParcelUuid uuid) { - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid())); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - public void onTerminateCall(int requestId, ParcelUuid uuid) { - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid())); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - public void onHoldCall(int requestId, ParcelUuid uuid) { - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid())); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - public void onUnholdCall(int requestId, ParcelUuid uuid) { - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid())); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) { - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri)); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - public void onJoinCalls(int requestId, List parcelUuids) { - List uuids = new ArrayList<>(); - for (ParcelUuid parcelUuid : parcelUuids) { - uuids.add(parcelUuid.getUuid()); - } - - final long identityToken = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids)); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - }; - - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothLeCallControl mService; - private BluetoothAdapter mAdapter; - private int mCcid = 0; - private String mToken; - private Callback mCallback = null; - private Object mServerIfLock = new Object(); - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) - Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - doUnbind(); - } else { - doBind(); - } - } - }; - - /** - * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth - * telephone bearer service. - */ - /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) { - mContext = context; - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mServiceListener = listener; - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - private boolean doBind() { - synchronized (mConnection) { - if (mService == null) { - if (VDBG) - Log.d(TAG, "Binding service..."); - try { - return mAdapter.getBluetoothManager(). - bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, - mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind TelephoneBearerService", e); - } - } - } - return false; - } - - private void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - if (VDBG) - Log.d(TAG, "Unbinding service..."); - try { - mAdapter.getBluetoothManager(). - unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, - mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to unbind TelephoneBearerService", e); - } finally { - mService = null; - } - } - } - } - - /* package */ void close() { - if (VDBG) - log("close()"); - unregisterBearer(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "", re); - } - } - mServiceListener = null; - doUnbind(); - } - - private IBluetoothLeCallControl getService() { - return mService; - } - - /** - * Not supported - * - * @throws UnsupportedOperationException - */ - @Override - public int getConnectionState(@Nullable BluetoothDevice device) { - throw new UnsupportedOperationException("not supported"); - } - - /** - * Not supported - * - * @throws UnsupportedOperationException - */ - @Override - public @NonNull List getConnectedDevices() { - throw new UnsupportedOperationException("not supported"); - } - - /** - * Not supported - * - * @throws UnsupportedOperationException - */ - @Override - public @NonNull List getDevicesMatchingConnectionStates( - @NonNull int[] states) { - throw new UnsupportedOperationException("not supported"); - } - - /** - * Register Telephone Bearer exposing the interface that allows remote devices - * to track and control the call states. - * - *

    - * This is an asynchronous call. The callback is used to notify success or - * failure if the function returns true. - * - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * - * - * - * - * - * - * @param uci Bearer Unique Client Identifier - * @param uriSchemes URI Schemes supported list - * @param capabilities bearer capabilities - * @param provider Network provider name - * @param technology Network technology - * @param executor {@link Executor} object on which callback will be - * executed. The Executor object is required. - * @param callback {@link Callback} object to which callback messages will - * be sent. The Callback object is required. - * @return true on success, false otherwise - * @hide - */ - @SuppressLint("ExecutorRegistration") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean registerBearer(@Nullable String uci, - @NonNull List uriSchemes, int capabilities, - @NonNull String provider, int technology, - @NonNull Executor executor, @NonNull Callback callback) { - if (DBG) { - Log.d(TAG, "registerBearer"); - } - if (callback == null) { - throw new IllegalArgumentException("null parameter: " + callback); - } - if (mCcid != 0) { - return false; - } - - mToken = uci; - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - synchronized (mServerIfLock) { - if (mCallback != null) { - Log.e(TAG, "Bearer can be opened only once"); - return false; - } - - mCallback = callback; - try { - CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback); - service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities, - provider, technology); - } catch (RemoteException e) { - Log.e(TAG, "", e); - mCallback = null; - return false; - } - - try { - mServerIfLock.wait(REG_TIMEOUT); - } catch (InterruptedException e) { - Log.e(TAG, "" + e); - mCallback = null; - } - - if (mCcid == 0) { - mCallback = null; - return false; - } - - return true; - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - - return false; - } - - /** - * Unregister Telephone Bearer Service and destroy all the associated data. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void unregisterBearer() { - if (DBG) { - Log.d(TAG, "unregisterBearer"); - } - if (mCcid == 0) { - return; - } - - int ccid = mCcid; - mCcid = 0; - mCallback = null; - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.unregisterBearer(mToken); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - } - - /** - * Get the Content Control ID (CCID) value. - * - * @return ccid Content Control ID value - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public int getContentControlId() { - return mCcid; - } - - /** - * Notify about the newly added call. - * - *

    - * This shall be called as early as possible after the call has been added. - * - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param call Newly added call - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void onCallAdded(@NonNull BluetoothLeCall call) { - if (DBG) { - Log.d(TAG, "onCallAdded: call=" + call); - } - if (mCcid == 0) { - return; - } - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.callAdded(mCcid, call); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - } - - /** - * Notify about the removed call. - * - *

    - * This shall be called as early as possible after the call has been removed. - * - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param callId The Id of a call that has been removed - * @param reason Call termination reason - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) { - if (DBG) { - Log.d(TAG, "callRemoved: callId=" + callId); - } - if (mCcid == 0) { - return; - } - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.callRemoved(mCcid, new ParcelUuid(callId), reason); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - } - - /** - * Notify the call state change - * - *

    - * This shall be called as early as possible after the state of the call has - * changed. - * - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param callId The call Id that state has been changed - * @param state Call state - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) { - if (DBG) { - Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state); - } - if (mCcid == 0) { - return; - } - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.callStateChanged(mCcid, new ParcelUuid(callId), state); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - } - - /** - * Provide the current calls list - * - *

    - * This function must be invoked after registration if application has any - * calls. - * - * @param calls current calls list - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void currentCallsList(@NonNull List calls) { - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.currentCallsList(mCcid, calls); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - } - - /** - * Provide the network current status - * - *

    - * This function must be invoked on change of network state. - * - *

    - * Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * - * - * @param provider Network provider name - * @param technology Network technology - * @hide - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void networkStateChanged(@NonNull String provider, int technology) { - if (DBG) { - Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology); - } - if (mCcid == 0) { - return; - } - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.networkStateChanged(mCcid, provider, technology); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - } - - /** - * Send a response to a call control request to a remote device. - * - *

    - * This function must be invoked in when a request is received by one of these - * callback methods: - * - *

      - *
    • {@link Callback#onAcceptCall} - *
    • {@link Callback#onTerminateCall} - *
    • {@link Callback#onHoldCall} - *
    • {@link Callback#onUnholdCall} - *
    • {@link Callback#onPlaceCall} - *
    • {@link Callback#onJoinCalls} - *
    - * - * @param requestId The ID of the request that was received with the callback - * @param result The result of the request to be sent to the remote devices - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void requestResult(int requestId, @Result int result) { - if (DBG) { - Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result); - } - if (mCcid == 0) { - return; - } - - final IBluetoothLeCallControl service = getService(); - if (service != null) { - try { - service.requestResult(mCcid, requestId, result); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - private static boolean isValidDevice(@Nullable BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } - - private final IBluetoothProfileServiceConnection mConnection = - new IBluetoothProfileServiceConnection.Stub() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) { - Log.d(TAG, "Proxy object connected"); - } - mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service)); - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED)); - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) { - Log.d(TAG, "Proxy object disconnected"); - } - doUnbind(); - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED)); - } - }; - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_TBS_SERVICE_CONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL, - BluetoothLeCallControl.this); - } - break; - } - case MESSAGE_TBS_SERVICE_DISCONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL); - } - break; - } - } - } - }; -} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index d0f74e98572..e047e5d81a9 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -239,20 +239,13 @@ public interface BluetoothProfile { */ int LE_AUDIO_BROADCAST = 26; - /** - * @hide - * Telephone Bearer Service from Call Control Profile - * - */ - int LE_CALL_CONTROL = 27; - /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 27; + int MAX_PROFILE_ID = 26; /** * Default priority for devices that we try to auto-connect to and diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 2c6bf2bd379..8860a816410 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -46,7 +46,6 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; -import android.bluetooth.IBluetoothLeCallControl; import android.content.ActivityNotFoundException; import android.content.AttributionSource; import android.content.BroadcastReceiver; @@ -1329,15 +1328,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + bluetoothProfile); } - Intent intent; - if (bluetoothProfile == BluetoothProfile.HEADSET) { - intent = new Intent(IBluetoothHeadset.class.getName()); - } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) { - intent = new Intent(IBluetoothLeCallControl.class.getName()); - } else { + if (bluetoothProfile != BluetoothProfile.HEADSET) { return false; } + Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); if (!psc.bindService()) { return false; -- GitLab From 90bd65132b36a8d9225b5645e14d1f54083fa108 Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Tue, 17 Nov 2020 09:11:17 +0000 Subject: [PATCH 1406/1408] gtbs: Add Generic Telephone Bearer Service support Tag: #feature Bug: 159786353 Bug: 213412267 Sponsor: jpawlowski@ Test: build Change-Id: Iecd56b91ce59f6b014878691fe4c6fae826b73f9 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothLeCall.java | 285 ++++++ .../bluetooth/BluetoothLeCallControl.java | 899 ++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- .../bluetooth/BluetoothManagerService.java | 9 +- 5 files changed, 1206 insertions(+), 3 deletions(-) create mode 100644 framework/java/android/bluetooth/BluetoothLeCall.java create mode 100644 framework/java/android/bluetooth/BluetoothLeCallControl.java diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 9b37457c990..c6c64b034b6 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3086,6 +3086,9 @@ public final class BluetoothAdapter { BluetoothCsipSetCoordinator csipSetCoordinator = new BluetoothCsipSetCoordinator(context, listener, this); return true; + } else if (profile == BluetoothProfile.LE_CALL_CONTROL) { + BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener); + return true; } else { return false; } @@ -3188,6 +3191,10 @@ public final class BluetoothAdapter { (BluetoothCsipSetCoordinator) proxy; csipSetCoordinator.close(); break; + case BluetoothProfile.LE_CALL_CONTROL: + BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy; + tbs.close(); + break; } } diff --git a/framework/java/android/bluetooth/BluetoothLeCall.java b/framework/java/android/bluetooth/BluetoothLeCall.java new file mode 100644 index 00000000000..fb7789db25c --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeCall.java @@ -0,0 +1,285 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.UUID; + +/** + * Representation of Call + * + * @hide + */ +public final class BluetoothLeCall implements Parcelable { + + /** @hide */ + @IntDef(prefix = "STATE_", value = { + STATE_INCOMING, + STATE_DIALING, + STATE_ALERTING, + STATE_ACTIVE, + STATE_LOCALLY_HELD, + STATE_REMOTELY_HELD, + STATE_LOCALLY_AND_REMOTELY_HELD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + /** + * A remote party is calling (incoming call). + * + * @hide + */ + public static final int STATE_INCOMING = 0x00; + + /** + * The process to call the remote party has started but the remote party is not + * being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_DIALING = 0x01; + + /** + * A remote party is being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_ALERTING = 0x02; + + /** + * The call is in an active conversation. + * + * @hide + */ + public static final int STATE_ACTIVE = 0x03; + + /** + * The call is connected but held locally. “Locally Held” implies that either + * the server or the client can affect the state. + * + * @hide + */ + public static final int STATE_LOCALLY_HELD = 0x04; + + /** + * The call is connected but held remotely. “Remotely Held” means that the state + * is controlled by the remote party of a call. + * + * @hide + */ + public static final int STATE_REMOTELY_HELD = 0x05; + + /** + * The call is connected but held both locally and remotely. + * + * @hide + */ + public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06; + + /** + * Whether the call direction is outgoing. + * + * @hide + */ + public static final int FLAG_OUTGOING_CALL = 0x00000001; + + /** + * Whether the call URI and Friendly Name are withheld by server. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002; + + /** + * Whether the call URI and Friendly Name are withheld by network. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004; + + /** Unique UUID that identifies this call */ + private UUID mUuid; + + /** Remote Caller URI */ + private String mUri; + + /** Caller friendly name */ + private String mFriendlyName; + + /** Call state */ + private @State int mState; + + /** Call flags */ + private int mCallFlags; + + /** @hide */ + public BluetoothLeCall(@NonNull BluetoothLeCall that) { + mUuid = new UUID(that.getUuid().getMostSignificantBits(), + that.getUuid().getLeastSignificantBits()); + mUri = that.mUri; + mFriendlyName = that.mFriendlyName; + mState = that.mState; + mCallFlags = that.mCallFlags; + } + + /** @hide */ + public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName, + @State int state, int callFlags) { + mUuid = uuid; + mUri = uri; + mFriendlyName = friendlyName; + mState = state; + mCallFlags = callFlags; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + BluetoothLeCall that = (BluetoothLeCall) o; + return mUuid.equals(that.mUuid) && mUri.equals(that.mUri) + && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState + && mCallFlags == that.mCallFlags; + } + + @Override + public int hashCode() { + return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags); + } + + /** + * Returns a string representation of this BluetoothLeCall. + * + *

    + * Currently this is the UUID. + * + * @return string representation of this BluetoothLeCall + */ + @Override + public String toString() { + return mUuid.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeString(mUri); + out.writeString(mFriendlyName); + out.writeInt(mState); + out.writeInt(mCallFlags); + } + + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothLeCall createFromParcel(Parcel in) { + return new BluetoothLeCall(in); + } + + public BluetoothLeCall[] newArray(int size) { + return new BluetoothLeCall[size]; + } + }; + + private BluetoothLeCall(Parcel in) { + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); + mUri = in.readString(); + mFriendlyName = in.readString(); + mState = in.readInt(); + mCallFlags = in.readInt(); + } + + /** + * Returns an UUID of this BluetoothLeCall. + * + *

    + * An UUID is unique identifier of a BluetoothLeCall. + * + * @return UUID of this BluetoothLeCall + * @hide + */ + public @NonNull UUID getUuid() { + return mUuid; + } + + /** + * Returns a URI of the remote party of this BluetoothLeCall. + * + * @return string representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getUri() { + return mUri; + } + + /** + * Returns a friendly name of the call. + * + * @return friendly name representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getFriendlyName() { + return mFriendlyName; + } + + /** + * Returns the call state. + * + * @return the state of this BluetoothLeCall + * @hide + */ + public @State int getState() { + return mState; + } + + /** + * Returns the call flags. + * + * @return call flags + * @hide + */ + public int getCallFlags() { + return mCallFlags; + } + + /** + * Whether the call direction is incoming. + * + * @return true if incoming call, false otherwise + * @hide + */ + public boolean isIncomingCall() { + return (mCallFlags & FLAG_OUTGOING_CALL) == 0; + } +} diff --git a/framework/java/android/bluetooth/BluetoothLeCallControl.java b/framework/java/android/bluetooth/BluetoothLeCallControl.java new file mode 100644 index 00000000000..fb080c9ec3e --- /dev/null +++ b/framework/java/android/bluetooth/BluetoothLeCallControl.java @@ -0,0 +1,899 @@ +/* + * Copyright 2019 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; +import android.annotation.SuppressLint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the APIs to control the Call Control profile. + * + *

    + * This class provides Bluetooth Telephone Bearer Service functionality, + * allowing applications to expose a GATT Service based interface to control the + * state of the calls by remote devices such as LE audio devices. + * + *

    + * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothLeCallControl proxy object. + * + * @hide + */ +public final class BluetoothLeCallControl implements BluetoothProfile { + private static final String TAG = "BluetoothLeCallControl"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** @hide */ + @IntDef(prefix = "RESULT_", value = { + RESULT_SUCCESS, + RESULT_ERROR_UNKNOWN_CALL_ID, + RESULT_ERROR_INVALID_URI, + RESULT_ERROR_APPLICATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Result { + } + + /** + * Opcode write was successful. + * + * @hide + */ + public static final int RESULT_SUCCESS = 0; + + /** + * Unknown call Id has been used in the operation. + * + * @hide + */ + public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1; + + /** + * The URI provided in {@link Callback#onPlaceCallRequest} is invalid. + * + * @hide + */ + public static final int RESULT_ERROR_INVALID_URI = 2; + + /** + * Application internal error. + * + * @hide + */ + public static final int RESULT_ERROR_APPLICATION = 3; + + /** @hide */ + @IntDef(prefix = "TERMINATION_REASON_", value = { + TERMINATION_REASON_INVALID_URI, + TERMINATION_REASON_FAIL, + TERMINATION_REASON_REMOTE_HANGUP, + TERMINATION_REASON_SERVER_HANGUP, + TERMINATION_REASON_LINE_BUSY, + TERMINATION_REASON_NETWORK_CONGESTION, + TERMINATION_REASON_CLIENT_HANGUP, + TERMINATION_REASON_NO_SERVICE, + TERMINATION_REASON_NO_ANSWER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TerminationReason { + } + + /** + * Remote Caller ID value used to place a call was formed improperly. + * + * @hide + */ + public static final int TERMINATION_REASON_INVALID_URI = 0x00; + + /** + * Call fail. + * + * @hide + */ + public static final int TERMINATION_REASON_FAIL = 0x01; + + /** + * Remote party ended call. + * + * @hide + */ + public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02; + + /** + * Call ended from the server. + * + * @hide + */ + public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03; + + /** + * Line busy. + * + * @hide + */ + public static final int TERMINATION_REASON_LINE_BUSY = 0x04; + + /** + * Network congestion. + * + * @hide + */ + public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05; + + /** + * Client terminated. + * + * @hide + */ + public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06; + + /** + * No service. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_SERVICE = 0x07; + + /** + * No answer. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_ANSWER = 0x08; + + /* + * Flag indicating support for hold/unhold call feature. + * + * @hide + */ + public static final int CAPABILITY_HOLD_CALL = 0x00000001; + + /** + * Flag indicating support for joining calls feature. + * + * @hide + */ + public static final int CAPABILITY_JOIN_CALLS = 0x00000002; + + private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102; + private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103; + + private static final int REG_TIMEOUT = 10000; + + /** + * The template class is used to call callback functions on events from the TBS + * server. Callback functions are wrapped in this class and registered to the + * Android system during app registration. + * + * @hide + */ + public abstract static class Callback { + + private static final String TAG = "BluetoothLeCallControl.Callback"; + + /** + * Called when a remote client requested to accept the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be accepted + * @hide + */ + public abstract void onAcceptCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to terminate the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to terminate + * @hide + */ + public abstract void onTerminateCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to hold the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be put on hold + * @hide + */ + public void onHoldCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to unhold the call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to unhold + * @hide + */ + public void onUnholdCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to place a call. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The Id to be assigned for the new call + * @param uri The caller URI requested + * @hide + */ + public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri); + + /** + * A remote client has requested to join the calls. + * + *

    + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callIds The call Id list requested to join + * @hide + */ + public void onJoinCalls(int requestId, @NonNull List callIds) { + Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!"); + } + } + + private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub { + + private final Executor mExecutor; + private final Callback mCallback; + + CallbackWrapper(Executor executor, Callback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onBearerRegistered(int ccid) { + if (mCallback != null) { + mCcid = ccid; + } else { + // registration timeout + Log.e(TAG, "onBearerRegistered: mCallback is null"); + } + } + + @Override + public void onAcceptCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onTerminateCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onHoldCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onUnholdCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onJoinCalls(int requestId, List parcelUuids) { + List uuids = new ArrayList<>(); + for (ParcelUuid parcelUuid : parcelUuids) { + uuids.add(parcelUuid.getUuid()); + } + + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + private Context mContext; + private ServiceListener mServiceListener; + private volatile IBluetoothLeCallControl mService; + private BluetoothAdapter mAdapter; + private int mCcid = 0; + private String mToken; + private Callback mCallback = null; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) + Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + doUnbind(); + } else { + doBind(); + } + } + }; + + /** + * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth + * telephone bearer service. + */ + /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) { + mContext = context; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServiceListener = listener; + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + doBind(); + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) + Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager(). + bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind TelephoneBearerService", e); + } + } + } + return false; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + if (VDBG) + Log.d(TAG, "Unbinding service..."); + try { + mAdapter.getBluetoothManager(). + unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to unbind TelephoneBearerService", e); + } finally { + mService = null; + } + } + } + } + + /* package */ void close() { + if (VDBG) + log("close()"); + unregisterBearer(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "", re); + } + } + mServiceListener = null; + doUnbind(); + } + + private IBluetoothLeCallControl getService() { + return mService; + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public int getConnectionState(@Nullable BluetoothDevice device) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List getConnectedDevices() { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List getDevicesMatchingConnectionStates( + @NonNull int[] states) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Register Telephone Bearer exposing the interface that allows remote devices + * to track and control the call states. + * + *

    + * This is an asynchronous call. The callback is used to notify success or + * failure if the function returns true. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * + * + * + * + * + * + * @param uci Bearer Unique Client Identifier + * @param uriSchemes URI Schemes supported list + * @param capabilities bearer capabilities + * @param provider Network provider name + * @param technology Network technology + * @param executor {@link Executor} object on which callback will be + * executed. The Executor object is required. + * @param callback {@link Callback} object to which callback messages will + * be sent. The Callback object is required. + * @return true on success, false otherwise + * @hide + */ + @SuppressLint("ExecutorRegistration") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean registerBearer(@Nullable String uci, + @NonNull List uriSchemes, int capabilities, + @NonNull String provider, int technology, + @NonNull Executor executor, @NonNull Callback callback) { + if (DBG) { + Log.d(TAG, "registerBearer"); + } + if (callback == null) { + throw new IllegalArgumentException("null parameter: " + callback); + } + if (mCcid != 0) { + return false; + } + + mToken = uci; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + if (mCallback != null) { + Log.e(TAG, "Bearer can be opened only once"); + return false; + } + + mCallback = callback; + try { + CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback); + service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities, + provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + mCallback = null; + return false; + } + + if (mCcid == 0) { + mCallback = null; + return false; + } + + return true; + } + + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + + return false; + } + + /** + * Unregister Telephone Bearer Service and destroy all the associated data. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void unregisterBearer() { + if (DBG) { + Log.d(TAG, "unregisterBearer"); + } + if (mCcid == 0) { + return; + } + + int ccid = mCcid; + mCcid = 0; + mCallback = null; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.unregisterBearer(mToken); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Get the Content Control ID (CCID) value. + * + * @return ccid Content Control ID value + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getContentControlId() { + return mCcid; + } + + /** + * Notify about the newly added call. + * + *

    + * This shall be called as early as possible after the call has been added. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param call Newly added call + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallAdded(@NonNull BluetoothLeCall call) { + if (DBG) { + Log.d(TAG, "onCallAdded: call=" + call); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callAdded(mCcid, call); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify about the removed call. + * + *

    + * This shall be called as early as possible after the call has been removed. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The Id of a call that has been removed + * @param reason Call termination reason + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) { + if (DBG) { + Log.d(TAG, "callRemoved: callId=" + callId); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callRemoved(mCcid, new ParcelUuid(callId), reason); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify the call state change + * + *

    + * This shall be called as early as possible after the state of the call has + * changed. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The call Id that state has been changed + * @param state Call state + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) { + if (DBG) { + Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callStateChanged(mCcid, new ParcelUuid(callId), state); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Provide the current calls list + * + *

    + * This function must be invoked after registration if application has any + * calls. + * + * @param calls current calls list + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void currentCallsList(@NonNull List calls) { + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.currentCallsList(mCcid, calls); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + /** + * Provide the network current status + * + *

    + * This function must be invoked on change of network state. + * + *

    + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * + * + * @param provider Network provider name + * @param technology Network technology + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void networkStateChanged(@NonNull String provider, int technology) { + if (DBG) { + Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.networkStateChanged(mCcid, provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Send a response to a call control request to a remote device. + * + *

    + * This function must be invoked in when a request is received by one of these + * callback methods: + * + *

      + *
    • {@link Callback#onAcceptCall} + *
    • {@link Callback#onTerminateCall} + *
    • {@link Callback#onHoldCall} + *
    • {@link Callback#onUnholdCall} + *
    • {@link Callback#onPlaceCall} + *
    • {@link Callback#onJoinCalls} + *
    + * + * @param requestId The ID of the request that was received with the callback + * @param result The result of the request to be sent to the remote devices + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void requestResult(int requestId, @Result int result) { + if (DBG) { + Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.requestResult(mCcid, requestId, result); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private final IBluetoothProfileServiceConnection mConnection = + new IBluetoothProfileServiceConnection.Stub() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) { + Log.d(TAG, "Proxy object connected"); + } + mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service)); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED)); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + if (DBG) { + Log.d(TAG, "Proxy object disconnected"); + } + doUnbind(); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED)); + } + }; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_TBS_SERVICE_CONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL, + BluetoothLeCallControl.this); + } + break; + } + case MESSAGE_TBS_SERVICE_DISCONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL); + } + break; + } + } + } + }; +} diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java index e047e5d81a9..d0f74e98572 100644 --- a/framework/java/android/bluetooth/BluetoothProfile.java +++ b/framework/java/android/bluetooth/BluetoothProfile.java @@ -239,13 +239,20 @@ public interface BluetoothProfile { */ int LE_AUDIO_BROADCAST = 26; + /** + * @hide + * Telephone Bearer Service from Call Control Profile + * + */ + int LE_CALL_CONTROL = 27; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 26; + int MAX_PROFILE_ID = 27; /** * Default priority for devices that we try to auto-connect to and diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 450e9881bef..c8b4f1109b5 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -46,6 +46,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.bluetooth.IBluetoothLeCallControl; import android.content.ActivityNotFoundException; import android.content.AttributionSource; import android.content.BroadcastReceiver; @@ -1328,11 +1329,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + bluetoothProfile); } - if (bluetoothProfile != BluetoothProfile.HEADSET) { + Intent intent; + if (bluetoothProfile == BluetoothProfile.HEADSET) { + intent = new Intent(IBluetoothHeadset.class.getName()); + } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) { + intent = new Intent(IBluetoothLeCallControl.class.getName()); + } else { return false; } - Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); if (!psc.bindService()) { return false; -- GitLab From 56e00da8e76a12e0a6c99fdc92d7621513ec9ee4 Mon Sep 17 00:00:00 2001 From: Rahul Sabnis Date: Wed, 12 Jan 2022 14:06:51 -0800 Subject: [PATCH 1407/1408] Temporarily comment out usages of PropertyInvalidatedCache in Bluetooth code while it is pending migration to module-utils Tag: #feature Bug: 210468958 Test: Manual Change-Id: Iab59d235ca59fe2b5863fb4714eaccbc64fa915c --- .../android/bluetooth/BluetoothAdapter.java | 88 +++++++++++++------ .../android/bluetooth/BluetoothDevice.java | 23 ++--- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index c6c64b034b6..f94ee8579ea 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -28,8 +28,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.app.PropertyInvalidatedCache; +import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache; import android.bluetooth.BluetoothDevice.Transport; import android.bluetooth.BluetoothProfile.ConnectionPolicy; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; @@ -676,14 +675,15 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; + public static final int STATE_DISCONNECTED = + 0; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; /** The profile is in connecting state */ - public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; + public static final int STATE_CONNECTING = 1; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; /** The profile is in connected state */ - public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; + public static final int STATE_CONNECTED = 2; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = - BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; + 3; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; @@ -1044,6 +1044,7 @@ public final class BluetoothAdapter { return false; } + /* private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state"; private final PropertyInvalidatedCache mBluetoothGetStateCache = @@ -1059,17 +1060,22 @@ public final class BluetoothAdapter { } } }; + */ /** @hide */ + /* @RequiresNoPermission public void disableBluetoothGetStateCache() { mBluetoothGetStateCache.disableLocal(); } + */ /** @hide */ + /* public static void invalidateBluetoothGetStateCache() { PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY); } + */ /** * Fetch the current bluetooth state. If the service is down, return @@ -1081,14 +1087,12 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; + //state = mBluetoothGetStateCache.query(null); + state = mService.getState(); } + } catch (RemoteException e) { + Log.e(TAG, "", e); + e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } @@ -2100,6 +2104,7 @@ public final class BluetoothAdapter { } } + /* private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY = "cache_key.bluetooth.is_offloaded_filtering_supported"; private final PropertyInvalidatedCache mBluetoothFilteringCache = @@ -2122,17 +2127,22 @@ public final class BluetoothAdapter { } }; + */ /** @hide */ + /* @RequiresNoPermission public void disableIsOffloadedFilteringSupportedCache() { mBluetoothFilteringCache.disableLocal(); } + */ /** @hide */ + /* public static void invalidateIsOffloadedFilteringSupportedCache() { PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY); } + */ /** * Return true if offloaded filters are supported @@ -2145,7 +2155,18 @@ public final class BluetoothAdapter { if (!getLeAccess()) { return false; } - return mBluetoothFilteringCache.query(null); + //return mBluetoothFilteringCache.query(null); + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.isOffloadedFilteringSupported(); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; } /** @@ -2551,15 +2572,13 @@ public final class BluetoothAdapter { return supportedProfiles; } + /* private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_adapter_connection_state"; private final PropertyInvalidatedCache mBluetoothGetAdapterConnectionStateCache = new PropertyInvalidatedCache ( 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) { - /** - * This method must not be called when mService is null. - */ @Override @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(Void query) { @@ -2570,18 +2589,23 @@ public final class BluetoothAdapter { } } }; + */ /** @hide */ + /* @RequiresNoPermission public void disableGetAdapterConnectionStateCache() { mBluetoothGetAdapterConnectionStateCache.disableLocal(); } + */ /** @hide */ + /* public static void invalidateGetAdapterConnectionStateCache() { PropertyInvalidatedCache.invalidateCache( BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY); } + */ /** * Get the current connection state of the local Bluetooth adapter. @@ -2605,20 +2629,18 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mBluetoothGetAdapterConnectionStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "getConnectionState:", e.getCause()); - } else { - throw e; + return mService.getAdapterConnectionState(); } + //return mBluetoothGetAdapterConnectionStateCache.query(null); + } catch (RemoteException e) { + Log.e(TAG, "failed to getConnectionState, error: ", e); } finally { mServiceLock.readLock().unlock(); } return BluetoothAdapter.STATE_DISCONNECTED; } + /* private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY = "cache_key.bluetooth.get_profile_connection_state"; private final PropertyInvalidatedCache @@ -2646,17 +2668,22 @@ public final class BluetoothAdapter { query); } }; + */ /** @hide */ + /* @RequiresNoPermission public void disableGetProfileConnectionStateCache() { mGetProfileConnectionStateCache.disableLocal(); } + */ /** @hide */ + /* public static void invalidateGetProfileConnectionStateCache() { PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY); } + */ /** * Get the current connection state of a profile. @@ -2678,7 +2705,18 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) { return BluetoothProfile.STATE_DISCONNECTED; } - return mGetProfileConnectionStateCache.query(new Integer(profile)); + try { + mServiceLock.readLock().lock(); + if (mService != null) { + mService.getProfileConnectionState(profile); + } + //return mGetProfileConnectionStateCache.query(new Integer(profile)); + } catch (RemoteException e) { + Log.e(TAG, "failed to getProfileConnectionState, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return BluetoothProfile.STATE_DISCONNECTED; } /** diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 93f02686085..1edf5cc96b0 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -23,8 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.app.PropertyInvalidatedCache; +import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; @@ -1597,6 +1596,7 @@ public final class BluetoothDevice implements Parcelable, Attributable { return false; } + /* private static final String BLUETOOTH_BONDING_CACHE_PROPERTY = "cache_key.bluetooth.get_bond_state"; private final PropertyInvalidatedCache mBluetoothBondCache = @@ -1612,16 +1612,19 @@ public final class BluetoothDevice implements Parcelable, Attributable { } } }; + */ /** @hide */ - public void disableBluetoothGetBondStateCache() { + /* public void disableBluetoothGetBondStateCache() { mBluetoothBondCache.disableLocal(); - } + } */ /** @hide */ + /* public static void invalidateBluetoothGetBondStateCache() { PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY); } + */ /** * Get the bond state of the remote device. @@ -1643,13 +1646,11 @@ public final class BluetoothDevice implements Parcelable, Attributable { return BOND_NONE; } try { - return mBluetoothBondCache.query(this); - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e); - } else { - throw e; - } + //return mBluetoothBondCache.query(this); + return sService.getBondState(this, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "failed to ", e); + e.rethrowFromSystemServer(); } return BOND_NONE; } -- GitLab From a7b1ef55f8de25af82b14ce22b04de0b9d020db7 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Fri, 14 Jan 2022 16:58:35 +0000 Subject: [PATCH 1408/1408] Add OWNERS block to manage slippage during migration. BUG: 196026708 Test: TH Change-Id: Ib9d939f11240857affcf34cbb5b6265640c7ba82 --- framework/java/android/bluetooth/OWNERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS index fbee5777317..8e9d7b74bf0 100644 --- a/framework/java/android/bluetooth/OWNERS +++ b/framework/java/android/bluetooth/OWNERS @@ -1,6 +1,4 @@ # Bug component: 27441 -rahulsabnis@google.com sattiraju@google.com -siyuanh@google.com -zachoverflow@google.com +baligh@google.com -- GitLab